我的编程空间,编程开发者的网络收藏夹
学习永远不晚

解析HTTP请求报文(GET、POST)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

解析HTTP请求报文(GET、POST)

目的:

一个WEB服务器需要解析客户端(浏览器)发来的请求,两种常见的请求方式是GETPOST

GET的请求格式:

  • GET请求没有请求体只有请求头
  • GET请求的请求参数放在URL后加上一个"?"的后面,参数以key=value的形式传递,参数与参数之间使用"&"进行连接。
GET /signin?next=%2F HTTP/2\r\nHost: www.zhihu.com\r\nUser-Agent: Mozilla/5.0\r\nAccept: **\r\nAccept-Language: zh-CN\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 29\r\nConnection: keep-alive\r\n\r\nusername=root&password=123456
  1. 请求头中每一行的后面都要加"\r\n"结尾;
  2. 第一行是状态行,分别是请求方法(POST)、请求路径(/login)、协议版本(HTTP/1.1);
  3. 请求头内的剩余内容均以XXX: XXXX的格式表示;
  4. 请求头最后需要一个"\r\n"的空行作为结束的标志;
  5. 放在请求体内的请求参数以key=value的形式传递,参数与参数之间使用"&"进行连接。

功能介绍: 

使用状态机正则表达式完成了对HTTP请求报文的解析,支持解析GET报文和POST报文(仅限Content-Type: application/x-www-form-urlencoded)。

由于计划完成的web服务器需要实现展示主页(GET)用户登录(POST)用户注册(POST)获取图片(GET)获取视频(GET)五个功能,所以web服务器的请求解析模块满足:

若为GET请求,可根据状态行信息,完成对请求内容地址的转换,以及请求头内其他内容的提取。

若为POST请求,可根据请求参数,完成登录和注册这两个功能(登录:根据后台数据库表中的信息判断用户名与密码是否正确;注册:向后台数据库表中插入符合条件的新用户名和密码)。

状态机流程:

enum PARSE_STATE    {        REQUEST_LINE,        HEADERS,        BODY,        FINISH    };

如果为GET请求: REQUEST_LINE——>HEADERS——>FINISH;

如果为POST请求:REQUEST_LINE——>HEADERS——>BODY——>FINISH。
 

用到的正则表达式:

 1、^([^ ]*) ([^ ]*) HTTP/([^ ]*)$        匹配状态行

 2、^([^:]*): ?(.*)$        匹配请求头内的XXX: XXXX字段

 

 3、(?!&)(.*?)=(.*?)(?=&|$)        匹配POST的请求参数

 

HttpRequest类结构        httprequest.h

#ifndef HTTPREQUEST_H#define HTTPREQUEST_H#include #include #include #include #include #include #include #include "buffer.h"#include "log.h"#include "sqlconnpool.h"using std::string;class HttpRequest{public:    enum PARSE_STATE//解析流程的状态    {        REQUEST_LINE,        HEADERS,        BODY,        FINISH    };    HttpRequest();    ~HttpRequest()=default;    bool parse(Buffer& buffer);//解析全过程    const string& getMethod() const;    const string& getPath() const;    const string& getVersion() const;    bool isKeepAlive() const;private:    void parseRequestLine(const string& line);//解析状态行    void parseHeader(const string& line);//解析请求头    void parseBody(const string& line);//解析请求体    void parsePath();//解析请求路径    void parsePost();//解析POST请求    void parseUrlencoded();//解析POST请求的请求参数    bool userVertify(const string& username,const string& password,int tag);//身份验证    PARSE_STATE state;    string method;    string path;    string version;    string body;    std::unordered_map header;//存储请求头字段    std::unordered_map post; //存储POST请求参数    static const std::unordered_set DEFAULT_HTML;    static const std::unordered_map DEFAULT_HTML_TAG;};#endif // !HTTPREQUEST_H

 HttpRequest类实现       httprequest.cpp

#include "httprequest.h"const std::unordered_set HttpRequest::DEFAULT_HTML={"/home","/register","/login","/video","/picture"};const std::unordered_map HttpRequest::DEFAULT_HTML_TAG={{"/register.html", 0},{"/login.html", 1}};HttpRequest::HttpRequest(){    Log::getInstance()->init();    init();}void HttpRequest::init(){    method="";    path="";    version="";    body="";    state = REQUEST_LINE;    header.clear();    post.clear();}bool HttpRequest::parse(Buffer& buffer){    if(buffer.readableBytes()<=0)        return false;    while(buffer.readableBytes()&&state!=FINISH)    {        const char CRLF[3]="\r\n";        const char* lineEnd=std::search(buffer.peek(),static_cast(buffer.beginWrite()),CRLF,CRLF+2);        string line(buffer.peek(),lineEnd);        switch (state)        {        case REQUEST_LINE:            parseRequestLine(line);            parsePath();            break;        case HEADERS:            parseHeader(line);            break;        case BODY:            parseBody(line);            break;        default:            break;        }        if(lineEnd==buffer.beginWrite())//解析完请求体(不由"\r\n"结尾)            break;        buffer.retrieveUntil(lineEnd+2);//解析完一行Headers(由"\r\n"结尾)    }    return true;}void HttpRequest::parsePath(){    if(path=="/")         path="/home.html";    else        if(DEFAULT_HTML.count(path))            path+=".html";}void HttpRequest::parseRequestLine(const string& line){    std::regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");    std::smatch match;    if(!std::regex_match(line,match,patten))    {        LOG_ERROR("%s","Parse RequestLine Error");    }    method=match[1];    path=match[2];    version=match[3];    state=HEADERS;}void HttpRequest::parseHeader(const string& line){    std::regex patten("^([^:]*): ?(.*)$");    std::smatch match;    if(std::regex_match(line,match,patten))    {        header[match[1]]=match[2];    }            else    {        state=BODY;    }}void HttpRequest::parseBody(const string& line){    body=line;    parsePost();    state=FINISH;}void HttpRequest::parsePost(){    if(method=="POST"&&header["Content-Type"]=="application/x-www-form-urlencoded")    {        parseUrlencoded();        if(DEFAULT_HTML_TAG.count(path))        {            int tag=DEFAULT_HTML_TAG.find(path)->second;            if(userVertify(post["username"],post["password"],tag))            {                path="/home.html";            }            else            {                path="/error.html";            }        }    }}void HttpRequest::parseUrlencoded(){std::regex patten("(?!&)(.*?)=(.*?)(?=&|$)");    std::smatch match;    string::const_iterator begin=body.begin();    string::const_iterator end=body.end();    while(std::regex_search(begin,end,match,patten))    {        post[match[1]]=match[2];        begin=match[0].second;    }}bool HttpRequest::userVertify(const string& username,const string& password,int tag){    SqlConnPool* pool = SqlConnPool::getInstance();    std::shared_ptr conn=pool->getConn();    string order1="SELECT username,password FROM user WHERE username='"+username+"' LIMIT 1";    string order2="INSERT INTO user(username, password) VALUES('"+username+"','"+password+"')";    MYSQL_RES* res=conn->query(order1);    string user;    string pwd;    MYSQL_ROW row=nullptr;    while((row=mysql_fetch_row(res))!=nullptr)     {        user=row[0];        pwd=row[1];    }    if(tag)//登录    {        if(pwd!=password)//密码错误        {            LOG_ERROR("%s","Password Error");            return false;        }        LOG_INFO("%s Login Success",username);    }    else//注册    {        if(!user.empty())//用户名已被使用        {            LOG_ERROR("%s","Username Used");            return false;        }        if(!conn->update(order2))//数据库插入失败        {            LOG_ERROR("%s","Insert Error");            return false;        }        LOG_INFO("%s Register Success",username);    }    mysql_free_result(res);    return true;}const string& HttpRequest::getMethod() const{    return method;}const string& HttpRequest::getPath() const{    return path;}const string& HttpRequest::getVersion() const{    return version;}bool HttpRequest::isKeepAlive() const{    if(header.count("Connection"))    {        return header.find("Connection")->second=="keep-alive";    }    return false;}

 测试程序        testHttpRequest.cpp

分别解析GET请求和POST请求,根据解析内容进行判断。

#include "httprequest.h"#include using namespace std;void testPost(){    HttpRequest request;    Buffer input;    input.append("POST /login HTTP/1.1\r\n"            "Host: 127.0.0.1:8888\r\n"            "User-Agent: Mozilla/5.0\r\n"             "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,**;q=0.8\r\n"            "Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n"            "Accept-Encoding: gzip, deflate, br\r\n"            "Connection: keep-alive\r\n"            "Upgrade-Insecure-Requests: 1\r\n"            "Cache-Control: max-age=0\r\n"            "TE: trailers\r\n"            "\r\n");    request.parse(input);    cout<<"method:"<

运行结果:

 由日志信息可以判断,对GET和POST的请求解析正确。

附:

 Makefile

CXX = g++CFLAGS = -std=c++14 -O2 -Wall -g TARGET = testHttpRequestOBJS =  buffer.cpp log.cpp blockqueue.h\sqlconn.cpp sqlconnpool.cpp httprequest.cpp\        testHttpRequest.cppall: $(OBJS)$(CXX) $(CFLAGS) $(OBJS) -o $(TARGET)  -pthread -L/usr/lib64/mysql -lmysqlclientclean:rm -rf $(OBJS) $(TARGET)

数据库连接池(C++11实现)_{(sunburst)}的博客-CSDN博客

同步+异步日志系统(C++实现)_{(sunburst)}的博客-CSDN博客_c++ 异步日志

缓冲区Buffer类的设计(参考Muduo实现)_{(sunburst)}的博客-CSDN博客

基于C++11实现的阻塞队列(BlockQueue)_{(sunburst)}的博客-CSDN博客_c++11 阻塞队列

来源地址:https://blog.csdn.net/weixin_50437588/article/details/128570178

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

解析HTTP请求报文(GET、POST)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Qt怎么实现HTTP的Get/Post请求

本文小编为大家详细介绍“Qt怎么实现HTTP的Get/Post请求”,内容详细,步骤清晰,细节处理妥当,希望这篇“Qt怎么实现HTTP的Get/Post请求”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。借助Qt的
2023-06-30

HTTP协议中请求方法的Get和Post

HTTP协议中请求方法Get和Post的区别是什么?Get:是以实体的方式得到由请求URI所指定资源的信息,如果请求URI只是一个数据产生过程,那么最终要在响应实体中返回的是处理过程的结果所指向的资源,而不是处理过程的描述。 Post:用来
2023-06-03

nodejs处理http请求实例详解之get和post

最近一段时间在学习前端向服务器发送数据和请求数据,下面这篇文章主要给大家介绍了关于nodejs处理http请求实例详解之get和post的相关资料,需要的朋友可以参考下
2023-01-28

HTTP的请求方式GET和POST有什么区别

这篇文章主要介绍“HTTP的请求方式GET和POST有什么区别”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“HTTP的请求方式GET和POST有什么区别”文章能帮助大家解决问题。HTTP的两种常用请
2023-06-27

解析HTTP协议六种请求方法,get,head,put,delete,post有什么区别

标准Http协议支持六种请求方法,即: 1、GET 2、POST 3、PUT 4、Delete 5、HEAD 6、Options 但其实我们大部分情况下只用到了GET和POST。如果想设计一个符合RESTful规范的web应用
2023-06-04

如何解决vue中$http的get和post请求跨域问题

这篇文章给大家分享的是有关如何解决vue中$http的get和post请求跨域问题的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。vue $http的get和post请求跨域问题首先在config/index.js
2023-06-15

Android发送GET与POST请求的DEMO详解

4.0后网络访问必须单独起一个子线程访问,否则无法运行,这里有一个发送请求的工具类GetPostUtil 代码如下:public class GetPostUtil{ /** * 向指定URL发送GET方法的请求 * * @para
2022-06-06

编程热搜

目录