请求和响应结构完全对称,只有第一行不同:请求写「方法 目标 版本」,响应写「版本 码 短语」。
上一节看了报文的通用形状,这一节把两种报文并排放,说清哪一段对应哪一段、哪些字段只在一边出现。
一、对称的四段
| 部位 | 请求报文 | 响应报文 |
|---|---|---|
| 第一行 | 请求行:方法 目标 版本 | 状态行:版本 码 短语 |
| 中间 | 请求首部若干行 | 响应首部若干行 |
| 分界 | 空行 CRLF | 空行 CRLF |
| 最后 | 请求主体(可无) | 响应主体(可无) |
| 四段一一对应。写解析器时可以复用同一段代码,只在读第一行时分支——这是所有 HTTP 库的做法。 |
二、只在一边出现的首部
有些首部只能出现在请求里:Host、Accept、Range、Referer、User-Agent。
有些只能出现在响应里:Location、Server、Set-Cookie、WWW-Authenticate。
还有些两边都能出现:Content-Type、Cache-Control、Date。
第六章会按这个分类把它们分到四组(通用/请求/响应/实体)。
Demo 里响应有 4 行首部、请求有 3 行,其中 Content-Type 两边都有——它是少见的「通用型」,
谁都能带。
三、303 这一行值得单独说
Demo 的响应是 HTTP/1.1 303 See Other,配 Location: /done。
这是 POST 之后避免重复提交的标准做法:处理完让客户端「去别处看看结果」。
为什么不用 302?因为 302 在历史实现里语义混乱,有的客户端会把 POST 改成 GET,有的原样重发 POST。
303 明确规定了「换成 GET 去那个地址」,307/308 则明确规定「保持原方法重发」。
这条差别直接关系到「用户会不会被重复下单」——第四章讲 3XX 时会再细说。
四、动手 Demo:并排拆一份请求与一份响应
发一个带正文的 POST,服务端回 303 加 Location,然后用同一套解析把两边各拆成四段。
req = _req(u'POST', u'/submit?x=1',
extra=[(u'Content-Type', u'text/plain')],
body=u'abc'.encode())
rl = req.split(b'\r\n')
sl = got.split(b'\r\n')
print(rl[0].decode(), u'|', sl[0].decode())
实跑输出:
--------------------------------------------------------------------
请求与响应:同样四段,只差首行
--------------------------------------------------------------------
请求首行 POST /submit?x=1 HTTP/1.1
响应的首行 HTTP/1.1 303 See Other
请求首部行数 3
响应首部行数 4
请求主体 3 字节
响应主体 9 字节
两段结构完全对称:第一行说明「这是什么」,中间若干行说明「怎么处理」,
空行之后是「真正的内容」。
请求第一行三段是「方法 目标 版本」,响应第一行三段是「版本 码 短语」——
**同样三段,语义完全不同**,这是读抓包时最容易看错的地方。
这里的 303 是「去别处看结果」,常配合 POST 之后跳转,避免刷新重提交。
请求首行 POST /submit?x=1 HTTP/1.1 对 响应的首行 HTTP/1.1 303 See Other——三段对三段,
位置一样、含义全反。
请求首部行数 3、响应首部行数 4:响应多出来的那行是 Location,它是 3XX 与 201 的专属首部。
请求主体 3 字节、响应主体 9 字节。
两边的正文都可以为空——空的话空行后面直接结束,不再有任何字节。
30 秒实验
- 把 303 换成 307,想想客户端重发的会是什么方法。
- 在请求里加
Accept: text/html,看它该归到哪一组首部。 - 把
Location写成一个完整 URL(http://127.0.0.1/x),看客户端会不会跟去。
常见误区
- 「请求行和状态行格式一样」——都是三段,但第一段一个方法一个版本,完全相反。
- 「所有首部两边都能用」——
Host、Location这类是单向的。 - 「303 和 302 没区别」——303 强制换 GET,302 历史语义混乱。
自测三问(点开对答案)
1. 请求行三段各是什么? —— 方法、请求目标、HTTP 版本。 2. 状态行三段各是什么? —— HTTP 版本、状态码、原因短语。 3. 哪个首部只在响应里出现、用来指示新地址? —— `Location`。结构讲清了。下一节进最容易被误解的一对词:报文主体与实体主体。
- ☐ (本节完,下一节 3.3.1 报文主体和实体主体的差异)