仿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;
    }
   
};
相关推荐
千里马-horse2 小时前
ubuntu 电脑安装protoc-gen-grpc-kotlin
linux·运维·ubuntu
从零点2 小时前
ubuntu网络没有WiFi怎么办?网络配置解决步骤
linux·服务器·网络
计算机与认知2 小时前
Linux Device Link机制
java·linux·服务器
last demo2 小时前
docker容器监控
运维·docker·容器
BY组态2 小时前
从零开始:Ricon组态系统快速入门指南
运维·物联网·web组态·组态
Barkamin2 小时前
网络编程套接字
运维·服务器·网络
原来是猿2 小时前
Linux-【ELF文件】
linux·运维·服务器
似水এ᭄往昔2 小时前
【Linux】--基础开发工具->gcc/g++
linux·运维·服务器
顶点多余2 小时前
Linux中库的制作和原理详解
linux·运维·服务器