【C++实现HTTP服务器项目记录】HTTP报文处理

C++实现HTTP服务器之HTTP报文处理
摘要由CSDN通过智能技术生成

一、HTTP报文格式

1. 请求报文

请求报文 描述
请求行 用来说明请求类型、要访问的资源以及所使用的HTTP协议版本。
请求头 格式为:属性名:属性值,服务端据此获取客户端的信息。
空行 即使请求体为空,请求头后面的空行也必须要有。
请求体 将页面表单中的组件值通过param1=value1&param2=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;
    // m_read_idx:读缓冲区中数据的最后一个字节的下一个位置
    // m_checked_idx:指向从状态机当前正在分析的字节,最终指向读缓冲区下一行的开头
    for (; m_checked_idx < m_read_idx; ++m_checked_idx)
    {
   
        temp = m_read_buf[m_checked_idx];
        // 当前是'\r',则有可能会读取到完整行
        if (temp == '\r')
        {
   
            // 下一个字符的位置是读缓冲区末尾
            if ((m_checked_idx + 1) == m_read_idx)
            {
   
                // 读取的行不完整,需要继续接收
                return LINE_OPEN;
            }
            // 下一个字符是'\n'
            else if (m_read_buf[m_checked_idx + 1] == '\n')
            {
   
                // 将'\r\n'改为'\0\0'
                m_read_buf[m_checked_idx++] = '\0';
                m_read_buf[m_checked_idx++] = '\0';
                // 完整读取一行
                return LINE_OK;
            }
            // 否则报文语法有误
            return LINE_BAD;
        }
        // 当前字符是'\n',则有可能读取到完整行
        // 上次读取到'\r'就到读缓冲区末尾了,没有接收完整,再次接收时会出现这种情况
        else if (temp == '\n')
        {
   
            // 前一个字符是'\r'
            if (m_checked_idx > 1 && m_read_buf[m_checked_idx - 1] == '\r')
            {
   
                // 将'\r\n'改为'\0\0'
                m_read_buf[m_checked_idx - 1] = '\0';
                m_read_buf[m_checked_idx++] = '\0';
                // 完整读取一行
                return LINE_OK;
            }
            // 否则报文语法有误
            return LINE_BAD;
        }
    }
    // 没有找到'\r\n',读取的行不完整
    return LINE_OPEN;
}

// 解析HTTP请求行
http_conn::HTTP_CODE http_conn::parse_request_line(char *text)
{
   
    // 返回请求行中最先含有 空格 或 '\t'的位置
    m_url = strpbrk(text, " \t");

    // 如果没有 '\t' 或 空格
    if (!m_url)
    {
   
        // HTTP请求报文有语法错误
        return BAD_REQUEST;
    }
    // 将该位置改为'\0',用于将请求类型取出
    *m_url++ = '\0';
    char *method = text;

    // 忽略大小写比较
    if (strcasecmp(method, "GET") == 0)
    {
   
        // GET请求
        m_method = GET;
    }
    else if (strcasecmp(method, "POST") == 0)
    {
   
        // POST请求
        m_method = POST;
        cgi = 1;
    }
    else
    {
   
        // HTTP请求报文有语法错误
        return BAD_REQUEST;
    }

    // 此时m_url跳过了第一个 空格 或 '\t',但不知道之后是否还有
    // 将m_url向后偏移,通过查找继续跳过 空格 和 '\t',指向请求资源的第一个字符
    m_url += strspn(m_url, " \t");

    m_version = strpbrk(m_url, " \t");
    // 如果没有 '\t' 或 空格
    if (!m_version)
    {
   
        // HTTP请求报文有语法错误
        return BAD_REQUEST;
    }
    // 将该位置改为'\0',用于将请求资源取出
    
  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值