HTTP 的历史包袱

HTTP/3 通过基于 UDP 的 QUIC 传输。RFC 9114 于 2022 年发布,状态是 Proposed Standard。教科书里那句“HTTP 基于 TCP”,看来得补个脚注了:HTTP/1.1 通常跑在 TCP 上,HTTP/3 则跑在 QUIC 上;HTTP 本身不是 TCP 的别名。

RFC 9110 把 HTTP 定义为应用层协议,底下只要求可靠、有序的传输或会话层连接。协议和传输层本来就不是一回事,只是 TCP 陪 HTTP/1.1 陪得太久,久到大家快把它俩当成一对了。

HTTP/0.9 的请求行长这样:GET /page.html。没有版本号,也没有请求标头。后来请求行加上了版本信息,于是版本号被放在末尾:GET /page.html HTTP/1.1。响应却是 HTTP/1.1 404 Not Found,版本号跑到了最前面。

请求行和状态行并排看上去,像是分别由两个小组设计的:一个把版本号写在句尾,一个写在开头。强迫症看了想统一,兼容性看了说:“你先别急。”

不过,这种“文本长相”也不是 HTTP 永远不变的脸。HTTP/2 和 HTTP/3 在线路上改用二进制帧,方法、路径等控制信息则通过伪首部字段表达。语义大体还是那套语义,只是包装换了;抓包时看到的熟悉文本格式,主要属于 HTTP/1.x。互联网不是没改过,只是每次改动都得给旧世界留条路。

Accept 表示客户端能接受哪些媒体类型,Accept-Language 表示能接受哪些语言。一个名字像是少写了半截,另一个倒是把分类写得明明白白。Accept-Type?听起来确实整齐,可惜历史只负责留下名字,不负责售后。

ETag 里的 entity-tag 不是“实体资源”的标签,而是用来区分同一资源不同表示的验证器。HTTP 的术语后来更常用 representation(表示),entity 却还留在字段名和定义里,像老员工的工牌还没来得及换。

还有 Referer,少了一个 r。这不是我拼错,RFC 9110 也特意标注了它是 misspelled。标准都承认拼错了,字段名还是不能改——兼容性:互联网最资深的“将错就错”。

状态码也有类似的补丁史。早期浏览器处理 301、302 时,对 POST 请求到底要不要改成 GET,行为并不一致;后来标准干脆允许这种转换,同时增加 307、308,明确表示重定向时保留原方法。于是,同样是“搬家”,还得分清是可以顺手把请求改了,还是必须原样带过去。标准有时不是在设计新世界,而是在给已经发生的分歧补路牌。

至于 418 I'm a teapot,它来自 1998 年愚人节发布的 RFC 2324:那份关于“网络咖啡壶控制协议”的玩笑规范规定,拿茶壶煮咖啡就该返回 418。这个玩笑后来成了互联网里颇有生命力的彩蛋;RFC 9110 还特意记下,418 因为被部署为玩笑而保留,不能挪作别用。标准文档一本正经地给茶壶留了个位置,还挺有意思的。

参考: