1、前言
咱们在平常的抓包过程当中常常能够看到以Accept开头的请求首部,好比:Accept-Language 有一个q值,确定有人好奇在HTTP规范中为何要定义这个q值;还有在响应首部有一个名为Vary的首部,这个首部又有什么意义?如图所示:
2、内容协商
要讲清楚这两个问题,咱们须要引入HTTP协议的内容协商概念:某一资源,服务器有多个版本,客户端告知服务器本身的偏好,服务器根据偏好选择合适的版本响应客户端的请求。内容协商技术一般有三种实现方案:
(1)客户端驱动
客户端发起请求,服务器发送可选项列表,客户端做出选择后在发送第二次请求。
优势:比较容易实现
缺点:增长了时延,至少要发送两次请求,第一次请求获取资源列表,第二次获取选择的副本。
(2)服务器驱动
服务器检查客户端的请求首部集并决定提供哪一个版本的页面。
优势:比客户端驱动的协商要快。HTTP提供了q机制,容许服务器近似匹配,还提供了vary首部供服务器告知下游的设备(如代理服务器)如何对请求估值。
缺点:首部集不匹配,服务器要作猜想
(3)透明协商
某个中间设备(一般是缓存代理)表明客户端进行协商
优势:免除了web服务器的协商开销,比客户端驱动的协商要快。
缺点:HTTP并无提供相应的规范
其中,服务器驱动的解决方案应用的较为普遍。
3、通用的内容协商首部
客户端发送Accept首部集发送用户的偏好信息,服务器发送实体首部集匹配客户端的Accept首部集:
Accept Content-type
Accept-Language Content-Language
Accept-Encoding Content-Encoding
4、q质量值的应用场景
假设客户端的Accept-Language指定的是西班牙语,可是服务端只有英语与法语版本,这个客户端但愿在没有西班牙语的时候优先返回英语。这就意味着,咱们须要一种HTTP机制更详细的描述偏好。这种机制就是质量值(q值)。示例以下:
Accept-Language: en;q=0.5, fr;q=0.0, nl;q=1.0, tr;q=0.0
这个首部表示:用户最愿意接受荷兰语(nl),英文也行(en),就是不肯意接受法语(fr)或者土耳其语(tr)
q值的范围从0.0~1.0(1.0优先级最高)
5、vary首部的应用场景
服务器的决策不是依据Accept首部集(常规的内容协商首部集),而是好比Accept-Encoding
假设整个请求过程是这样的:客户端 -> 代理服务器(具有缓存功能) ->web服务器。
第一个支持gzip压缩的客户端向中间代理服务器发送请求,代理服务器转发该请求,向web服务器拉取内容,拿到内容后代理服务器缓存该内容(因为请求首部有Accept-Encoding: gzip 因此内容会被压缩)。
第二个不支持gzip压缩的客户端也向中间代理服务器发送同一个请求,代理服务器发现该请求已经被缓存了,因而就把压缩后的内容响应给该客户端。悲剧了,由于该客户端根本不支持gzip压缩,也就无法解压。
6、Vary首部的工做原理
HTTP的Vary响应首部中列出了全部客户端请求首部,缓存服务器能够用这些首部来选择文档或者产生定制的内容。好比:若给客户端的响应内容取决于Accept-Encoding,Vary首部就必须包含Accept-Encoding。
当新的请求到达时,缓存服务器会根据内容协商首部集来寻找最佳匹配。可是在把文档提供给客户端以前,它必须检查web服务器有没有在已缓存响应中发生Vary首部。若是有,那么新请求中那些首部的值必须与旧的已缓存请求里相应的首部相同。由于web服务器可能会根据客户端请求的首部来改变响应,为了实现透明协商,
缓存服务器必须为每一个已缓存变体保存客户端请求首部和相应的服务器响应首部。
简单的讲:
web服务器添加响应首部Vary: Accept-Encoding 告知代理服务器根据客户端的请求首部Accept-Encoding缓存不一样的版本,这样下次客户端请求同一资源时,根据Accept-Encoding选择相应的缓存版本响应。
其实咱们还能够禁用中间实体的缓存功能解决该问题:
web服务器设置响应首部: Cache-Control: private
一般添加Vary首部的解决方案比较通用,由于咱们仍是但愿充分利用中间实体的缓存功能的。
7、参考资料
书籍:《HTTP权威指南》