服务端解析 HTTP 请求的过程遵循 HTTP 协议规范,主要分为以下步骤:
1. 读取并解析请求行(Request Line)
- 结构:
METHOD URI HTTP/VERSION
(如GET /index.html HTTP/1.1
)。 - 解析步骤:
- 读取首行数据:通过套接字读取数据,直到遇到
\r\n
(CRLF)结束符。 - 分割三部分:按空格分割方法(如
GET
)、请求的 URI(如/index.html
)、协议版本(如HTTP/1.1
)。 - 校验合法性:验证方法是否有效(如 GET、POST)、URI 格式、协议版本是否支持。
- URI 处理:对 URI 进行 URL 解码(如
%20
转为空格)和路径规范化。
- 读取首行数据:通过套接字读取数据,直到遇到
2. 解析请求头(Headers)
- 结构:键值对格式,如
Host: example.com
,以空行\r\n
结束。 - 解析步骤:
- 逐行读取:持续读取直到遇到空行
\r\n\r\n
。 - 处理键值对:每行按
:
分割键和值(需处理首尾空格及多行折叠情况)。 - 关键头部处理:
- Host:确定请求的目标域名(HTTP/1.1 必需字段)。
- Content-Length:明确请求体长度(字节单位)。
- Transfer-Encoding:如
chunked
表示分块传输。 - Content-Type:指定请求体格式(如
application/json
或multipart/form-data
)。
- 逐行读取:持续读取直到遇到空行
3. 处理请求体(Body)
- 触发条件:仅当方法允许携带请求体(如 POST、PUT)时处理。
- 解析策略:
- 根据 Content-Length 读取:
- 直接读取指定字节数的数据作为请求体。
- 校验实际长度与头部是否一致。
- 分块传输(Transfer-Encoding: chunked):
- 循环读取每个分块:先读十六进制长度,再读对应数据,直到遇到
0\r\n
结束。
- 循环读取每个分块:先读十六进制长度,再读对应数据,直到遇到
- 根据 Content-Type 解析内容:
- URL 编码(
application/x-www-form-urlencoded
):解析key=value
对。 - 多部分表单(
multipart/form-data
):按boundary
分隔符分割各部分,逐项解析字段和文件。 - JSON/XML:反序列化为结构化数据。
- URL 编码(
- 根据 Content-Length 读取:
4. 错误处理与安全措施
- 协议错误:非法格式、缺失必需头部时返回
400 Bad Request
。 - 数据限制:限制请求头/体大小,防止资源耗尽(如
client_max_body_size
)。 - 编码处理:正确解码 URL 和字符编码(如 UTF-8)。
- 路径安全:防止路径遍历攻击(如
../
跳出根目录)。
示例解析流程
POST /submit HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
name=Alice&message=Hello%21
- 请求行:解析方法
POST
,URI/submit
,版本HTTP/1.1
。 - 头部:读取
Host
、Content-Type
和Content-Length
。 - 请求体:按长度 27 字节读取数据,解析为
name=Alice&message=Hello!
。
实际应用中的实现
- 底层框架:如 Node.js 的
http
模块、Python 的http.server
自动完成解析。 - 开发者接口:通过
req.method
、req.url
、req.headers
和req.body
(需中间件解析)访问数据。
通过以上步骤,服务端将原始字节流转换为结构化的请求对象,供应用程序处理业务逻辑。