前言
此博客记录对于TinyWebServer项目的学习,并根据自己的理解做出些许更改。
原项目地址:https://github.com/qinguoyi/TinyWebServer
测试效果
先放一波效果图
HTTP请求
HTTP GET请求行内容。
第一行为请求行,分为三个部分:请求方法、请求地址URL和HTTP协议版本,它们之间用空格分割。
后面都是头部字段。
在所有头部字段之后,HTTP请求必须包含一个空行,以标识头部字段的结束。请求行和每个头部字段都必须以结束(回车符和换行符)﹔而空行则必须只包含一-个,不能有其他字符,甚至是空白字符。
这是解析HTTP请求的关键。
而post请求与get请求相比多了消息体,如下图所示,在处理时与get不同,需要将其中的内容提取出来。
解析HTTP请求
使用两个状态机实现,主状态机在内部调用从状态机,从状态机实现按行读取HTTP请求,如下图所示
主状态机调用从状态机读取到完整的一行请求后,按照HTTP请求的结构判断该行属于请求行还是头部字段,然后进行分别解析,直到遇到一个空行,表明得到了一个正确的http请求。
//主状态机状态,检查请求报文中元素
enum CHECK_STATE
{
CHECK_STATE_REQUESTLINE = 0,
CHECK_STATE_HEADER,
CHECK_STATE_CONTENT
};
//HTTP状态码
enum HTTP_CODE
{
NO_REQUEST,
GET_REQUEST,
BAD_REQUEST,
NO_RESOURCE,
FORBIDDEN_REQUEST,
FILE_REQUEST,
INTERNAL_ERROR,
CLOSED_CONNECTION
};
//从状态机的状态,文本解析是否成功
enum LINE_STATUS
{
LINE_OK = 0,
LINE_BAD,
LINE_OPEN
};
//从m_read_buf读取,并处理请求报文
HTTP_CODE process_read();
//向m_write_buf写入响应报文数据
bool process_write(HTTP_CODE ret);
//主状态机解析报文中的请求行数据
HTTP_CODE parse_request_line(char *text);
//主状态机解析报文中的请求头数据
HTTP_CODE parse_headers(char *text);
//主状态机解析报文中的请求内容
HTTP_CODE parse_content(char *text);
//m_start_line是已经解析的字符
//get_line用于将指针向后偏移,指向未处理的字符
char *get_line() { return m_read_buf + m_start_line; };
//从状态机读取一行
LINE_STATUS parse_line();
//存储读取的请求报文数据
char m_read_buf[READ_BUFFER_SIZE];
//缓冲区中m_read_buf中数据的最后一个字节的下一个位置
int m_read_idx;
//m_read_buf读取的位置m_checked_idx
int m_checked_idx;
//m_read_buf中已经解析的字符个数
int m_start_line;
//主状态机的状态
CHECK_STATE m_check_state;
主状态机逻辑
//有限状态机处理请求报文
http_conn::HTTP_CODE http_conn::process_read()
{
LINE_STATUS line_status = LINE_OK;
HTTP_CODE ret = NO_REQUEST;
char *text = 0;
//parse_line()更新m_checked_idx,从而更新text
while ((m_check_state == CHECK_STATE_CONTENT && line_status == LINE_OK) || ((line_status = parse_line()) == LINE_OK))
{
//text指向m_read_buf的未处理数据的开始
text = get_line();
//m_checked_idx表示当前已处理的位置,这里更新m_start_line为下一个的开始
m_start_line = m_checked_idx;
LOG_INFO("%s", text);
switch (m_check_state)
{
//请求行
case CHECK_STATE_REQUESTLINE:
{
ret = parse_request_line(text);
if (ret == BAD_REQUEST)
return BAD_REQUEST;
break;
}
//头部字段
case CHECK_STATE_HEADER:
{
ret = parse_headers(text);
if (ret == BAD_REQUEST)
return BAD_REQUEST;
else if (ret == GET_REQUEST)
{
//得到一个完整的请求就进入处理请求的程序
return do_request();
}
break;
}
case CHECK_STATE_CONTENT:
{
ret = parse_content(text);
if (ret == GET_REQUEST)
return do_request();
//没读完,继续读
line_status = LINE_OPEN;
break;
}
default:
return INTERNAL_ERR