一、HTTP报文格式
1. 请求报文
请求报文 |
描述 |
请求行 |
用来说明请求类型、要访问的资源以及所使用的HTTP协议版本。 |
请求头 |
格式为:属性名:属性值,服务端据此获取客户端的信息。 |
空行 |
即使请求体为空,请求头后面的空行也必须要有。 |
请求体 |
将页面表单中的组件值通过param1=value1¶m2=value2键值对的形式编码成一个格式化串。 |
请求头字段 |
含义 |
Accept |
浏览器可以处理的MIME类型。 |
Accept-Charset |
浏览器能识别的字符集。 |
Accept-Encoding |
浏览器可以处理的编码方式。 |
Accept-Language |
浏览器接收的语言。 |
Connection |
连接管理,可以是keep-alive或close。 |
Content-length |
请求体的长度,单位为字节。 |
Host |
服务器域名或IP地址。 |
Referer |
用户从该URL代表的页面访问当前请求的页面。 |
User-Agent |
用户的浏览器相关信息。 |
2. 响应报文
响应报文 |
描述 |
状态行 |
用来说明HTTP协议版本号、状态码、状态消息。 |
响应头 |
用来说明客户端要使用的一些附加信息。 |
空行 |
响应头后面的空行必须要有。 |
响应体 |
服务器返回给客户端的文本信息。 |
状态码 |
描述 |
200 |
客户端请求被正常处理。 |
301 |
永久重定向,该资源已被永久移动到新位置,将来对该资源的访问都要使用本响应返回的若干个URL之一。 |
302 |
临时重定向,请求的资源现在临时从不同的URL中获得。 |
400 |
请求报文存在语法错误。 |
403 |
请求被服务器拒绝。 |
404 |
请求不存在,服务器上找不到请求的资源。 |
500 |
服务器在执行请求时出现错误。 |
二、解析HTTP请求报文
1. 有限状态机
- 为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。
- 有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。
- 有限状态自动机可以表示为一个有向图。
2. 状态转换图
主状态机状态 |
描述 |
CHECK_STATE_REQUESTLINE |
解析请求行 |
CHECK_STATE_HEADER |
解析请求头 |
CHECK_STATE_CONTENT |
解析请求体 |
从状态机状态 |
描述 |
LINE_OK |
完整读取一行 |
LINE_BAD |
报文语法有误 |
LINE_OPEN |
读取的行不完整 |
处理结果 |
描述 |
NO_REQUEST |
请求不完整,需要继续读取请求报文数据 |
GET_REQUEST |
获得了完整的HTTP请求 |
BAD_REQUEST |
HTTP请求报文有语法错误 |
INTERNAL_ERROR |
服务器内部错误 |
3. 代码实现
- 从状态机负责读取报文的一行,主状态机负责对该行数据进行解析。
- 主状态机内部调用从状态机,从状态机驱动主状态机。
http_conn::LINE_STATUS http_conn::parse_line()
{
char temp;
for (; m_checked_idx < m_read_idx; ++m_checked_idx)
{
temp = m_read_buf[m_checked_idx];
if (temp == '\r')
{
if ((m_checked_idx + 1) == m_read_idx)
{
return LINE_OPEN;
}
else if (m_read_buf[m_checked_idx + 1] == '\n')
{
m_read_buf[m_checked_idx++] = '\0';
m_read_buf[m_checked_idx++] = '\0';
return LINE_OK;
}
return LINE_BAD;
}
else if (temp == '\n')
{
if (m_checked_idx > 1 && m_read_buf[m_checked_idx - 1] == '\r')
{
m_read_buf[m_checked_idx - 1] = '\0';
m_read_buf[m_checked_idx++] = '\0';
return LINE_OK;
}
return LINE_BAD;
}
}
return LINE_OPEN;
}
http_conn::HTTP_CODE http_conn::parse_request_line(char *text)
{
m_url = strpbrk(text, " \t");
if (!m_url)
{
return BAD_REQUEST;
}
*m_url++ = '\0';
char *method = text;
if (strcasecmp(method, "GET") == 0)
{
m_method = GET;
}
else if (strcasecmp(method, "POST") == 0)
{
m_method = POST;
cgi = 1;
}
else
{
return BAD_REQUEST;
}
m_url += strspn(m_url, " \t");
m_version = strpbrk(m_url, " \t");
if (!m_version)
{
return BAD_REQUEST;
}