仿muduo库实现高并发服务器---HttpContext上下文类实现

目录

一、成员变量

二、成员函数

三、全部代码


一、成员变量

响应状态码

当前接收及解析的阶段状态

已经解析得到的请求消息

cpp 复制代码
    int _res_statu;
    HttpRecvstatu _recv_statu;
    HttpResquest _request;

二、成员函数

构造函数

解析阶段

获取行

1、获取一行数据,带有末尾的换行

2、需要考虑的一些要素,缓冲区中的数据不足一行,获取的一行数据超大

3、缓冲区中的数据不足一行,则需要判断缓冲区的可读数据长度,如果很长了都不足一行,这是有问题的

4、缓冲区中的数据不足一行但是也不多就等新数据到来

5、首行处理完毕,进入头部获取阶段

cpp 复制代码
  bool RecvHttpLine(Buffer *buf)
    {
        if (_recv_statu != RECV_HTTP_LINE)
        {
            return false;
        }
        std::string line = buf->GetLineAndPop();
        if (line.size() == 0)
        {
            if (buf->ReadAblesize() > MAX_LINE)
            {
                _res_statu = 414;
                _recv_statu = RECV_HTTP_ERROR;
                return false;
            }
            return true;
        }
        if (line.size() > MAX_LINE)
        {
            _res_statu = 414;
            _recv_statu = RECV_HTTP_ERROR;
            return false;
        }
        bool ret = ParseHttpLine(line);
        if (ret == false)
        {
            return false;
        }
        _recv_statu = RECV_HTTP_HEAD;
        return true;
    }

解析行

1、使用正则匹配库进行匹配

2、请求方法的获取

3、资源路径的获取,需要进行URL解码操作,但是不需要+转空格

4、协议版本的获取

5、查询字符串的格式,先以&符号进行分割,得到key和val,得到之后也需要进行URL解

cpp 复制代码
   bool ParseHttpLine(const std::string &line)
    {
        std::smatch matches;
        std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
        bool ret = std::regex_match(line, matches, e);
        if(ret == false)
        {
            _res_statu = 414;
            _recv_statu = RECV_HTTP_ERROR;
            return false;
        }
        _request._method = matches[1];
        std::transform(_request._method.begin(),_request._method.end(),_request._method.begin(),::toupper);
        _request._path = Util::urlDeCode(matches[2],false);
        _request._version = matches[4];
        std::vector<std::string> active;
        std::string query_string = matches[3];
        Util::split(query_string,"&",&active);
        for(auto& str:active)
        {
            auto pos = str.find("=");
            if(pos == std::string::npos)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _res_statu = 400;
            }
            std::string key = Util::urlDeCode(str.substr(0,pos),true);
            std::string val = Util::urlDeCode(str.substr(pos+1),true);
            _request.SetParam(key,val);
        }
        return true;
    }

获取头部

1、一行一行取出数据,直到遇到空行为止

2、需要考虑一些要素:缓冲区的数据不足一行,获取一行数据超大

3、缓冲区中的数据不足一行,则需要判断缓冲区的可读数据长度,如果很长了都不足一行,这是有问题的

4、缓冲区中数据不足一行,但是也不多,就等等新数据的到来

5、头部处理完毕,进入正文获取阶段

cpp 复制代码
bool RecvHttpHead(Buffer* buf)
    {
        if(_recv_statu!=RECV_HTTP_HEAD) return false;
        while(1)
        {
            std::string line = buf->GetLineAndPop();
            if (line.size() == 0)
            {
                if (buf->ReadAblesize() > MAX_LINE)
                {
                    _res_statu = 414;
                    _recv_statu = RECV_HTTP_ERROR;
                    return false;
                }
                return true;
            }
            if (line.size() > MAX_LINE)
            {
                _res_statu = 414;
                _recv_statu = RECV_HTTP_ERROR;
                return false;
            }
            if(line == "\n"||line =="\r\n")
            {
                break;
            }
            bool ret = ParseHttpHead(line);
            if(ret == false)
            {
                return false;
            }
        } 
        _recv_statu = RECV_HTTP_BODY;
        return true;
    }

解析头部

cpp 复制代码
 bool ParseHttpHead(std::string &line)
    {
        auto pos = line.find(": ");
        if(pos == std::string::npos)
        {
            _recv_statu = RECV_HTTP_HEAD;
            _res_statu = 400;
            return false;
        }
        std::string key = line.substr(0,pos);
        std::string val = line.substr(pos+2);
        _request.SetHeader(key,val);
        return true;
    }

接收正文

1、获取正文长度

2、当前已经接收了多少正文,其实就是往 _request._body中放了多少数据(实际还需要接收的正文长度)

3、接收正文放到body中,但是也要考虑当前缓冲区中的数据,是否是全部正文

4、缓冲区中数据,包含了当前请求的所有正文,则取出所需的数据

5、缓冲区中数据,无法满足当前正文的需要,数据不足,取出数据,然后等待新数据的到来

重置

返回状态码

返回解析阶段

返回Requst

不同的状态,做不同的事情,但是这里不要break,因为处理完请求行后,应该立即处理头部,而不是退出等待

三、全部代码

cpp 复制代码
typedef enum
{
    RECV_HTTP_ERROR,
    RECV_HTTP_LINE,
    RECV_HTTP_HEAD,
    RECV_HTTP_BODY,
    RECV_HTTP_OVER
} HttpRecvstatu;
#define MAX_LINE 8192
class HttpContext
{
private:
    int _res_statu;
    HttpRecvstatu _recv_statu;
    HttpResquest _request;
private:
    bool RecvHttpLine(Buffer *buf)
    {
        if (_recv_statu != RECV_HTTP_LINE)
        {
            return false;
        }
        std::string line = buf->GetLineAndPop();
        if (line.size() == 0)
        {
            if (buf->ReadAblesize() > MAX_LINE)
            {
                _res_statu = 414;
                _recv_statu = RECV_HTTP_ERROR;
                return false;
            }
            return true;
        }
        if (line.size() > MAX_LINE)
        {
            _res_statu = 414;
            _recv_statu = RECV_HTTP_ERROR;
            return false;
        }
        bool ret = ParseHttpLine(line);
        if (ret == false)
        {
            return false;
        }
        _recv_statu = RECV_HTTP_HEAD;
        return true;
    }
    bool ParseHttpLine(const std::string &line)
    {
        std::smatch matches;
        std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
        bool ret = std::regex_match(line, matches, e);
        if(ret == false)
        {
            _res_statu = 414;
            _recv_statu = RECV_HTTP_ERROR;
            return false;
        }
        _request._method = matches[1];
        std::transform(_request._method.begin(),_request._method.end(),_request._method.begin(),::toupper);
        _request._path = Util::urlDeCode(matches[2],false);
        _request._version = matches[4];
        std::vector<std::string> active;
        std::string query_string = matches[3];
        Util::split(query_string,"&",&active);
        for(auto& str:active)
        {
            auto pos = str.find("=");
            if(pos == std::string::npos)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _res_statu = 400;
            }
            std::string key = Util::urlDeCode(str.substr(0,pos),true);
            std::string val = Util::urlDeCode(str.substr(pos+1),true);
            _request.SetParam(key,val);
        }
        return true;
    }
    bool RecvHttpHead(Buffer* buf)
    {
        if(_recv_statu!=RECV_HTTP_HEAD) return false;
        while(1)
        {
            std::string line = buf->GetLineAndPop();
            if (line.size() == 0)
            {
                if (buf->ReadAblesize() > MAX_LINE)
                {
                    _res_statu = 414;
                    _recv_statu = RECV_HTTP_ERROR;
                    return false;
                }
                return true;
            }
            if (line.size() > MAX_LINE)
            {
                _res_statu = 414;
                _recv_statu = RECV_HTTP_ERROR;
                return false;
            }
            if(line == "\n"||line =="\r\n")
            {
                break;
            }
            bool ret = ParseHttpHead(line);
            if(ret == false)
            {
                return false;
            }
        } 
        _recv_statu = RECV_HTTP_BODY;
        return true;
    }
    bool ParseHttpHead(std::string &line)
    {
        auto pos = line.find(": ");
        if(pos == std::string::npos)
        {
            _recv_statu = RECV_HTTP_HEAD;
            _res_statu = 400;
            return false;
        }
        std::string key = line.substr(0,pos);
        std::string val = line.substr(pos+2);
        _request.SetHeader(key,val);
        return true;
    }
    bool RecvHttpBody(Buffer* buf)
    {
        if(_recv_statu!=RECV_HTTP_BODY)
        {
            return false;
        }
        size_t content_length = _request.ContentLength();
        if(content_length == 0)
        {
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }
        size_t len = content_length-_request._body.size();
        if(buf->ReadAblesize()>=len)
        {
            _request._body.append(buf->ReadPosition(),len);
            buf->MoveReadoffset(len);
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }
         _request._body.append(buf->ReadPosition(),buf->ReadAblesize());
         buf->MoveReadoffset( buf->ReadAblesize());
         return true;
    }

public:
    HttpContext():_res_statu(200),_recv_statu(RECV_HTTP_LINE)
    {

    }
    void ReSet()
    {
        _res_statu = 200;
        _recv_statu = RECV_HTTP_LINE;
        _request.ReSet();
    }
    int RespStatu()
    {
        return _res_statu;
    }
    HttpRecvstatu Recvstatu()
    {
        return _recv_statu;
    }
    HttpResquest &Requst()
    {
        return _request;
    }
    void RecvHttpRequest(Buffer *buf)
    {
        switch(_recv_statu)
        {
            case RECV_HTTP_LINE: RecvHttpLine(buf);
            case RECV_HTTP_HEAD: RecvHttpHead(buf);
            case RECV_HTTP_BODY: RecvHttpBody(buf);

        }
        return;
    }
   
};
相关推荐
TonyLee01713 分钟前
Claude Code + DeepSeek:服务器安装与使用笔记
linux·服务器·deepseek·claude code
St_rive14 分钟前
Pytest skip&&skipif跳过⽤例
运维·服务器·pytest
buhuizhiyuci25 分钟前
【Linux 篇】数字世界的水流蓄水池 —— IO 缓冲区机制深度实战解析
linux·运维·服务器·c++
贝锐29 分钟前
多校区互通、临时联网、远程运维,校园网络升级用蒲公英更省心!
运维·组网
骇客野人33 分钟前
Linux 服务器 Python 版 MCP 服务部署整体方案(适配 Claude/Cursor/自研Agent)
linux·服务器·python
啦啦啦啦啦zzzz1 小时前
moduo网络库 Acceptor TcpConnection
服务器·网络·c++·reactor·网络库
游戏开发爱好者81 小时前
App Store 上传 IPA 自动化,.p8 密钥认证与 CI/CD 接入实战
android·运维·ci/cd·小程序·uni-app·自动化·iphone
天疆说1 小时前
04 稳定性与性能调优实录:OOM 崩溃、并发槽位、前缀缓存与 DSpark 调参
linux·运维·缓存
微硬创新1 小时前
大点数模拟量采集落地:耐达讯自动化32路0-20mA适配PROFINET总线实践解析
运维·人工智能·网络协议·自动化·信息与通信
星卯教育tony1 小时前
NOI Linux 2.0 服务器多用户网页桌面部署方案(腾讯云轻量Ubuntu20.04专属版 CSP复赛比赛环境搭建免安装虚拟机 )
linux·服务器·腾讯云