http协议头部部分参数解析与应用

这个周工作中遇到了几个问题,都是关于http协议的头部报错的,大致可以分为三类,Referrer Policy, 断点续传,范围请求的。这里搜索和整理了一点内容还有应用。

Referrer Policy

Referer

首先要搞明白Referer是什么。

简单来说,当你发起一个http请求,请求头中的referrer字段就说明了你是从哪个页面发起该请求的。详细的解释可以看阮一峰老师的Http Referer 教程

Referer Policy

Referrer-Policy的作用就是为了控制请求头中referrer的内容,目前是一个候选标准,不过已经有部分浏览器支持该标准。

目前Referrer-Policy只包含以下几种值:

1
2
3
4
5
6
7
8
9
10
11
enum ReferrerPolicy {
"",
"no-referrer",
"no-referrer-when-downgrade",
"same-origin",
"origin",
"strict-origin",
"origin-when-cross-origin",
"strict-origin-when-cross-origin",
"unsafe-url"
};复制代码

空字符串

若设为空串则默认按照浏览器的机制设置referrer的内容,默认情况下是和no-referrer-when-downgrade设置得一样。

no-referrer

不显示referrer的任何信息在请求头中。

no-referrer-when-downgrade

这是默认值。当从https网站跳转到http网站或者请求其资源时(安全降级HTTPS→HTTP),不显示referrer的信息,其他情况(安全同级HTTPS→HTTPS,或者HTTP→HTTP)则在referrer中显示完整的源网站的URL信息。

same-origin

表示浏览器只会显示referrer信息给同源网站,并且是完整的URL信息。所谓同源网站,是协议、域名、端口都相同的网站。

origin

表示浏览器在referrer字段中只显示源网站的源地址(即协议、域名、端口),而不包括完整的路径。

strict-origin

该策略更为安全些,和origin策略相似,只是不允许referrer信息显示在从https网站到http网站的请求中(安全降级)。

origin-when-cross-origin

当发请求给同源网站时,浏览器会在referrer中显示完整的URL信息,发个非同源网站时,则只显示源地址(协议、域名、端口)

strict-origin-when-cross-origin

origin-when-cross-origin相似,只是不允许referrer信息显示在从https网站到http网站的请求中(安全降级)。

unsaft-url

浏览器总是会将完整的URL信息显示在referrer字段中,无论请求发给任何网站

Referrer-Policy更改方法

可以有以下5种方法:

1. 通过Referrer-Policy HTTP header设置:

1
Referrer-Policy: origin复制代码

2. 通过``元素改变Referrer Policy,直接修改名为referrer的内容

1
<meta name="referrer" content="origin">复制代码

3.](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a), [, ![img](), ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe), 或者[元素设置referrerpolicy属性

1
<a href="http://example.com" referrerpolicy="origin">复制代码

4. 如需设置不显示referrer信息时,也可以给 ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a), [, ``元素设置rel的链接关系。

1
<a href="http://example.com" rel="noreferrer">

范围请求

范围请求主要是针对较大的文件的请求或者上传,可以仅操作它的某一段。

一个比较常见的场景,就是断点续传/下载,在网络情况不好的时候,可以在断开连接以后,仅继续获取部分内容。例如在网上下载软件,已经下载了 95% 了,此时网络断了,如果不支持范围请求,那就只有被迫重头开始下载。但是如果有范围请求的加持,就只需要下载最后 5% 的资源,避免重新下载。

另一个场景就是多线程下载,对大型文件,开启多个线程,每个线程下载其中的某一段,最后下载完成之后,在本地拼接成一个完整的文件,可以更有效的利用资源。

这算是两个比较常见的场景,接下来我们来看看范围请求的 HTTP 协议支持的技术细节。

HTTP 的范围请求

是否支持范围请求

HTTP 本身是一种无状态的“松散”协议,而在经历了很多版本的迭代之后,只在 HTTP/1.1(RFC2616) 之上,才支持范围请求。所以如果客户端或者服务端两端的某一端低于 HTTP/1.1,我们就不应该使用范围请求的功能。

而在 HTTP/1.1 中,很明确的声明了一个响应头部 Access-Ranges 来标记是否支持范围请求,它只有一个可选参数 bytes

例如这里给了一个 MP4 的响应头,可以看到它是有 Accept-Ranges:bytes 来标记的,有此标记标识当前资源支持范围请求。

使用范围请求

如果已经确定双端都支持范围请求,我们就可以在请求资源的时候使用它。

所有的文件最终都是存储在磁盘或者内存中的字节,对于待操作的文件可以将其以字节为单位分割。这样只需要 HTTP 支持请求该文件从 n 到 n+x 这个范围内的资源,就可以实现范围请求了。

HTTP/1.1 中定义了一个 Ranges 的请求头,来指定请求实体的范围。它的范围取值是在 0 - Content-Length 之间,使用 - 分割。。

例如已经下载了 1000 bytes 的资源内容,想接着继续下载之后的资源内容,只要在 HTTP 请求头部,增加 Ranges:bytes=1000- 就可以了。

Range 还有几种不同的方式来限定范围,可以根据需要灵活定制:

1. 500-1000:指定开始和结束的范围,一般用于多线程下载。

2. 500- :指定开始区间,一直传递到结束。这个就比较适用于断点续传、或者在线播放等等。

3. -500:无开始区间,只意思是需要最后 500 bytes 的内容实体。

4. 100-300,1000-3000:指定多个范围,这种方式使用的场景很少,了解一下就好了。

HTTP 协议是一种双边协商的协议,既然请求头部已经确定是使用 Ranges 了,还有响应头部中,也需要使用 Content-Ragne 这个响应头来标记响应的实体内容范围。

Content-Range 的格式也很清晰,首先标记它的单位是 bytes 然后标记当前传递的内容实体范围和总长度。

1
Content-Range: bytes 100-999/1000

在这个例子中,会传递 100 ~ 999 范围的内容实体,而该资源文件的总大小是 1000 bytes。并且此时的 HTTP 响应状态码为 206 Partial Content

资源变化

当我们在一些下载工具中,下载大尺寸资源的时候,偶尔中间暂停过再重新下载,可能会遇见它又重头开始下载的情况。

这看似是 HTTP 的范围请求失效了,但是实际上并不一定如此,很可能是因为请求的资源,在请求的这个过程中,发生了改变。

假如你下载的过程中,下载的源资源文件发生了变化,但是 URL 没有改变,此时文件长度可能已经变化了(这是非常容易发现的),极端情况下就算没有长度没有变化,你再继续下载,很可能最终下载完成之后,无法将下载的内容拼接成我们需要的文件。

如果我们需要从服务器上下载某个资源,一定要预防此资源可能发生的变动。在之前讲 HTTP 缓存的时候讲到,在 HTTP 协议中,可以通过 ETag 或者 Last-Modified 来标识当前资源是否变化。

  • ETag:当前文件的一个验证令牌指纹,用于标识文件的唯一性。
  • Last-Modified:标记当前文件最后被修改的时间。

在 HTTP 的范围请求中,也可以使用这两个字段来区分分段请求的资源,是否有修改过,只需要在请求头中,将它放在 If-Range 这个请求报文头中即可。If-Range 使用 ETag 或者 Last-Modified 两个参数任意一个,原样填入即可。

参考文献:

https://juejin.im/post/5cd81b59518825686a06fd05

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referrer-Policy

https://juejin.im/post/5b555f055188251af25700aa

https://www.jianshu.com/p/934d3e8d371e