HTTP 请求实体和编码

Gao
## 实体和编码 报文是箱子,实体是货物 - Example ```http http/1.0 200 ok server: GWS Content-Type: text/plain Content-length: 18 Hi! I'm a message! ``` 从 Content-type 开始到结束都是实体, header 部分是实体首部, body 部分是实体主体 HTTP 实体首部描述了 HTTP 报文的内容。 HTTP/1.1 版定义了以下 10 个基本字体首部字 段。 - Content-Type 实体中所承载对象的类型。 - Content-Length 所传送实体主体的长度或大小。 - Content-Language 与所传送对象最相配的人类语言。 - Content-Encoding 对象数据所做的任意变换(比如,压缩)。 - Content-Location 一个备用位置,请求时可通过它获得对象。 - Content-Range 如果这是部分实体,这个首部说明它是整体的哪个部分。 - Content-MD5 实体主体内容的校验和。 - Last-Modified 所传输内容在服务器上创建或最后修改的日期时间。 - Expires 实体数据将要失效的日期时间。 - Allow 该资源所允许的各种请求方法,例如,GET 和 HEAD。 - ETag 这份文档特定实例(参见 15.7 节)的唯一验证码。ETag 首部没有正式定义为实 体 首部,但它对许多涉及实体的操作来说,都是一个重要的首部。 - Cache-Control 指出应该如何缓存该文档。和 ETag 首部类似,Cache-Control 首部也没 有正 式定义为实体首部。 ### 确定实体主体长度的规则 1. 如果特定的 HTTP 报文类型中不允许带有主体,就忽略 Content-Length 首 部,它是对 (没有实际发送出来的)主体进行计算的。这种情况下,Content- Length 首部是提示性 的,并不说明实际的主体长度。(考虑不周的 HTTP 应用 程序会认为有了 Content-Length 就有主体存在,这样就会出问题。) 最重要的例子就是 HEAD 响应。HEAD 方法请求服务器发送等价的 GET 请 求中会出现的首 部,但不要包括主体。因为对 GET 的响应会带有 Content- Length 首部,所以 HEAD 响应 里面也有;但和 GET 响应不同的是,HEAD 响应中不会有主体。1XX、204 以及 304 响应也 可以有提示性的 Content- Length 首部,但是也都没有实体主体。那些规定不能带有实体 主体的报文, 不管带有什么首部字段,都必须在首部之后的第一个空行终止。 2. 如果报文中含有描述传输编码的 Transfer-Encoding 首部(不采用默认的 HTTP“恒等”编 码),那实体就应由一个称为“零字节块”(zero-byte chunk) 的特殊模式结束,除非报文 已经因连接关闭而结束。我们将在本章后面讨论传 输编码和分块编码。 3. 如果报文中含有 Content-Length 首部(并且报文类型允许有实体主体),而且没有非恒 等的 Transfer-Encoding 首部字段,那么 Content-Length 的值就是主体的长度。如果 收到的报文中既有 Content-Length 首部字段又有非恒等的 Transfer-Encoding 首部字 段,那就必须忽略 Content-Length,因为 传输编码会改变实体主体的表示和传输方式( 因此可能就会改变传输的字节数 4. 如果报文使用了 multipart/byteranges(多部分 / 字节范围)媒体类型,并且没有用 Content-Length 首部指出实体主体的长度,那么多部分报文中的每个部 分都要说明它 自己的大小。这种多部分类型是唯一的一种自定界的实体主体类 型,因此除非发送方知 道接收方可以解析它,否则就不能发送这种媒体类型。 - 因为 Range 首部可能会被不理解多部分 / 字节范围的更原始的代理所转发,所以如果发 送方不能确 定接收方是否理解这种自定界的格式的话,就必须用本节的方法(1)、(3)或 (5)来对报文定界。 5. 如果上面的规则都不匹配,实体就在连接关闭的时候结束。实际上,只有服务 器可以使 用连接关闭来指示报文的结束。客户端不能用关闭连接来指示客户端 - 客户端可以使用半关闭,也就是只把连接的输出端关闭,但很多服务器应用程序设计的时 候没有考虑 到处理这种情况,会把半关闭当作客户端要从服务器断开连接来处理。HTTP 没有对连接管理进行良 好的规范。详情请参见第 4 章。 ### 媒体类型和字符集 Content-Type 首部字段说明了实体主体的 MIME 类型。 MIME 类型是标准化的名字,用以 说明作为货物运载实体的基本媒体类型。客户端应用程序使用 MIME 类型来解释和处理其 内容。 Content-Type 的值是标准化的 MIME 类型,都在互联网号码分配机构(Internet Assigned Numbers Authority,简称 IANA)中注册。 MIME 类型由一个主媒体类 型(比如 :text、image 或 audio 等)后面跟一条斜线以及一个子类型组成,子类 型用于进一步描述 媒体类型。 ### 内容编码 ![](http-entities-encoding/content-encoding.jpg) #### 内容编码类型 HTTP 定义了一些标准的内容编码类型,并允许用扩展编码的形式增添更多的编码。由互联 网号码分配机构(IANA)对各种编码进行标准化,它给每个内容编码算法分 配了唯一的代号 。 - gzip RFC1952 gzip 编码 - compress Unix 文件压缩 - deflate RFC1950 和 1951 讲解 zlib 和 deflate - identity 没有编码 #### Accept-Encoding 为了避免服务器使用客户端不支持的编码方式,客户端就把自己支持的内容编码方式列表放 在请求的 Accept-Encoding 首部里发出去。如果 HTTP 请求中没有包含 Accept-Encoding 首部,服务器就可以假设客户端能够接受任何编码方式(等价 于发送 Accept-Encoding: \*)。 Example: ```http Accept-Encoding: compress, gzip Accept-Encoding: Accept-Encoding: * Accept-Encoding: compress;q=0.5, gzip;q=1.0 Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0 ``` Q 值的范围从 0.0 到 1.0,0.0 说明客户端不想接受所说明的编码,1.0 则表明最希望使 用的编码。 “\*”表示“任何其他方法”。 ### 传输编码和分块编码 传输编码也是作用在实体主体上的可逆变换,但使用它们是由 于架构方面的原因,同内容 的格式无关。 ![](http-entities-encoding/transfer-encoding.png) #### 可靠传输 - 未知尺寸如果不先生成内容,某些网关应用程序和内容编码器就无法确定报文主体的最终 大小。通常,这些服务器希望在知道大小之前就开始传输数据。因为 HTTP 协议 354 要 求 Content-Length 首部必须在数据之前,有些服务器就使用传输编码来发送数据,并用 特别的结束脚注表明数据结束。 - 安全性你可以用传输编码来把报文内容扰乱,然后在共享的传输网络上发送。 #### Transfer-Encoding Headers - Transfer-Encoding 告知接收方为了可靠地传输报文,已经对其进行了何种编码。 - TE 用在请求首部中,告知服务器可以使用哪些传输编码扩展。 请求使用了 TE 首部来告诉服务器它可以接受分块编码(如果是 HTTP/1.1 应用程序的话, 这就是必须的)并且愿意接受附在分块编码的报文结尾上的拖挂: ```http GET /new_products.html HTTP/1.1 Host: www.gsmiot.com User-Agent: Mozilla/4.61 [en] (WinNT; I) TE: trailers, chunked ... ``` #### 分块编码 分块编码把报文分割为若干个大小已知的块。块之间是紧挨着发送的,这样就不需要在发送 之前知道整个报文的大小了。 ![](http-entities-encoding/chunked.png) ### 随时间变化的实例 HTTP 协议规定了称为实例操控(instance manipulations)的一系列请求和响应操 作,用以 操控对象的实例。两个主要的实例操控方法是范围请求和差异编码。这两 种方法都要求客 户端能够标识它所拥有(如果有的话)的资源的特定副本,并在一定的条件下请求新的实例。 ### 验证码和新鲜度 服务器应当告知客户端能够将内容缓存多长时间,在这个时间之内就是新鲜的。服务器可以 用这两个首部之一来提供这种信息: - Expires(过期) - Cache- Control(缓存控制) #### 有条件的请求与验证码 | 请求类型 | 验证码 | 描述 | | ------------------- | ------------- | ---------------------------------------------------------------------------------------- | | If-Modified-Since | Last-Modified | 如果在前一条响应的 Last-Modified 首部中说明的 时间之后,资源的版本发生变化,就发送其副本 | | If-Unmodified-Since | Last-Modified | 仅在前一条响应的 Last-Modified 首部中说明的时 间之后,资源的版本没有变化,才发送其副本 | | If-Match | ETag | 如果实体的标记与前一次响应首部中的 ETag 相同, 就发送该资源的副本 | | If-None-Match | ETag | 如果实体的标记与前一次响应首部中的 ETag 不同, 就发送该资源的副本 | ### 范围请求 有了范围请求,HTTP 客户端可以通过请求曾获取失败的实体的一个范围(或者说 一部分), 来恢复下载该实体。 Example: ```http GET /bigfile.html HTTP/1.1 Host: www.gsmiot.com Range: bytes=4000- User-Agent: Mozilla/4.61 [en] (WinNT; I) ... ``` 对于客户端在一个请求内请求多个不同范围的情况,返回的响应也是单个实体,它有一个多 部分主体及 Content-Type: multipart/byteranges 首部。 服务器可以通过在响应中包含 Accept-Ranges 首部的形式向客户端说明可以接受的范围请 求。这个首部的值是计算范围的单位,通常是以字节计算的。 ### 差异编码 差异编码也是一类实例操控,因为它依赖客户端和服务器之间针对特定的对象实例来交换信 息。RFC 3229 描述了差异编码。 如果客户端想告诉服务器它愿意接受该页面的差异,只要发送 A-IM 首部就可以了。 A-IM 是 Accept-Instance-Manipulation(接受实例操控)的缩写。形象比喻的话,客户端相当于 这样说:“哦对了,我能接受某些形式的实例操控,如果你会其中一种的话,就不用发送完整 的文档给我了。” 在 A-IM 首部中,客户端会说明它知道哪些算法可以把差异应用于老版本 而得到最新版本。服务端发送回下面这些内容: 一个特殊的响应代码——226 IM Used,告知 客户端它正在发送的是所请求对象的实例操控,而不是那个完整的对象自身; 一个 IM(Instance-Manipulation 的缩写) 首部,说明用于计算差异的算法; 新的 ETag 首部和 Delta-Base 首部,说明用于计算差异的基线文档的 ETag(理论上,它应该和客户端之前请 求里的 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 相同) | **实例操控、差异生成器和差异应用器** 客户端可以使用 A-IM 首部说明可以接受的一些实例操控的类型。一些在 IANA 注册的实例 操控类型。 | 类型 | 说明 | | -------- | --------------------------------------------------------------------------- | | vcdiff | 用 vcdiff 算法计算差异 (vcdiff 的规范由 RFC3284 发布) | | diffe | 用 Unix 系统的 diff-e 命令计算差异 | | gdiff | 用 gdiff 算法计算差异 [link](http://www.w3.org/TR/NOTE-gdiff-19970901.html) | | gzip | 用 gzip 算法压缩 | | deflate | 用 deflate 算法压缩 | | range | 用在服务器的响应中,说明响应是针对范围选择得到的部分内容 | | identity | 用在客户端请求中的 A-IM 首部中,说明客户端愿意接受恒等实例操控 | ### 更多信息 关于实体和编码方面的更多信息,请参考以下资源。 - http://www.ietf.org/rfc/rfc2616.txt RFC 2616,也就是 HTTP/1.1 版的规范,是实体 主体管理和编码方面的主要参考。 - http://www.ietf.org/rfc/rfc3229.txt RFC 3229,“Delta Encoding in HTTP”(“HTTP 中的差异编码”),说明了如何通 过扩展 HTTP/1.1 来支持差异编码。 - Introduction to Data Compression11(《数据压缩导论》) 这本书的作者是 Khalid Sayood,出版商为 Morgan Kaufmann Publishers。该书介 绍了几种 HTTP 内容编码支持 的压缩算法。 - http://www.ietf.org/rfc/rfc1521.txt RFC 1521,“Multipurpose Internet Mail Extensions, Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies”(“ 多 用 途 因 特网邮件扩展,第一部分:规定和描述因特网 报文主体格式的机制”),描述了 MIME 主体的格式。这份参考材料很有用,因为 HTTP 从 MIME 中借用了大量内容。设计这份文档的目的,就是为了提供在单一报文中包含多个对 象的各种设施,比如用 US-ASCII 之外的字符集来表示主体文本,表示多种字体格式的文 本消息以及表示非文本类的信息,比如图像和声音片段等。 - http://www.ietf.org/rfc/rfc2045.txt RFC 2045,“Multipurpose Internet Mail Extensions, Part One: Format of Internet Message Bodies”(“多用途因特网邮件扩展 ,第一部分:因特网报文主体的格 式”),规定了用来描述 MIME 格式报文结构的各种首部 ,其中许多都和 HTTP 中的用法类似或相同。 - http://www.ietf.org/rfc/rfc1864.txt RFC 1864,“The Content-MD5 Header Field”(“Content-MD5 首部字段”),提 供了用 Content-MD5 首部字段来做报文完整性检 查的行为及用途方面的一些历史 细节。 - http://www.ietf.org/rfc/rfc3230.txt RFC 3230,“Instance Digests in HTTP”(“HTTP 中 的 实 例 摘 要 ”), 描 述 了 对 HTTP 实体摘要处理的改进,解决了 Content-MD5 中存在的各种问题。