2. HTTP报文格式解析与构造
任务目标
编写一个HTTP报文解析与构造工具,能够:
- 解析完整的HTTP/1.1请求报文,提取方法、URL、头部字段和消息主体;
- 根据给定参数构造合法的HTTP请求报文。
实现方案
使用Python标准库实现,不依赖第三方包。核心包含两个函数:parse_http_request() 负责解析,build_http_request() 负责构造。解析采用逐行扫描方式,正确处理请求行、头部区与主体之间的空行分隔。
完整代码
def parse_http_request(raw: str) -> dict:
"""解析HTTP/1.1请求报文,返回结构化字典"""
# 分割请求行与后续部分
lines = raw.split("\r\n")
if len(lines) < 1 or not lines[0]:
raise ValueError("空报文")
# 解析请求行: METHOD SP URL SP VERSION
request_line = lines[0].split(" ")
if len(request_line) != 3:
raise ValueError(f"非法请求行: {lines[0]}")
method, url, version = request_line
# 解析头部与主体
headers = {}
body = ""
body_started = False
idx = 1
while idx < len(lines):
line = lines[idx]
if body_started:
# 主体部分(可能包含空行,直接拼接)
body += line + ("\r\n" if idx < len(lines) - 1 else "")
elif line == "":
# 空行:头部结束,后续为主体
body_started = True
else:
# 头部字段: Name: Value
if ":" not in line:
raise ValueError(f"非法头部行: {line}")
name, value = line.split(":", 1)
headers[name.strip()] = value.strip()
idx += 1
return {
"method": method,
"url": url,
"version": version,
"headers": headers,
"body": body
}
def build_http_request(method: str, url: str,
headers: dict = None,
body: str = "") -> str:
"""构造HTTP/1.1请求报文"""
if not method or not url:
raise ValueError("method和url不能为空")
# 请求行
lines = [f"{method} {url} HTTP/1.1"]
# 头部
headers = headers or {}
# 自动补充必要头部
if "Host" not in headers:
headers["Host"] = "localhost"
if body and "Content-Length" not in headers:
headers["Content-Length"] = str(len(body.encode("utf-8")))
for name, value in headers.items():
lines.append(f"{name}: {value}")
# 空行分隔头部与主体
lines.append("")
# 主体
if body:
lines.append(body)
return "\r\n".join(lines)
# ---------- 验证示例 ----------
if __name__ == "__main__":
# 构造一个POST请求
raw_request = build_http_request(
method="POST",
url="/api/login",
headers={
"Host": "example.com",
"Content-Type": "application/json",
"User-Agent": "test-agent"
},
body='{"username":"admin","password":"123456"}'
)
print("=== 构造的报文 ===")
print(raw_request)
print()
# 解析该报文
parsed = parse_http_request(raw_request)
print("=== 解析结果 ===")
print(f"方法: {parsed['method']}")
print(f"URL: {parsed['url']}")
print(f"版本: {parsed['version']}")
print(f"头部数量: {len(parsed['headers'])}")
for k, v in parsed['headers'].items():
print(f" {k}: {v}")
print(f"主体: {parsed['body']}")
# 断言验证
assert parsed["method"] == "POST"
assert parsed["url"] == "/api/login"
assert parsed["headers"]["Content-Type"] == "application/json"
assert parsed["body"] == '{"username":"admin","password":"123456"}'
print("\n✅ 所有断言通过:解析结果与构造参数一致")
运行与验证
- 将代码保存为
http_message.py; - 在终端执行
python3 http_message.py; - 观察输出:程序先打印构造的完整报文,再打印解析出的各字段;
- 通过条件:终端显示“✅ 所有断言通过”,且解析出的方法、URL、头部值与主体内容与构造时传入的参数完全一致。
关键说明
- 解析器严格按
\r\n作为行分隔符,符合HTTP/1.1规范; - 头部名称统一去除首尾空格后作为字典键,值同样去除首尾空格;
- 构造时若未提供
Host头则自动补localhost,若存在主体且未指定Content-Length则按UTF-8字节数自动计算; - 本实现聚焦请求报文,响应报文格式类似(状态行+头部+主体),可复用头部与主体处理逻辑。