【sylar-webserver】10 HTTP模块
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 XXINVALID_METHOD
};/*** @brief HTTP状态枚举*/
enum class HttpStatus {
#define XX(code, name, desc) name = code,HTTP_STATUS_MAP(XX)
#undef XX
};
HTTP 请求和响应结构
HTTP请求和响应的格式可参考HTTP消息 - HTTP | MDN
请求:
- 起始行
- 标头 Header
- 主体 Body
响应:
- 状态行
- 标头 Header
- 主体 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_close
为false
。m_websocket
: 此请求不是 WebSocket 请求,所以m_websocket
为false
。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_close
为false
。m_websocket
: 此响应不是 WebSocket 响应,所以m_websocket
为false
。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
相关文章:

【sylar-webserver】10 HTTP模块
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) …...
攻略生成模块
攻略生成模块 这个模块实现了一个旅行行程规划服务,主要流程如下: 核心思路是通过前端传来的城市和出游天数信息,先在本地数据库中查找是否已存有相应的旅游数据(例如景点、美食等),如果没有就自动检索和…...

海康NVR录像回放SDK原始流转FLV视频流:基于Java的流媒体转码(无需安装第三方插件ffmpeg)
wlinker-video-monitor 代码地址:https://gitee.com/wlinker/wlinker-video-monitor 背景与需求 在安防监控、智能楼宇等场景中,海康威视设备作为行业主流硬件,常需要将录像回放功能集成到Web系统中。然而,海康设备的原始视频流…...
深入理解设计模式:工厂模式、单例模式
深入理解设计模式:工厂模式、单例模式 设计模式是软件开发中解决常见问题的可复用方案。本文将详细介绍两种种重要的创建型设计模式:工厂模式、单例模式,并提供Java实现示例。 一、工厂模式 工厂模式是一种创建对象的设计模式,…...

运维Linux之Ansible详解学习(更新中)
什么是Ansible Ansible 是一款新出现的自动化运维工具,基于 Python 开发。以下是对它的详细介绍: 功能特点:集合了众多运维工具的优点,能实现批量系统配置、批量程序部署、批量运行命令等功能。它是基于模块工作的,本…...

深入浅出IIC协议 - 从总线原理到FPGA实战开发 -- 第三篇:Verilog实现I2C Master核
第三篇:Verilog实现I2C Master核 副标题 :从零构建工业级I2C控制器——代码逐行解析与仿真实战 1. 架构设计 1.1 模块分层设计 三层架构 : 层级功能描述关键信号PHY层物理信号驱动与采样sda_oe, scl_oe控制层协议状态机与数据流控制state…...
网络世界的“变色龙“:动态IP如何重构你的数据旅程?
在深秋的下午调试代码时,我偶然发现服务器日志中出现异常登录记录——IP地址显示为某个境外数据中心。更有趣的是,当我切换到公司VPN后,这个"可疑IP"竟自动消失在了防火墙监控列表中。这个瞬间让我意识到:现代网络架构中…...
进阶-自定义类型(结构体、位段、枚举、联合)
自定义类型:结构体,枚举,联合 结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对齐 结构体传参 结构体实现位段(位段的填充&可移植性) 枚举 枚举类型的定义 枚举的优点 枚举的使用 联合 联合类型的定义 联…...
5G 网络全场景注册方式深度解析:从信令交互到报文分析
摘要 本文全面梳理 5G 网络包含的初始注册、移动性注册更新、紧急注册、周期性注册更新、服务请求触发注册、切换触发注册、基于策略的注册更新等多种注册方式。详细阐述每种注册方式的触发条件、信令流程、关键报文结构,结合对比分析与实际案例,助力读者深入理解 5G 网络接…...

ARM笔记-嵌入式系统基础
第一章 嵌入式系统基础 1.1嵌入式系统简介 1.1.1嵌入式系统定义 嵌入式系统定义: 嵌入式系统是以应用为中心,以计算机技术为基础,软硬件可剪裁,对功能、可靠性、成本、体积、功耗等有严格要求的专用计算机系统 ------Any devic…...
一文讲透golang channel 的特点、原理及使用场景
在 Go 语言中,通道(Channel) 是实现并发编程的核心机制之一,基于 CSP(Communicating Sequential Processes) 模型设计。它不仅用于协程(Goroutine)之间的数据传递,还通过…...

upload-labs通关笔记-第19关文件上传之条件竞争
系列目录 upload-labs通关笔记-第1关 文件上传之前端绕过(3种渗透方法) 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
今天遇到一个问题,svn 在提交代码的时候出现了svn is already locked,解决方案...

【深度学习】1. 感知器,MLP, 梯度下降,激活函数,反向传播,链式法则
一、感知机 对于分类问题,我们设定一个映射,将x通过函数f(x)映射到y 1. 感知机的基本结构 感知机(Perceptron)是最早期的神经网络模型,由 Rosenblatt 在 1958 年提出,是现代神经网络和深度学习模型的雏形…...

云原生安全:网络协议TCP详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 (注:文末附可视化流程图与专有名词说明表) 1. 基础概念 TCP(Transmission Control Protocol)是…...

使用CentOS部署本地DeekSeek
一、查看服务器的操作系统版本 cat /etc/centos-release二、下载并安装ollama 1、ollama下载地址: 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…...
Spring Boot与Eventuate Tram整合:构建可靠的事件驱动型分布式事务
精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、引言 在现代微服务架构中,分布式事务管理一直是复杂系统中的核心挑战之一。传统的两阶段提交(2PC)方案存在性能瓶颈&…...
Python:从脚本语言到工业级应用的传奇进化
一、Python的诞生:一场喜剧与编程的奇妙相遇 1989年的冬天,荷兰程序员Guido van Rossum在阿姆斯特丹的CWI研究所里,用一段独特的代码开启了编程语言的新纪元。这个被命名为"Python"的项目,灵感并非源自冷血的蟒蛇,而是源于Guido对英国喜剧团体Monty Python的痴…...
【排序算法】典型排序算法 Java实现
以下是典型的排序算法分类及对应的 Java 实现,包含时间复杂度、稳定性说明和核心代码示例: 一、比较类排序(通过元素比较) 1. 交换排序 ① 冒泡排序 时间复杂度:O(n)(优化后最优O(n)) 稳定性&…...
node.js如何实现双 Token + Cookie 存储 + 无感刷新机制
node.js如何实现双 Token Cookie 存储 无感刷新机制 为什么要实施双token机制? 优点描述安全性Access Token 短期有效,降低泄露风险;Refresh Token 权限受限,仅用于获取新 Token用户体验用户无需频繁重新登录,Toke…...
[DS]使用 Python 库中自带的数据集来实现上述 50 个数据分析和数据可视化程序的示例代码
使用 Python 库中自带的数据集来实现上述 50 个数据分析和数据可视化程序的示例代码 摘要:由于 sample_data.csv 是一个占位符文件,用于代表任意数据集,我将使用 Python 库中自带的数据集来实现上述 50 个数据分析和数据可视化程序的示例代码…...
探索智能仓颉
探索智能仓颉:Cangjie Magic体验有感 一、引言 在人工智能和智能体开发领域,新的技术和框架不断涌现,推动着行业的快速发展。2025年3月,仓颉社区开源了Cangjie Magic,这是一个基于仓颉编程语言原生构建的LLM Agent开…...
Ubuntu 上开启 SSH 服务、禁用密码登录并仅允许密钥认证
1. 安装 OpenSSH 服务 如果尚未安装 SSH 服务,运行以下命令: sudo apt update sudo apt install openssh-server2. 启动 SSH 服务并设置开机自启 sudo systemctl start ssh sudo systemctl enable ssh3. 生成 SSH 密钥对(本地机器…...

LLMs之Qwen:《Qwen3 Technical Report》翻译与解读
LLMs之Qwen:《Qwen3 Technical Report》翻译与解读 导读:Qwen3是Qwen系列最新的大型语言模型,它通过集成思考和非思考模式、引入思考调度机制、扩展多语言支持以及采用强到弱的知识等创新技术,在性能、效率和多语言能力方面都取得…...
springboot3 configuration
1 多数据库配置 github: https://github.com/baomidou/dynamic-datasource 使用DS()注解来切换数据库 详情介绍:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611 注意:DS 可以注解在方法上或类上,同时存在就近原则 方法上注…...

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

如何设计一个高性能的短链设计
1.什么是短链 短链接(Short URL) 是通过算法将长 URL 压缩成简短字符串的技术方案。例如将 https://flowus.cn/veal/share/3306b991-e1e3-4c92-9105-95abf086ae4e 缩短为 https://sourl.cn/aY95qu,用户点击短链时会自动重定向到原始长链接。其…...

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