HTTP之实体和编码

1. Content-Length: 实体的大小

Content-Length 首部指示出报文中实体主体的字节大小。这个大小是包含了所有内容编码的,比如,对文本文件进行了 gzip 压缩的话,Content-Length 首部就是压缩后的大小,而不是原始大小。

除非了使用了分块编码,否则 Content-Length 首部就是带有实体主体的报文必须使用的。使用 Content-Length 首部是为了能够检测出服务器崩溃而导致的报文截尾,并对共享持久连接的多个报文进行正确分段。

1.1 检测截尾

客户端通过 Content-Length 来检测报文截尾。

缓存代理服务器通常不会为没有显式 Content-Length 首部的 HTTP 主体做缓存,以此来减小缓存已截尾报文的风险。

1.2 Content-Length 与持久连接

如果响应通过持久连接传送,就可能有另一条 HTTP 响应紧随其后。客户端通过 Content-Length 首部就可以知道报文在何处结束,下一条报文从何处开始。因为连接是持久的,客户端无法依赖连接关闭来判别报文的结束。

有一种情况下,使用持久连接时可以没有 Content-Length 首部,即采用分块编码(chunked encoding)时。在分块编码的情况下,数据是分为一系列的块来发送的,每块都有大小说明。

1.3 内容编码

如果主体进行了内容编码,Content-Length 首部说明的就是编码后(encoded)的主体的字节长度,而不是未编码的原始主体的长度。

2. 实体摘要

服务器使用 Content-MD5 首部发送对实体主体运行 MD5 算法的结果。只有产生响应的原始服务器可以计算并发送 Content-MD5 首部。中间代理和缓存不应当修改或添加这个首部,否则就会与验证端到端完整性的这个最终目的相冲突。Content-MD5 首部是在对内容做了所有需要的内容编码之后,还没有做任何传输编码之前,计算出来的。为了验证报文的完整性,客户端必须先进行传输编码的解码,然后计算所得到的未进行传输编码的实体主体的 MD5。

假若一份文档使用 gzip 算法进行压缩,然后用分块编码发送,那么就对整个经 gzip 压缩的主体进行 MD5 计算。

3. 媒体类型和字符集

Content-Type 首部字段说明了实体主体的 MIME 类型。MIME 类型是标准化的名字,用以说明作为货物运载实体的基本媒体类型。

MIME 类型由一个主媒体类型(比如:text、image 或 audio 等)后面紧跟一条斜线以及一个子类型组成,子类型用于进一步描述媒体类型。

下列是一些 Content-Type 首部中常用的 MIME 类型。

  • text/html:实体主体是 HTML 文档
  • text/plain:实体主体是纯文本文档
  • image/gif:实体主体是 GIF 格式的图像
  • image/jpeg:实体主体是 JPEG 格式的图像
  • audio/x-wav:实体主体包含 WAV 格式的图像
  • model/vrml:实体主体是三维的 VRML 模型
  • application/vnd.ms-powerpoint:实体主体是 Microsoft PowerPoint 演示文档
  • multipart/byterange:实体主体有若干部分,每个部分都包含了完整文档中不同的字节范围
  • message/http:实体主体包含完整的 HTTTP 报文

Content-Type 首部说明的是原始实体主体的媒体类型。例如,如果实体经过内容编码的话,Content-Type 首部说明的仍是编码之前的实体主体的类型。

3.1 文本的字符编码

Content-Type 首部还支持可选的参数来进一步说明内容的类型。如 charset(字符集)参数,它说明把实体中的比特转换为文本文件中的字符的方法:

Content-Type: text/html; charset=iso-8859-4

3.2 多部分媒体类型

MIME 中的 multipart(多部分)电子邮件报文中包含多个报文,它们合在一起作为单一的复杂报文发送。每一部分都是独立的,有各自的描述其内容的集;不同的部分之间用分界字符串连接在一起。

HTTP 也支持多部分主体。不过,通常只用在下列两种情形之一:提交填写好的表格,或是作为承载若干文档片段的范围响应。

3.3 多部分表格提交

当提交填写的 HTTP 表格时,变长的文本字段和上传的对象都作为多部分主体里面独立的部分发送,这样表格中就可以填写各种不同类型和长度的值。

HTTP 使用 Content-Type: multipart/form-data 或 Content-Type: multipart/mixed 这样的首部以及多部分主体来发送这种请求,示例如下:

Content-Type: multipart/form-data; boundary=[abcdefghijklnopqrstuvwxyz]

其中 boundary 参数说明了分割主体中不同部分所用的字符串。

下例展示了 multipart/form-data 编码。假设有如下表格:

若用户在文本输入字段中键入了 Sally,并选择了文本文件 essayfile.txt,用户 Agent 代理可能会发回下面这样的数据:

Contet-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="submit-name"
Sally
--AaB03x
Content-Disposition: form-data; name="files"; filename="essayfile.txt"
Content-Type: text/plain
..contents of essayfile.txt
--AaB03x

3.4 多部分范围响应

HTTP 对范围请求的响应也可以是多部分的。这样的响应中有 Content-Type: multipart/byteranges 首部和带有不同范围的多部分主体。如下示例展示了对文档不同范围的请求产生的响应:

4. 内容编码

HTTP 应用程序有时在发送之前需要对内容进行编码。

4.1 内容编码过程

  1. 网站服务器生成原始响应报文,其中有原始的 Content-Type 和 Content-Length 首部。
  2. 内容编码服务器创建编码后的报文。编码后的报文有同样的 Content-Type 但 Content-Length 可能不同(如主体被压缩了)。内容编码服务器在编码后的报文中增加了 Content-Encoding 首部,这样接收的应用程序就可以进行解码了。
  3. 接收程序得到编码后的报文,进行解码,获得原始报文。

4.2 内容编码类型

HTTP 定义了一些标准的内容编码类型,并允许用扩展编码的形式增添更多的编码。由互联网号码分配机构(IANA)对各种编码进行标准化,它给每个内容编码算法分配了唯一的代号。Content-Encoding 首部就用这些标准化的代号来说明编码时使用的算法。

  • gzip:表明实体采用 GNU zip 编码
  • compress:表明实体采用 Unix 的文件压缩程序
  • deflate:表明实体是用 zlib 的格式压缩的
  • identity:表明没有对实体进行编码。当没有 Content-Encoding 首部时,就默认为这种情况

gzip、compress 以及 deflate 编码都是无损压缩算法,用于减少传输报文的大小,不会导致信息损失。

4.3 Accept-Encoding 首部

Accept-Encoding 字段包含了用逗号分隔的支持编码的列表:

  • Accept-Encoding: compress, gzip
  • Accept-Encoding:
  • Accept-Encoding: *
  • Accept-Encoding: compress;1=0.5, gzip;q=1.0
  • Accept-Encoding: gzip;q=1.0, identity; q=1.0, *;q=0

客户端可以给每种编码附带 Q(质量)值参数来说明编码的优先级。Q 值的范围从 0.0 到 1.0,0.0 说明客户端不想接受所说明的编码,1.0 则表明最希望使用的编码。"*" 表示 "任何其他方法"。

identity 编码代号只能在 Accept-Encoding 首部中出现,客户端用它来说明相对于其他内容编码算法的优先级。

5. 传输编码和分块编码

使用传输编码是为了改变报文中的数据在网络上传输的方式。

5.1 Transfer-Encoding 首部

HTTP 协议中只定义了下面两个首部来描述和控制传输编码:

  • Transfer-Encoding:告知接收方为了可靠地传输报文,已经对其进行了何种编码。
  • TE:用在请求首部中,告知服务器可以使用哪些传输编码扩展。

如下示例,请求使用了 TE 首部来告诉服务器它可以接受分块编码并且愿意接受附在分块编码的报文结尾上的拖鞋:

GET /new_products.html HTTP/1.1
Host: www.joes-hardware.com
User-Agent: Mozilla/4.61 [en] (WinNT; I)
TE: trailers, chunked
...

对它的响应中包含 Transfer-Encoding 首部,用该告诉接收方已经用分块编码对报文进行了传输编码:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Server: Apache/3.0
...

5.2 分块编码

分块编码把报文分割为若干个大小已知的块。块之间是紧挨着发送的,这样就不需要在发送之前知道整个报文的大小了。

注:分块编码是一种传输编码,因此是报文的属性,而不是主体的属性。多部分编码就是主体的属性,它和分块编码是完全独立的。

1. 分块与持久连接

当使用持久连接时,在服务器写主体之前,必须知道它的大小并在 Content-Length 首部中发送。如果服务器动态创建内容,就可能在发送之前无法知道主体的长度。

分块编码解决了这种问题,只要允许服务器把主体逐块发送,说明每块的大小就可以了。

分块编码由起始的 HTTP 响应首部块开始,随后是一系列分块。每个分块包含一个长度值和该分块的数据。长度值是十六进制形式并将 CRLF 与数据分隔开。分块中数据的大小以字节计算,不包括长度值与数据之间的 CRLF 序列以及分块结尾的 CRLF 序列。最后一个块的长度值为 0,表示"主体结束"。

2. 分块报文的拖挂

若客户端的 TE 首部中说明它可以接受拖挂的话,就可以在分块的报文最后加上拖挂。产生原始响应的服务器也可以在分块的报文最后加上拖挂。拖挂的内容是可选的元数据,客户端不一定需要理解和使用(客户端可以忽略并丢弃拖挂中的内容)。

拖挂中可以包含附带的首部字段,它们的值在报文开始的时候可能是无法确定的(如,必须要先生成主体的内容)。Content-MD5 首部就是一个可以在拖挂中发送的首部,因为在文档生成之前,很难算出它的 MD5。

除了 Transfer-Encoding、Trailer 以及 Content-Length 首部之外,其他 HTTP 首部都可以作为拖挂发送。

6. 范围请求

HTTP 允许客户端只请求文档的一部分,或者说某个范围。服务器可以通过在响应中包含 Accept-Rranges 首部的形式向客户端说明可以接受的范围请求。这个首部的值是计算范围的单位,通常是以字节计算的:

HTTP/1.1 200 OK
Date: Fri, 05 Nov 1999 22:35:15 GMT
Server: Apache/1.2.4
Accept-Ranges: bytes
...

7. 差异编码

差异编码是 HTTP 协议的一个扩展,它通过交换对象改变的部分而不是完整的对象来优化传输性能。

客户端在 If-None-Match 首部中使用的是它所持有页面版本的唯一标识,这个标识是服务器之前响应客户端时在 ETag 首部中发送的。

差异编码所用的首部:

  • ETag:文档每个实例的唯一标识符。由服务器在响应中发送;客户端在后继请求的 If-Match 首部和 If-None-Match 首部中可以使用它
  • If-None-Match:客户端发送的请求首部,当且仅当客户端的文档版本与服务器不同时,才行服务器请求该文档
  • A-IM:客户端请求首部,说明可以接受的实例操控类型
  • IM:服务器响应首部,说明作用在响应上的实例操控的类型。当响应代码是 226 IM Used 时,会发送这个首部
  • Delta-Base:服务器响应首部,说明用于计算差异的基线文档的 ETag 值(应当与客户端请求中的 If-None-Match 首部里的 ETag 相同)

7.1 实例操控、差异生成器和差异应用器

客户端可以使用 A-IM 首部说明可以接受的一些实例操控的类型。服务器在 IM 首部中说明使用的是何种实例操控。

在 IANA 注册的实例操控类型:

  • vcdiff:用 vcdiff 算法计算差异
  • diffe:用 Unix 系统的 diff-e 命令计算差异
  • gdiff:用 gdiff 算法计算差异
  • gzip:用 gzip 算法压缩
  • deflate:用 deflate 算法压缩
  • range:用在服务器的响应中,说明响应是针对范围选择得到的部分内容
  • identity:用在客户端请求中的 A-IM 首部中,说明客户端愿意接受恒等实例操控
posted @ 2018-07-12 00:22  季末的天堂  阅读(419)  评论(0编辑  收藏  举报