📘 第六章 HTTP首部
📄 一句话概括

TE 说的是「我接受哪些传输编码」,最常见值是 trailers。 它和 Accept-Encoding 长得很像,但一个管传输、一个管内容,而且 TE 是逐跳的。

📝 本节和前面几节的关系

上一节是来源,这一节是传输协商。它和 6.4.3 那个字段最容易混,这一节专门分清。

一、两件事,两个字段

对比项 Accept-Encoding TE
管什么 内容编码(gzip 压缩正文) 传输编码(分块、尾块)
作用范围 端到端,一路传到最终接收者 逐跳,只对相邻两跳有效
常见值 gzip、br、deflate trailers
响应侧对应 Content-Encoding Transfer-Encoding
记住一句话:压缩是内容的事,切块是传输的事。
前者改的是正文的字节,后者改的是这些字节怎么被切开送出。
所以 TE 属于 6.2.6 那八个固定的逐跳字段之一,代理处理完必须删掉,不能往下传。

二、trailers 这个值的意义

TE: trailers 表示「我读得懂尾块」。服务端据此决定要不要用尾块发信息。 这就把 6.3.5 的 Trailer 和这里串起来了:服务端要不要发尾块,得先看客户端发没发这个 TE。 Demo 里三组:不发 TE 时响应 106 字节、没有 Trailer 首部; 发 TE: trailers 时响应 156 字节、有 Trailer 首部。多出来的 50 字节就是分块开销加那两个尾块。 带 q 值也认:TE: gzip, trailers;q=0.5 里 q 是 0.5,仍然大于 0,所以尾块照发。 要是写 trailers;q=0,就该当成拒绝。

三、为什么它能管传输编码

理论上 TE 还能协商别的传输编码,比如 TE: gzip 表示「你可以先 gzip 再发给我, 但这是在传输层做的,不是内容层」。 但这条几乎没人用。因为传输编码本来是给中间节点用的——代理为了在慢链路上省带宽,可以在转发时压一下。 客户端主动要求这个,收益不大还容易出错。 所以现实里 TE 就等同于 TE: trailers。其他取值都是纸面上的可能性。

四、动手 Demo:三组 TE,看服务端发不发尾块

服务端检查 TE 里有没有 trailers。有就用分块加尾块发,没有就发普通响应。 客户端对比两者的字节数和首部。

te = hd.get(u'te', u'')
if u'trailers' in te.lower():
    parts = [u'HTTP/1.1 200 OK',
             u'Trailer: X-Sum',
             u'Transfer-Encoding: chunked',
             u'Connection: close', u'', u'']
    out = bytearray(u'\r\n'.join(parts).encode(u'utf-8'))
    for seg in (b'aaa\n', b'bbb\n'):
        out += (u'%x\r\n' % len(seg)).encode() + seg + b'\r\n'
    out += b'0\r\nX-Sum: 8\r\n\r\n'
    return bytes(out)

实跑输出:

--------------------------------------------------------------------
TE:我能接受哪些传输编码(逐跳)
--------------------------------------------------------------------
  【不发 TE】
    TE                       (没发)
    服务端做法               普通响应,不带尾块
    响应有没有 Trailer 首部  没有
    响应字节数               106

  【TE: trailers】
    TE                       trailers
    服务端做法               带尾块
    响应有没有 Trailer 首部  有
    响应字节数               156

  【TE: gzip, trailers;q=0.5】
    TE                       gzip, trailers;q=0.5
    服务端做法               带尾块
    响应有没有 Trailer 首部  有
    响应字节数               156

  `TE` 和 `Accept-Encoding` 长得很像,但管的是两件事:
  **`Accept-Encoding` 管内容编码**(gzip 压缩正文),是端到端的;
  **`TE` 管传输编码**(分块、以及能不能接受尾块),是逐跳的。
  所以 `TE` 属于 6.2.6 那八个固定的逐跳字段之一,代理必须处理掉。

  它最常见的值就是 `trailers`:告诉服务端「我读得懂尾块」。
  服务端据此决定要不要用尾块发校验和——
  也就是说 6.3.5 那个 Trailer 要不要出现,得先看客户端发没发这个 TE。

不发 TE:普通响应,106 字节,「响应有没有 Trailer 首部」是「没有」。 发 TE: trailers:156 字节,有 Trailer 首部,正文用分块传输。 发 TE: gzip, trailers;q=0.5:同样 156 字节,同样带尾块。q=0.5 仍然表示接受。 多出来的 50 字节是分块开销加尾块。 所以尾块不是免费的——只有确实需要「发完才知道」的信息时,才值得用它。

30 秒实验

  1. 发 TE: trailers;q=0,看服务端该不该发尾块。
  2. 发 TE: gzip,看服务端会不会把正文 gzip 后放传输层(本 Demo 不会)。
  3. 想一想:为什么 TE 是逐跳的,而 Accept-Encoding 是端到端的?

常见误区

  1. 「TE 和 Accept-Encoding 是一回事」——一个管传输编码,一个管内容编码。
  2. 「TE 是端到端的」——是逐跳的,代理必须处理掉。
  3. 「TE 能协商任意压缩算法」——理论上可以,实际只用 trailers。
自测三问(点开对答案) 1. `TE` 和 `Accept-Encoding` 的分工是什么? —— `Accept-Encoding` 管内容编码(gzip 正文),`TE` 管传输编码(分块、尾块)。 2. `TE: trailers` 是什么意思? —— 我读得懂尾块,你可以用尾块给我发信息。 3. 为什么 `TE` 是逐跳字段? —— 传输编码只对相邻两跳之间的这一条连接有意义,换一条连接就失效了。

19 个请求首部只剩最后一个:客户端的自我介绍。

相关笔记