RFC7233 HTTP范围请求(Range Requests)
由于HTTP请求容易(被)取消或(被)中断,HTTP客户端常常获取不到完整的数据(数据传输中断)。因此作为客户端,在获取到了一部分数据后,更希望在后续的请求中获取剩余的数据,而不是一下子获取所有数据。同样的,在一次请求中获取一部分数据,也很利于存储空间不足的设备。比如(大型)文档中的某一页,或者需要嵌入的图片的尺寸。
此文档定义了 HTTP/1.1 协议的范围请求(range requests)、部分响应(partial responses)以及传输的媒体类型。
范围请求(range requests)是可选的 HTTP 的特性,假如接收方没有实现这个特性(或者不支持特定的资源),请求也可以被当作普通的 GET 请求来处理,且不影响两端交互。
部分响应(partial responses)通过一个独特的状态码来表示,不会被没有实现此特性的浏览器错误的识别为完整的响应而缓存下来。
尽管范围请求协议允许拓展其他的范围单位,但这里只讨论以字节为单位的范围请求。
所有HTTP可传输的资源,通常是以一系列字节的形式传输的,因此字节范围是有存在的意义的。一个包含字节范围的请求,可以声明一段字节的范围,也可以一次性声明多段字节范围。
字节范围的声明包含首字节的位置以及末字节的位置,例如:
bytes=0-499
bytes=500-999
客户端在不知道资源大小时可以限制请求获取的字节数量;
如果没有指定末字节的位置,或者指定的位置大于等于资源的大小,那么这段字节范围会被服务端转译为资源剩余大小的范围(或者更小)
客户端可以通过使用下面这种方式请求资源的最后N个字节: -${length}
如果长度大于资源长度,表示请求整个资源
假设资源长度为10000字节,以下几个例子:
bytes=-500
OR bytes=9500-
bytes=0-0,-1
bytes=500-600,601-999
OR bytes=500-700,601-999
一个合格的字节范围,可以是以下格式中的一种:
-${length}
, 非0长度${first-byte-pos}-
, first-byte-pos
必须小于资源长度${first-byte-pos}-${last-byte-pos}
此响应头 Accept-Ranges
允许服务器告诉客户端,对于请求的资源所支持的范围请求方式。
如果服务器对于请求的资源支持字节范围的请求,那么就会发送:
Accept-Ranges: bytes
告诉客户端哪种范围单位是其支持的。
如果服务器不支持任何形式的范围请求,那么就会如下:
Accept-Ranges: none
GET方法请求头中的 Range
字段,改变了GET方法的语义,请求的不再是整个指定资源,而是该资源的一段或多段子范围数据。
服务器可能会忽略请求头中的 Range
字段。但是,源服务器、中间路由等应该尽可能的支持字节范围请求。然而非GET请求中携带的 Range
头必须被抛弃。
如果请求头中的范围请求单位不被服务器支持,服务器也应当忽视这个字段。(代理也有可能会抛弃 Range
头如果它发觉该单位自己不支持)
如果范围请求头字段 Range
包含了两个(及以上)重叠的范围,或多个请求的范围不按升序排列时,支持范围请求的服务器也可能忽视或拒绝为范围请求服务,因为这很有可能是有人恶意为之(DOS)或客户端损坏导致的。
客户端发送多个请求获取多段范围时,这些范围应当按照升序的方式排列(朝着最终能获取到完整资源的方向)。除非有特殊的需求,需要不连续的提前获取某一段内容。
范围请求头 Range
在其他头部成功处理完(什么概念呢,就是说假如没有此 Range
头, 最终会返回200)之后处理,如果请求返回的结果是304那么此请求头就会被忽略。
如果所有前置条件都满足,服务器也支持此资源的范围请求,请求中声明的范围也都是合法且可以被满足的,服务端就会返回响应头 206 Partial Content 以及所请求的部分数据。
如果所有前置条件都满足,服务器也支持此资源的范围请求,但请求中声明的范围是无效或无法被满足的,服务器会返回 416 Range Not Satisfiable
如果客户端已有资源的部分数据并且希望获取该资源最新的完整的副本,那么客户端可以使用 GET
请求(携带 If-Unmodified-Since
或(与) If-Match
)来进行获取。如果发现资源已经过期,那么客户端需要再次发送一个请求以获取最新的资源数据。
HTTP请求头 If-Range
允许客户端省去这第二个请求。换句话说,它意味着: 如果资源没有发生变化,那么把请求中 Range
指定的部分数据发送给我;否则,发送完整的资源给我。(一般用于断点续传,如果再次开始请求数据发现数据没有发生变化,那么继续请求;否则重新获取资源。用来自从上次中断后,确保下载的资源没有发生改变)
If-Range = entity-tag / HTTP-date
客户端请求不能只有 If-Range
而没有 Range
。 服务端也必须在没有发现 Range
头的情况下(或是不支持 Range
头时)忽略 If-Range
请求头。
(关于 If-Range
和 ETag
以及 HTTP-date
, 暂时不译)
206
状态码表示服务器完成且传输了请求所需的部分数据。此响应必须包含 Content-Range
响应头,表明此次响应返回的数据范围。请求体则包含所请求的数据。
HTTP/1.1 206 Partial Content
Date: Wed, 15 Nov 1995 06:25:24 GMT
Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
Content-Range: bytes 21010-47021/47022
Content-Length: 26012
Content-Type: image/gif
... 26012 bytes of partial image data ...
如果传输不止一部分数据,此响应需要向如下:
HTTP/1.1 206 Partial Content
Date: Tue, 14 Nov 1995 06:25:24 GMT
Last-Modified: Tue, 14 July 04:58:08 GMT
Content-Length: 1741
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
--THIS_STRING_SEPARATES
Content-Type: video/example
Content-Range: bytes 500-999/8000
...the first range...
--THIS_STRING_SEPARATES
Content-Type: video/example
Content-Range: bytes 7000-7999/8000
...the second range
--THIS_STRING_SEPARATES--
需要在HTTP头部指定 Content-Type
字段,说明媒体类型 multipart/byteranges
和每段内容之间的分割边界。为了避免和只返回一部分数据的响应混淆,服务端不能在HTTP头部指定 Content-Range
字段。
在响应体中的每部分数据头部,服务端必须指定 Content-Range
字段,说明那部分数据的范围。并且,服务端需要在每部分数据头部指定 Content-Type
字段。(值就等于该资源返回状态码200时携带的 Content-Type
的值)
服务器可能不会根据请求头中的 Range
字段返回数据,而是返回合并范围后的数据。比如客户端请求的多段范围有重叠,或是分段的数据大小还不及分段所需的额外开销的大小。一般而言,多段数据传输的额外开销在80字节左右,根据资源类型、每段分隔符的不同而不同。因此,传输许多小范围的不相交的数据可能会比传输整个资源更加低效。
如果客户端没有请求多段数据,那么服务端不能返回多段数据,因为客户端可能不支持多段范围响应的解析。 但是,如果客户端请求了多段范围,服务端可以返回响应体内只有一段数据的但 Content-Type
为 multipart/byteranges
的响应。(可能是因为请求中只有一段范围能被满足或是在合并后只有一段范围剩余了这些情况) 客户端在不能处理多段范围响应时不能请求获取多段范围!
返回多段数据时,在请求头中 Rnage
字段声明的多个范围,除了无法被满足以及被合并处理的范围,服务端都应当按照该字段的顺序返回数据。客户端在接受数据时也应当检查每段数据头部的 Content-Range
以判断该段数据存放的是哪部分数据(客户端最好不要依赖自己发出的请求中指定的范围来处理返回的数据)
除了上述必须字段,响应码200会返回的字段,返回206响应时也必须返回。(Date、Cache-Control、ETag、Expires、、、)
206响应是可以被缓存的。
响应头中的 Content-Range
字段出现在以下情况:
Range
只请求一段范围,表示响应体数据范围Range
请求多段范围,在响应体的每部分头部出现,表示该部分数据的范围对于字节范围,发送者需要在指定数据范围的同时,声明资源的完整长度(大小),除非该资源的长度是未知的。星号 *
就可以用来表示未知。如下:
Content-Range: bytes 42-1233/1234
: 表示完整长度就是1234Content-Range: bytes 42-1233/*
: 表示完整长度未知Content-Range: bytes */1234
: 响应码为416返回,携带完整长度信息:1234因此,以下情况 Content-Range
是无效的:
除了206,416响应码,其他响应码如果携带此字段是没有意义的
下面是大小为1234的资源:
o The first 500 bytes:
Content-Range: bytes 0-499/1234
o The second 500 bytes:
Content-Range: bytes 500-999/1234
o All except for the first 500 bytes:(除了前500个字节)
Content-Range: bytes 500-1233/1234
o The last 500 bytes:
Content-Range: bytes 734-1233/1234
例子: http://hongjiang.info/http-header-range-and-content-range/