【sylar-webserver】10 HTTP模块

news2025/6/9 19:13:39

HTTP 解析

这里使用 nodejs/http-parser 提供的 HTTP 解析器。

HTTP 常量定义

HttpMethod HttpStatus

/* Request Methods */
#define HTTP_METHOD_MAP(XX)         \
  XX(0,  DELETE,      DELETE)       \
  XX(1,  GET,         GET)          \
  XX(2,  HEAD,        HEAD)         \
  XX(3,  POST,        POST)         \
  XX(4,  PUT,         PUT)          \
...
 
/* Status Codes */
#define HTTP_STATUS_MAP(XX)                                                 \
  XX(100, CONTINUE,                        Continue)                        \
  XX(101, SWITCHING_PROTOCOLS,             Switching Protocols)             \
  XX(102, PROCESSING,                      Processing)                      \
  XX(200, OK,                              OK)                              \
  XX(201, CREATED,                         Created)                         \
  XX(202, ACCEPTED,                        Accepted)                        \
  XX(203, NON_AUTHORITATIVE_INFORMATION,   Non-Authoritative Information)   \
...

看来宏定义里 XX 常表示 宏定义函数,开源代码也是这样设计。

/**
 * @brief HTTP方法枚举
 */
enum class HttpMethod {
#define XX(num, name, string) name = num,
    HTTP_METHOD_MAP(XX)
#undef XX
    INVALID_METHOD
};
 
/**
 * @brief HTTP状态枚举
 */
enum class HttpStatus {
#define XX(code, name, desc) name = code,
    HTTP_STATUS_MAP(XX)
#undef XX
};

HTTP 请求和响应结构

HTTP请求和响应的格式可参考HTTP消息 - HTTP | MDN

请求:

  1. 起始行
  2. 标头 Header
  3. 主体 Body

响应:

  1. 状态行
  2. 标头 Header
  3. 主体 Body

HttpRequest 类的部分成员变量
  • m_method: 表示 HTTP 请求的方法,如 GET、POST 等。
  • m_version: 表示 HTTP 协议的版本,例如 0x11 代表 HTTP/1.1。
  • m_close: 表示是否自动关闭连接,true 表示关闭,false 表示保持连接。
  • m_websocket: 表示是否为 WebSocket 请求。
  • m_parserParamFlag: 是一个标志位,用于记录参数解析的状态。每一位代表不同的解析状态,0x1 表示已解析 URL 参数,0x2 表示已解析 HTTP 消息体中的参数,0x4 表示已解析 Cookies。
  • m_url: 存储请求的完整 URL。
  • m_path: 存储请求的路径部分。
  • m_query: 存储请求的查询参数部分。
  • m_fragment: 存储请求的 Fragment 部分。
  • m_body: 存储请求的消息体。
  • m_headers: 是一个 MapType 类型的映射,用于存储请求的头部信息,键为头部字段名,值为头部字段值。
  • m_params: 是一个 MapType 类型的映射,用于存储请求的参数信息,键为参数名,值为参数值。
  • m_cookies: 是一个 MapType 类型的映射,用于存储请求的 Cookies 信息,键为 Cookie 名,值为 Cookie 值。
initQueryParam
void HttpRequest::initQueryParam() {
    if (m_parserParamFlag & 0x1) {
        return;
    }

#define PARSE_PARAM(str, m, flag, trim)                                                                    \
    size_t pos = 0;                                                                                        \
    do {                                                                                                   \
        size_t last = pos;                                                                                 \
        pos         = str.find('=', pos);                                                                  \
        if (pos == std::string::npos) {                                                                    \
            break;                                                                                         \
        }                                                                                                  \
        size_t key = pos;                                                                                  \
        pos        = str.find(flag, pos);                                                                  \
                                                                                                           \
        if (0) {                                                                                           \
            std::cout << "<key>:" << str.substr(last, key - last)                                          \
                      << " <decoded>:" << sylar::StringUtil::UrlDecode(str.substr(last, key - last))       \
                      << " <value>:" << str.substr(key + 1, pos - key - 1)                                 \
                      << " <decoded>:" << sylar::StringUtil::UrlDecode(str.substr(key + 1, pos - key - 1)) \
                      << std::endl;                                                                        \
        }                                                                                                  \
                                                                                                           \
        m.insert(std::make_pair(sylar::StringUtil::UrlDecode(trim(str.substr(last, key - last))),          \
                                sylar::StringUtil::UrlDecode(str.substr(key + 1, pos - key - 1))));        \
        if (pos == std::string::npos) {                                                                    \
            break;                                                                                         \
        }                                                                                                  \
        ++pos;                                                                                             \
    } while (true);

    PARSE_PARAM(m_query, m_params, '&', );
    m_parserParamFlag |= 0x1;
}

从 m_query 解析,查询参数按照 & 分割,每个参数再按 = 分割成键值对。

对键和值进行 URL 编码,并插入到 m_params 中。

initBodyParam
void HttpRequest::initBodyParam() {
    if (m_parserParamFlag & 0x2) {
        return;
    }
    std::string content_type = getHeader("content-type");
    if (strcasestr(content_type.c_str(), "application/x-www-form-urlencoded") == nullptr) {
        m_parserParamFlag |= 0x2;
        return;
    }
    PARSE_PARAM(m_body, m_params, '&', );
    m_parserParamFlag |= 0x2;
}

从 m_headers 里拿到 Content-Type 字段,application/x-www-form-urlencoded 类型

m_body,消息体参数按 & 分割,每个参数再按 = 分割成键值对。

对键和值进行 URL 编码,并插入到 m_params 中。

initCookies
void HttpRequest::initCookies() {
    if (m_parserParamFlag & 0x4) {
        return;
    }
    std::string cookie = getHeader("cookie");
    if (cookie.empty()) {
        m_parserParamFlag |= 0x4;
        return;
    }
    PARSE_PARAM(cookie, m_cookies, ';', sylar::StringUtil::Trim);
    m_parserParamFlag |= 0x4;
}

从 m_headers 里拿到 cookie 字段

m_body,消息体参数按 ; 分割,每个参数再按 = 分割成键值对。

对键和值进行 URL 编码,并插入到 m_cookies 中。

上面的 m_path,m_query,m_fragment,m_headers(MapType),m_version,m_method,m_body 参数是 Http_parser 时调用回调添加的,详细见下一部分。

解析案例

GET /search?keyword=apple&category=fruit#product1 HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Language: en-US,en;q=0.9
Cookie: session_id=123456; user=testuser
Connection: keep-alive

这个案例里 m_body 里没有 content-type

不同的 Content-Type 意味着消息体的格式和解析方式不同。例如:⭐

  • application/x-www-form-urlencoded:消息体是由 & 分隔的键值对,如 key1=value1&key2=value2
  • multipart/form-data:常用于文件上传,消息体的格式更为复杂,包含多个部分,每个部分有自己的头部和数据。
  • application/json:消息体是 JSON 格式的数据。

HttpRequest 类成员变量对应说明
  • m_method: 代表 HTTP 请求的方法,在这个例子中是 GET
  • m_version: 代表 HTTP 协议的版本,这里是 0x11,即 HTTP/1.1。
  • m_close: 根据 Connection 头部字段判断是否自动关闭连接,由于这里是 keep - alive,所以 m_closefalse
  • m_websocket: 此请求不是 WebSocket 请求,所以 m_websocketfalse
  • m_url: 请求的完整 URL,即 /search?keyword=apple&category=fruit#product1
  • m_path: 请求路径,为 /search
  • m_query: 请求参数,是 keyword=apple&category=fruit
  • m_fragment: 请求的 Fragment 部分,是 product1
  • m_body: 由于是 GET 请求,通常没有消息体,所以 m_body 为空字符串。
  • m_headers: 存储请求的头部信息,是一个映射,键值对如下:
    • "Host": "www.example.com"
    • "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    • "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
    • "Accept-Language": "en-US,en;q=0.9"
    • "Cookie": "session_id=123456; user=testuser"
    • "Connection": "keep-alive"
  • m_params: 在调用 initQueryParam 方法后,会存储解析后的查询参数,键值对为:
    • "keyword": "apple"
    • "category": "fruit"
  • m_cookies: 在调用 initCookies 方法后,会存储解析后的 Cookie 信息,键值对为:
    • "session_id": "123456"
    • "user": "testuser"

HTTP/1.1 200 OK
Date: Tue, 15 Jun 2023 12:00:00 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Content-Length: 1234
Connection: keep-alive

<!DOCTYPE html>
<html>
<head>
    <title>Search Results</title>
</head>
<body>
    <h1>Search Results for Apple in Fruit Category</h1>
    <!-- 其他HTML内容 -->
</body>
</html>
HttpResponse 类成员变量对应说明
  • m_status: 代表响应状态,这里是 200,即 HttpStatus::OK
  • m_version: 代表 HTTP 协议的版本,为 0x11,即 HTTP/1.1。
  • m_close: 根据 Connection 头部字段判断是否自动关闭连接,由于是 keep - alive,所以 m_closefalse
  • m_websocket: 此响应不是 WebSocket 响应,所以 m_websocketfalse
  • m_body: 响应消息体,是 HTML 内容:
<!DOCTYPE html>
<html>
<head>
    <title>Search Results</title>
</head>
<body>
    <h1>Search Results for Apple in Fruit Category</h1>
    <!-- 其他HTML内容 -->
</body>
</html>
  • m_reason: 响应原因,是 OK
  • m_headers: 存储响应的头部信息,是一个映射,键值对如下:
    • "Date": "Tue, 15 Jun 2023 12:00:00 GMT"
    • "Server": "Apache/2.4.41 (Ubuntu)"
    • "Content-Type": "text/html; charset=UTF-8"
    • "Content-Length": "1234"
    • "Connection": "keep-alive"
  • m_cookies: 此响应中没有设置 Cookie,所以 m_cookies 为空。

HTTP 解析器

struct http_parser {
  /** PRIVATE **/
  unsigned int type : 2;         /* enum http_parser_type */
  unsigned int flags : 8;        /* F_* values from 'flags' enum; semi-public */
  unsigned int state : 7;        /* enum state from http_parser.c */
  unsigned int header_state : 7; /* enum header_state from http_parser.c */
  unsigned int index : 7;        /* index into current matcher */
  unsigned int lenient_http_headers : 1;

  uint32_t nread;          /* # bytes read in various scenarios */
  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */

  /** READ-ONLY **/
  unsigned short http_major;
  unsigned short http_minor;
  unsigned int status_code : 16; /* responses only */
  unsigned int method : 8;       /* requests only */
  unsigned int http_errno : 7;

  /* 1 = Upgrade header was present and the parser has exited because of that.
   * 0 = No upgrade header present.
   * Should be checked when http_parser_execute() returns in addition to
   * error checking.
   */
  unsigned int upgrade : 1;

  /** PUBLIC **/
  void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
HttpRequestParser::HttpRequestParser()   --->   http_parser_init(&m_parser, HTTP_REQUEST); HTTP_REQUEST,HTTP_RESPONSE指定解析的m_parser类型。

HttpRequestParser::execute(char *data, size_t len)	---> 	http_parser_execute(&m_parser, &s_request_settings, data, len);

// 设定解析中调用 回调函数,将参数保存到 m_data 中。
static http_parser_settings s_request_settings ={
    .on_message_begin    = on_request_message_begin_cb,
    .on_url              = on_request_url_cb,
    .on_status           = on_request_status_cb,
    .on_header_field     = on_request_header_field_cb,
    .on_header_value     = on_request_header_value_cb,
    .on_headers_complete = on_request_headers_complete_cb,
    .on_body             = on_request_body_cb,
    .on_message_complete = on_request_message_complete_cb,
    .on_chunk_header     = on_request_chunk_header_cb,
    .on_chunk_complete   = on_request_chunk_complete_cb
};

HttpSession

继承自 SocketStream,实现了在套接字流上读取HTTP请求与发送HTTP响应的功能,在读取HTTP请求时需要借助HTTP解析器,以便于将套接字流上的内容解析成HTTP请求。

HttpServer

继承自TcpServer,重载handleClient方法,将accept后得到的客户端套接字封装成HttpSession结构,以便于接收和发送HTTP消息。

HttpServlet

HTTP客户端HttpConnection

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2386230.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

海康NVR录像回放SDK原始流转FLV视频流:基于Java的流媒体转码(无需安装第三方插件ffmpeg)

wlinker-video-monitor 代码地址&#xff1a;https://gitee.com/wlinker/wlinker-video-monitor 背景与需求 在安防监控、智能楼宇等场景中&#xff0c;海康威视设备作为行业主流硬件&#xff0c;常需要将录像回放功能集成到Web系统中。然而&#xff0c;海康设备的原始视频流…

运维Linux之Ansible详解学习(更新中)

什么是Ansible Ansible 是一款新出现的自动化运维工具&#xff0c;基于 Python 开发。以下是对它的详细介绍&#xff1a; 功能特点&#xff1a;集合了众多运维工具的优点&#xff0c;能实现批量系统配置、批量程序部署、批量运行命令等功能。它是基于模块工作的&#xff0c;本…

深入浅出IIC协议 - 从总线原理到FPGA实战开发 -- 第三篇:Verilog实现I2C Master核

第三篇&#xff1a;Verilog实现I2C Master核 副标题 &#xff1a;从零构建工业级I2C控制器——代码逐行解析与仿真实战 1. 架构设计 1.1 模块分层设计 三层架构 &#xff1a; 层级功能描述关键信号PHY层物理信号驱动与采样sda_oe, scl_oe控制层协议状态机与数据流控制state…

ARM笔记-嵌入式系统基础

第一章 嵌入式系统基础 1.1嵌入式系统简介 1.1.1嵌入式系统定义 嵌入式系统定义&#xff1a; 嵌入式系统是以应用为中心&#xff0c;以计算机技术为基础&#xff0c;软硬件可剪裁&#xff0c;对功能、可靠性、成本、体积、功耗等有严格要求的专用计算机系统 ------Any devic…

upload-labs通关笔记-第19关文件上传之条件竞争

系列目录 upload-labs通关笔记-第1关 文件上传之前端绕过&#xff08;3种渗透方法&#xff09; upload-labs通关笔记-第2关 文件上传之MIME绕过-CSDN博客 upload-labs通关笔记-第3关 文件上传之黑名单绕过-CSDN博客 upload-labs通关笔记-第4关 文件上传之.htacess绕过-CSDN…

第5章:任务间通信机制(IPC)全解析

💬 在多线程开发中,线程之间如何协作?如何让一个线程产生数据,另一个线程消费数据?本章聚焦 Zephyr 提供的多种任务间通信机制(IPC)及实战使用技巧。 📚 本章导读 你将学到: Zephyr 提供的常用 IPC 接口:FIFO、消息队列、邮箱、信号量 每种机制适用场景和用法对比…

CAPL自动化-诊断Demo工程

文章目录 前言一、诊断控制面板二、诊断定义三、发送诊断通过类.方法的方式req.SetParameterdiagSetParameter四、SendRequestAndWaitForResponse前言 本文将介绍CANoe的诊断自动化测试,工程可以从CANoe的 Sample Configruration 界面打开,也可以参考下面的路径中打开(以实…

SVN被锁定解决svn is already locked

今天遇到一个问题&#xff0c;svn 在提交代码的时候出现了svn is already locked&#xff0c;解决方案

【深度学习】1. 感知器,MLP, 梯度下降,激活函数,反向传播,链式法则

一、感知机 对于分类问题&#xff0c;我们设定一个映射&#xff0c;将x通过函数f(x)映射到y 1. 感知机的基本结构 感知机&#xff08;Perceptron&#xff09;是最早期的神经网络模型&#xff0c;由 Rosenblatt 在 1958 年提出&#xff0c;是现代神经网络和深度学习模型的雏形…

云原生安全:网络协议TCP详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 &#xff08;注&#xff1a;文末附可视化流程图与专有名词说明表&#xff09; 1. 基础概念 TCP&#xff08;Transmission Control Protocol&#xff09;是…

使用CentOS部署本地DeekSeek

一、查看服务器的操作系统版本 cat /etc/centos-release二、下载并安装ollama 1、ollama下载地址&#xff1a; Releases ollama/ollama GitHubGet up and running with Llama 3.3, DeepSeek-R1, Phi-4, Gemma 3, Mistral Small 3.1 and other large language models. - Re…

LLMs之Qwen:《Qwen3 Technical Report》翻译与解读

LLMs之Qwen&#xff1a;《Qwen3 Technical Report》翻译与解读 导读&#xff1a;Qwen3是Qwen系列最新的大型语言模型&#xff0c;它通过集成思考和非思考模式、引入思考调度机制、扩展多语言支持以及采用强到弱的知识等创新技术&#xff0c;在性能、效率和多语言能力方面都取得…

从工程实践角度分析H.264与H.265的技术差异

作为音视频从业者&#xff0c;我们时刻关注着视频编解码技术的最新发展。RTMP推流、轻量级RTSP服务、RTMP播放、RTSP播放等模块是大牛直播SDK的核心功能&#xff0c;在这些模块的实现过程中&#xff0c;H.264和H.265两种视频编码格式的应用实践差异是我们技术团队不断深入思考的…

如何设计一个高性能的短链设计

1.什么是短链 短链接&#xff08;Short URL&#xff09; 是通过算法将长 URL 压缩成简短字符串的技术方案。例如将 https://flowus.cn/veal/share/3306b991-e1e3-4c92-9105-95abf086ae4e 缩短为 https://sourl.cn/aY95qu&#xff0c;用户点击短链时会自动重定向到原始长链接。其…

提升工作效率的可视化笔记应用程序

StickyNotes桌面便签软件介绍 StickyNotes是一款极为简洁的桌面便签应用程序&#xff0c;让您能够快速记录想法、待办事项或其他重要信息。这款工具操作极其直观&#xff0c;只需输入文字内容&#xff0c;选择合适的字体大小和颜色&#xff0c;然后点击添加按钮即可创建个性化…

11|省下钱买显卡,如何利用开源模型节约成本?

不知道课程上到这里&#xff0c;你账户里免费的5美元的额度还剩下多少了&#xff1f;如果你尝试着完成我给的几个数据集里的思考题&#xff0c;相信这个额度应该是不太够用的。而ChatCompletion的接口&#xff0c;又需要传入大量的上下文信息&#xff0c;实际消耗的Token数量其…

机器学习圣经PRML作者Bishop20年后新作中文版出版!

机器学习圣经PRML作者Bishop20年后新书《深度学习&#xff1a;基础与概念》出版。作者克里斯托弗M. 毕晓普&#xff08;Christopher M. Bishop&#xff09;微软公司技术研究员、微软研究 院 科学智 能 中 心&#xff08;Microsoft Research AI4Science&#xff09;负责人。剑桥…

吴恩达机器学习笔记:逻辑回归3

3.判定边界 现在说下决策边界(decision boundary)的概念。这个概念能更好地帮助我们理解逻辑回归的假设函数在计算什么。 在逻辑回归中&#xff0c;我们预测&#xff1a; 当ℎθ (x) > 0.5时&#xff0c;预测 y 1。 当ℎθ (x) < 0.5时&#xff0c;预测 y 0 。 根据…

docker中使用openresty

1.为什么要使用openresty 我这边是因为要使用1Panel&#xff0c;第一个最大的原因&#xff0c;就是图方便&#xff0c;比较可以一键安装。但以前一直都是直接安装nginx。所以需要一个过度。 2.如何查看openResty使用了nginx哪个版本 /usr/local/openresty/nginx/sbin/nginx …

Java 中的 super 关键字

个人总结&#xff1a; 1.子类构造方法中没有显式使用super&#xff0c;Java 也会默认调用父类的无参构造方法 2.当父类中没有无参构造方法&#xff0c;只有有参构造方法时&#xff0c;子类构造方法就必须显式地使用super来调用父类的有参构造方法。 3.如果父类没有定义任何构造…