📘 第三章 HTTP报文内的HTTP信息
📄 一句话概括

请求和响应结构完全对称,只有第一行不同:请求写「方法 目标 版本」,响应写「版本 码 短语」。

📝 本节和前面几节的关系

上一节看了报文的通用形状,这一节把两种报文并排放,说清哪一段对应哪一段、哪些字段只在一边出现。

一、对称的四段

部位 请求报文 响应报文
第一行 请求行:方法 目标 版本 状态行:版本 码 短语
中间 请求首部若干行 响应首部若干行
分界 空行 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 秒实验

  1. 把 303 换成 307,想想客户端重发的会是什么方法。
  2. 在请求里加 Accept: text/html,看它该归到哪一组首部。
  3. 把 Location 写成一个完整 URL(http://127.0.0.1/x),看客户端会不会跟去。

常见误区

  1. 「请求行和状态行格式一样」——都是三段,但第一段一个方法一个版本,完全相反。
  2. 「所有首部两边都能用」——Host、Location 这类是单向的。
  3. 「303 和 302 没区别」——303 强制换 GET,302 历史语义混乱。
自测三问(点开对答案) 1. 请求行三段各是什么? —— 方法、请求目标、HTTP 版本。 2. 状态行三段各是什么? —— HTTP 版本、状态码、原因短语。 3. 哪个首部只在响应里出现、用来指示新地址? —— `Location`。

结构讲清了。下一节进最容易被误解的一对词:报文主体与实体主体。

相关笔记