目录
一、成员变量
响应状态码
当前接收及解析的阶段状态
已经解析得到的请求消息

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;
}
};