仿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;
    }
   
};
相关推荐
志栋智能4 小时前
超自动化运维如何支持合规审计?
运维·自动化
小诗懂技术5 小时前
【网络通信UDP】基础 进程间不同主机的通信
运维·服务器·网络
weixin_440730505 小时前
selenium实战记录-从登陆-首页-进入应用流程用例三种参数化+日志+报告
服务器·selenium·测试工具
名字还没想好☜5 小时前
Docker 容器安全加固实战:非 root、只读根文件系统、drop capabilities 与最小攻击面
运维·安全·docker·容器·kubernetes
鹤落晴春5 小时前
有状态应用 vs 无状态应用
运维·云原生·k8s
Hstar_chen5 小时前
开源一款 HarmonyOS 服务器监控客户端:星辰云巡 1.0.3
服务器·华为·harmonyos
starzy19907 小时前
虚拟化解决方案全景:软件虚拟化、硬件虚拟化与 Docker 的位置
运维·docker·容器
Neighbor_OldY8 小时前
云上安全配置审计与误配置修复实战:从安全组、OSS、RAM到数据库的全栈排查复盘
大数据·运维·安全·云计算
开心大爆炸9 小时前
xrdp 连接 登录对话框输入密码后闪退
linux·运维·服务器
苏生Susheng10 小时前
【软件实施】Linux系统Shell脚本教程
linux·运维·服务器·chrome·spring boot·学习·实施