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

书里的「实体」指内容本身,「报文主体」指经过内容编码后实际传输的字节。 两者在压缩时不相等——这个术语已被 2014 年的新规范删掉。

📝 本节和前面几节的关系

上一节拆了报文四段。 这一节专门掰清楚「主体」这个词的两种含义,因为后面两节(内容编码、 分块传输)都建立在这个区分上。

一、书里的定义

按 RFC 2616 的说法:实体(entity)= 实体首部 + 实体主体,而报文主体是「实际传输的那段字节」。 不压缩时两者相等; 一旦用了 Content-Encoding: gzip,报文主体是压缩后的字节, 实体主体是解压后的内容——两者就差了一整个压缩比。 为什么要区分?因为校验、范围请求、内容协商全都针对「实体」算,而长度、分块却针对「报文主体」算。 混了就会算错。

二、实测:两个 MD5 不一样

Demo 里 2320 字节的内容压成 104 字节。 原始内容的 MD5 是 2db80300a9190fba0201f8aef3a2e027, 传输字节的 MD5 是 ddc8f7179478ba4c557f2c40105f9740。 两个 md5 完全不同。 如果客户端拿传输过来的字节直接算 MD5 去比对,永远对不上——必须先按 Content-Encoding 解码, 再校验。 这个顺序在 Content-MD5 这种首部上尤其重要(书里 6.6.6 节提到了它, 但没说清它算的是哪一层——答案是编码后的字节)。

三、2014 年之后这组词已经废了

RFC 7230 把 entity 整个概念删了,只留下 message body 和 payload。 现在说「实体」属于老派说法,工程里没人用。 所以读这本书要做一个转换:书里的「实体」大致等于现在说的「解码后的载荷」; 书里的「报文主体」就是「实际传的字节」。 这不是这本书的错——它写于 2013 年,引的是当时的规范。 但用今天的话跟人对齐时,最好直接说「压缩前/压缩后」,歧义最小。

四、动手 Demo:同一份内容,压缩前后两个 MD5

拿 2320 字节的 HTML 压一次,分别算原始字节与压缩字节的 MD5,看它们是不是同一份东西。

raw = BIG
gz = gzip.compress(raw, 9)
print(u'原始', len(raw), hashlib.md5(raw).hexdigest())
print(u'压缩后', len(gz), hashlib.md5(gz).hexdigest())
print(u'报文主体', len(gz), u'(=编码后的实体)')

实跑输出:

--------------------------------------------------------------------
报文主体不等于实体主体
--------------------------------------------------------------------
  实体(原始内容)           2320 字节
    它的 MD5                 2db80300a9190fba0201f8aef3a2e027
  内容编码之后               104 字节(4%)
    传输字节的 MD5           778de6ea092d5fa7856a7f3fe10513b8
  报文主体                   104 字节(=编码后的实体)

  两者相等吗                 否,差 2216 字节
  《图解HTTP》的说法         实体=报文主体经内容编码后剩下的部分
  RFC 7230 之后的说法        报文主体直接就是载荷;实体这个词已从规范里删掉

  这就是第三章最容易混的一对词。**书用的是 1999 年的 RFC 2616 术语**,
  2014 年 RFC 7230 把 entity 整个概念删了,只留 message body。
  实际工程里没人区分这两个词;要看的是 Content-Encoding 到底加没加。
  Demo 里两个 MD5 不同,说明客户端**必须先用 Content-Encoding 解码**,
  才能拿到能校验的原始内容——顺序搞反,校验永远失败。

实体(原始内容)2320 字节 → 内容编码之后 104 字节(4%)。 这个压缩比特别高,是因为样本里同一句话重复了四十遍——重复度越高,gzip 越猛, 真实页面一般压到两三成。 两者相等吗 否,差 2216 字节——这就是书里那个区分存在的全部理由。 顺手记一个易错点:Content-Length 报的是压缩后的字节数(104),不是 2320。 报错这个数,客户端会一直等剩下的 2216 字节。

30 秒实验

  1. 把 gzip.compress 换成 zlib.compress,看两个 MD5 变不变。
  2. 先算原始内容的 MD5 再 gzip 传,客户端解码后重算——这就是 Content-MD5 的正确用法。
  3. 想一想:范围请求的 Range 是在压缩前还是压缩后的字节上算?

常见误区

  1. 「报文主体就是正文」——它是「传输形态的正文」,压缩时不等于内容本身。
  2. 「Content-Length 是内容长度」——是传输字节数,压缩后算。
  3. 「实体这个词现在还在用」——2014 年已被规范删除,只是老书还在用。
自测三问(点开对答案) 1. 报文主体与实体主体的区别? —— 报文主体是实际传输的字节;实体主体是解码后的内容。压缩时两者不等。 2. `Content-Length` 报的是哪一个? —— 报文主体的长度,也就是压缩后的字节数。 3. 为什么校验必须先解码? —— 因为校验和通常是对原始内容算的,拿压缩字节去算必然对不上。

概念分清了。下一节看最常用的一种内容编码:压缩。

相关笔记