HTTP 实体与编码

文章同步于 Github blog

天天都有数以亿计的各类媒体对象经由 HTTP 传送,如图像、文本、影片以及软件程序等。HTTP 要确保它所承载
的“货物”满 足如下条件:html

  • 能够被正确地识别(经过Content-Type首部说明媒体格式,Content- Language 首部说明语言),以便浏览器和其余客户端能正确处理内容。
  • 能够被正确地解包(经过Content-Length首部和Content-Encoding首部)。
  • 是最新的(经过实体验证码和缓存过时控制)
  • 符合用户的须要(基于Accept系列的内容协商首部)
  • 在网络上能够快速有效地传输(经过范围请求、差别编码以及其余数据压缩方法)
  • 完整到达、未被篡改(经过传输编码首部和Content-MD5校验和首部)

HTTP 实体首部

HTTP 实体首部描述了 HTTP 报文的内容。HTTP/1.1 版定义了如下 10 个基本字体首部字段。git

  • Content-Type:实体中所承载对象的类型。
  • Content-Length: 所传送实体主体的长度或大小。
  • Content-Language: 与所传送对象最相配的人类语言。
  • Content-Encoding: 对象数据所作的任意变换(好比,压缩)。
  • Content-Location: 一个备用位置,请求时可经过它得到对象。
  • Content-Range: 若是这是部分实体,这个首部说明它是总体的哪一个部分。
  • Content-MD5: 实体主体内容的校验和。
  • Last-Modified: 所传输内容在服务器上建立或最后修改的日期时间。
  • Expires: 实体数据将要失效的日期时间。
  • Allow: 该资源所容许的各类请求方法,例如,GET 和 HEAD。
  • ETag: 这份文档特定实例的惟一验证码。ETag 首部没有正式定义为实体首部,但它对许多涉及实体的操做来讲,都是一个重要的首部。
  • Cache-Control: 指出应该如何缓存该文档。和 ETag 首部相似,Cache-Control 首部也没有正 式定义为实体首部。

实体的大小(Content-Length)

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

Content-Length 首部指示出报文中实体主体的字节大小。这个大小是包含了全部内容编码的。若是主体进行了内容编码,Content-Length 首部说明的就是 编码后(encoded) 的主体的字节长度,而不是未编码的原始主体的长度。github

没有 Content-Length 的话,客户端没法区分究竟是报文结束时正常的链接关闭,仍是报文传输中因为服务器崩溃而致使的链接关闭。客户端须要经过 Content-Length 来检测 报文截尾算法

Content-Length 首部对于持久链接是必不可少的。若是响应经过持久链接传送, 就可能有另外一条 HTTP 响应紧随其后。客户端经过 Content-Length 首部就能够知道报文在何处结束,下一条报文从何处开始。由于链接是持久的,客户端没法依赖 链接关闭来判别报文的结束。浏览器

有一种状况下,使用持久链接时能够没有 Content-Length 首部,即采用 分块编码(chunked encoding)时。在分块编码的状况下,数据是分为一系列的块来发送的,每块都有大小说明。哪怕服务器在生成首部的时候不知道整个实体的大小(一般是由于实体是动态生成的),仍然可使用分块编码传输若干已知大小的块。

实体摘要(Content-MD5)

服务器使用 Content-MD5 首部发送对实体主体运行 MD5 算法的结果。只有产生响应的原始服务器能够计算并发送 Content-MD5 首部。若是一份文档使用 gzip 算法进 行压缩,而后用分块编码发送,那么就对整个经 gzip 压缩的主体进行 MD5 计算。缓存

媒体类型(MIME Type)和字符集

Content-Type 首部字段说明了实体主体的 MIME 类型。6MIME 类型是标准化的 名字,用以说明做为货物运载实体的基本媒体类型(好比:HTML 文件、Microsoft Word 文档或是 MPEG 视频等)。客户端应用程序使用 MIME 类型来解释和处理其内容服务器

Content-Type 首部说明的是原始实体主体的媒体类型。例如, 若是实体通过内容编码的话,Content-Type 首部说明的还是编码以前的实体主体的类型。

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

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

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

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

内容编码(Content-Encoding)

内容编码,是对报文的主体进行的可逆变换。内容编码是和内容的具体格式细节紧密相关的。例如,你可能会用 gzip 压缩文本文件,但不是 JPEG 文 件,由于 JPEG 这类东西用 gzip 压缩的不够好。

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

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

Accept-Encoding首部

毫无疑问,咱们不但愿服务器用客户端没法解码的方式来对内容进行编码。为了不服务器使用客户端不支持的编码方式,客户端就把本身支持的内容编码方式列表放在请求的 Accept-Encoding 首部里发出去。

若是 HTTP 请求中没有包含 Accept-Encoding 首部,服务器就能够假设客户端可以接受任何编码方式(等价 于发送 Accept-Encoding: *)。

客户端能够给每种编码附带 Q(质量)值参数来讲明编码的优先级。Q 值的范围从0.0 到 1.0,0.0 说明客户端不想接受所说明的编码,1.0 则代表最但愿使用的编码。 “*”表示“任何其余方法”。决定在响应中回送什么内容给客户端是个更通用的过程,而选择使用何种内容编码则是此过程的一部分。

compress;q=0.5, gzip;q=1.0 gzip;q=1.0, identity; q=0.5, *;q=0

传输编码(Transfer Codings)

传输编码

传输编码也是做用在实体主体上的可逆变换,但使用它们是因为架构方面的缘由,同内容的格式无关。使用传输编码是为了改变 报文中的数据在网络上传输的方式。

Transfer-Encoding首部

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

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

下面的例子中,请求使用了 TE 首部来告诉服务器它能够接受分块编码(若是是 HTTP/1.1 应用程序的话,这就是必须的)而且愿意接受附在分块编码的报文结尾上 的拖挂:

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
...

在这个起始首部以后,报文的结构就将发生改变。

传输编码的值都是大小写无关的。HTTP/1.1 规定在 TE 首部和 Transfer-Encoding 首部中使用传输编码值。最新的 HTTP 规范只定义了一种传输编码,就是分块编码

分块编码

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

要注意的是,分块编码是一种传输编码,所以是报文的属性,而不是主体的属性

分块与持久链接

若客户端和服务器之间不是持久链接,客户端就不须要知道它正在读取的主体的长度,而只须要读到服务器关闭主体链接为止。

当使用持久链接时,在服务器写主体以前,必须知道它的大小并在 Content- Length 首部中发送。若是服务器动态建立内容,就可能在发送以前没法知道主体的长度。

分块编码为这种困难提供了解决方案,只要容许服务器把主体逐块发送,说明每块的大小就能够了。由于主体是动态建立的,服务器能够缓冲它的一部分,发送其大小和相应的块,而后在主体发送完以前重复这个过程。服务器能够用大小为 0 的块做为主体结束的信号,这样就能够继续保持链接,为下一个响应作准备。

客户端也能够发送分块的数据给服务器。由于客户端事先不知道服务器是否接受分块编码(这是由于服务器不会在给客户端的响应中发送 TE 首部),因此客户端必须作好服务器用411 Length Required(须要Content-Length首部)响应来拒绝分块请求的准备。

分块报文的拖挂

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

拖挂中能够包含附带的首部字段,它们的值在报文开始的时候多是没法肯定的 (例如,必需要先生成主体的内容)。Content-MD5 首部就是一个能够在拖挂中发送的首部,由于在文档生成以前,很难算出它的 MD5。

除了 Transfer-Encoding、Trailer 以及 Content-Length 首部以外,其余 HTTP 首部均可以做为拖挂发送。

验证码和新鲜度

当文档在客户端“过时”以后(也就是说,客户端再也不认为该副本有效),客户端必须从服务器请求一份新的副本。不过,若是该文档在服务器上并未发生改变,客户 端也就不须要再接收一次了——继续使用缓存的副本便可。

这种特殊的请求,称为 条件请求(conditional request),要求客户端使用 验证码 (validator)
来告知服务器它当前拥有的版本号,并仅当它的当前副本再也不有效时才要求发送新的副本。

新鲜度

服务器应当告知客户端可以将内容缓存多长时间,在这个时间以内就是新鲜的。 服务器能够用这两个首部之一来提供这种信息: Expires(过时)Cache- Control(缓存控制)

客户端和服务器为了能正确使用 Expires 首部,它们的时钟必须同步。

条件请求(Conditional Requests)

HTTP 为客户端提供了一种方法,仅当资源改变时才请求副本, 这种特殊请求称为有条件的请求。有条件的请求是标准的 HTTP 请求报文,但仅当某个特定条件为真时才执行。

例如,某个缓存服务器可能发送下面的有条件 GET 报文给服务器,仅当文件 /announce.html 从 2002 年 6 月 29 日(这是缓存的文档最后 被做者修改的时间)以后发生改变的状况下才发送它:

GET /announce.html HTTP/1.0
If-Modified-Since: Sat, 29 Jun 2002, 14:30:00 GMT

有条件的请求是经过以“If-”开头的有条件的首部来实现的。

验证码

每一个有条件的请求都经过特定的验证码来发挥做用。验证码是文档实例的一个特殊属性,用它来测试条件是否为真。从概念上说,你能够把验证码看做文件的序列号、 版本号,或者最后发生改变的日期时间。

HTTP把验证码分为两类弱验证码(weak validators)强验证码(strong validators)。 弱验证码不必定能惟一标识资源的一个实例,而强验证码必须如此。

最后修改时间(If-Modified-Since)被看成弱验证码,由于尽管它说明了资源最后被修改的时间,但它的描述精度最大就是 1 秒

ETag 首部被看成强验证码,由于每当资源内容改变时,服务器均可以在 ETag 首部放置不一样的值。

有时候,客户端和服务器可能须要采用不那么精确的实体标记验证方法。例如,某服务器可能想对一个很大、被普遍缓存的文档进行一些美化修饰,但不想在缓存服务器再验证时产生很大的传输流量。在这种状况下,该服务器能够在标记前面加上“W/”前缀来广播一个“弱”实体标记。

范围请求(Range Requests)

关于客户端如何要求服务器只在资源的客户端副本再也不有效的状况下才发送其副本, 咱们已经清楚地理解了。HTTP 还进一步锦上添花:它容许客户端实际上只请求文 档的一部分,或者说某个范围

有了范围请求,HTTP 客户端能够经过请求曾获取失败的实体的一个范围(或者说一部分),来恢复下载该实体。固然这有一个前提,那就是从客户端上一次请求该实 体到此次发出范围请求的时段内,该对象没有改变过。例如:

GET /bigfile.html HTTP/1.1
Host: www.joes-hardware.com
Range: bytes=4000-
User-Agent: Mozilla/4.61 [en] (WinNT; I) ...

在本例中,客户端请求的是文档开头 4000 字节以后的部分(没必要给出结尾字节数, 由于请求方可能不知道文档的大小)。在客户端收到了开头的 4000 字节以后就失败的状况下,可使用这种形式的范围请求。

并非全部服务器都接受范围请求,但不少服务器能够。服务器能够经过在响应中包含 Accept-Ranges 首部的形式向客户端说明能够接受的范围请求。这个首部的值是计算范围的单位,一般是以字节计算的。例如:

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

涉及范围请求的一系列 HTTP 事务的例子:
image

Range 首部在流行的 点对点(Peer-to-Peer,P2P) 文件共享客户端软件中获得普遍应用,它们从不一样的对等实体同时下载多媒体文件的不一样部分。

差别编码(Delta encoding)

若是客户端有一个页面的已过时副本,就要请求页面的最新实例。若改变的地方比较少,与其发送完整的新页面给客户端,客户端更愿意服务器只发送页面发生改变的部分,这样就能够更快地获得最新的页面。

差别编码是 HTTP 协议的一个扩展,它经过交换对象改变的部分而不是完整的对象来优化传输性能。差别编码也是一类实例操控,由于它依赖客户端和服务器之间针对特定的对象实例来交换信息。RFC 3229 描述了差别编码。

若是客户端想告诉服务器它愿意接受该页面的差别,只要发送 A-IM 首部 就能够了。A-IM 是 Accept-Instance-Manipulation(接受实例操控) 的缩写。

在 A-IM 首部中,客户端会说明它知道哪些算法能够把差别应用于老版本而获得最新版本。

服务端发送回下面这些内容:一个特殊的响应代码—— 226 IM Used,告知客户端它正在发送的是所请求对象的实例操控,而不是那个完整的对象自身;一个 IM(Instance-Manipulation 的缩写) 首部,说明用于计算差别的算法;新的 ETag 首部Delta-Base 首部,说明用于计算差别的基线文档的 ETag。

image

服务器侧的“差别生成器”根据基线文档和该文档的最新实例,用客户端在 A-IM 首部中指明的算法计算它们之间的差别。客户端侧的“差别应用器” 获得差别,将其应用于基线文档,获得文档的最新实例。例如,若是产生差别的算 法是 Unix 系统的 diff-e 命令,客户端就能够用 Unix 系统中的文本编辑器 ed 提供的 功能来应用差别。

差别编码所用的首部

ETag

文档每一个实例的惟一标识符。由服务器在响应中发送;客户端在后继请求的 If-Match 首部和 If-None-Match 首部中可使用它

If-None-Match

客户端发送的请求首部,当且仅当客户端的文档版本与服务器不一样时,才向服务器请求该文档

A-IM

客户端请求首部,说明能够接受的实例操控类型

IM

服务器响应首部,说明做用在响应上的实例操控的类型。当响应代码是226 IM Used 时,会发送这个首部

Delta-Base

服务器响应首部,说明用于计算差别的基线文档的 ETag 值(应当与客户端请求
中的 If-None-Match 首部里的 ETag 相同)

参考

相关文章
相关标签/搜索