httpclient Accept-Encoding 乱码[通俗易懂]

httpclient Accept-Encoding 乱码[通俗易懂]解决方法1HttpEntityhttpEntity=httpResponse.getEntity();2if(httpEntity!=null){3if(httpEntity.getContentEncoding()!=null){4if("g…

大家好,又见面了,我是你们的朋友全栈君。

解决方法

 1 HttpEntity httpEntity = httpResponse.getEntity();
 2             if (httpEntity != null) {
 3                 if (httpEntity.getContentEncoding() != null) {
 4                     if ("gzip".equalsIgnoreCase(httpEntity.getContentEncoding().getValue())) {
 5                         httpEntity = new GzipDecompressingEntity(httpEntity);
 6                     } else if ("deflate".equalsIgnoreCase(httpEntity.getContentEncoding().getValue())) {
 7                         httpEntity = new DeflateDecompressingEntity(httpEntity);
 8                     }
 9                 }
10                 htmlByte = EntityUtils.toByteArray(httpEntity);
11             }

 

来自:https://www.imququ.com/post/vary-header-in-http.html

 

经常抓包看 HTTP 请求的同学应该对 Vary 这个响应头字段并不陌生,它有什么用?用 PageSpeed 工具检查页面时,经常看到「Specify a Vary: Accept-Encoding header(请指定一个 Vary: Accept-Encoding 标头)」这样的建议,为什么要这样做?本文记录我对 Vary 的一些研究,其中就包含这些问题的答案。

HTTP 内容协商

要了解 Vary 的作用,先得了解 HTTP 的内容协商机制。有时候,同一个 URL 可以提供多份不同的文档,这就要求服务端和客户端之间有一个选择最合适版本的机制,这就是内容协商。

协商方式有两种,一种是服务端把文档可用版本列表发给客户端让用户选,这可以使用 300 Multiple Choices 状态码来实现。这种方案有不少问题,首先多一次网络往返;其次服务端同一文档的某些版本可能是为拥有某些技术特征的客户端准备的,而普通用户不一定了解这些细节。举个例子,服务端通常可以将静态资源输出为压缩和未压缩两个版本,压缩版显然是为支持压缩的客户端而准备的,但如果让普通用户选,很可能选择错误的版本。

所以 HTTP 的内容协商通常使用另外一种方案:服务端根据客户端发送的请求头中某些字段自动发送最合适的版本。可以用于这个机制的请求头字段又分两种:内容协商专用字段(Accept 字段)、其他字段。

首先来看 Accept 字段,详见下表:

请求头字段 说明 响应头字段
Accept 告知服务器发送何种媒体类型 Content-Type
Accept-Language 告知服务器发送何种语言 Content-Language
Accept-Charset 告知服务器发送何种字符集 Content-Type
Accept-Encoding 告知服务器采用何种压缩方式 Content-Encoding

例如客户端发送以下请求头:

 
Accept:*/*
Accept-Encoding:
gzip
,deflate,sdch
Accept-Language:zh-CN,en-US;q=0.8,en;q=0.6

表示它可以接受任何 MIME 类型的资源;支持采用 gzip、deflate 或 sdch 压缩过的资源;可以接受 zh-CN、en-US 和 en 三种语言,并且 zh-CN 的权重最高(q 取值 0 – 1,最高为 1,最低为 0,默认为 1),服务端应该优先返回语言等于 zh-CN 的版本。

浏览器的响应头可能是这样的:

 
Content-Type: text
/javascript
Content-Encoding: 
gzip

表示这个文档确切的 MIME 类型是 text/javascript;文档内容进行了 gzip 压缩;响应头没有 Content-Language 字段,通常说明返回版本的语言正好是请求头 Accept-Language 中权重最高的那个。

有时候,上面四个 Accept 字段并不够用,例如要针对特定浏览器如 IE6 输出不一样的内容,就需要用到请求头中的 User-Agent 字段。类似的,请求头中的 Cookie 也可能被服务端用做输出差异化内容的依据。

由于客户端和服务端之间可能存在一个或多个中间实体(如缓存服务器),而缓存服务最基本的要求是给用户返回正确的文档。如果服务端根据不同 User-Agent 返回不同内容,而缓存服务器把 IE6 用户的响应缓存下来,并返回给使用其他浏览器的用户,肯定会出问题 。

所以 HTTP 协议规定,如果服务端提供的内容取决于 User-Agent 这样「常规 Accept 协商字段之外」的请求头字段,那么响应头中必须包含 Vary 字段,且 Vary 的内容必须包含 User-Agent。同理,如果服务端同时使用请求头中 User-Agent 和 Cookie 这两个字段来生成内容,那么响应中的 Vary 字段看上去应该是这样的:

 
Vary: User-Agent, Cookie

也就是说 Vary 字段用于列出一个响应字段列表,告诉缓存服务器遇到同一个 URL 对应着不同版本文档的情况时,如何缓存和筛选合适的版本。

有 BUG 的缓存服务

再来看 PageSpeed 的「Specify a Vary: Accept-Encoding header」这个提示,按照上面的说明,Accept-Encoding 属于内容协商专用字段,服务端只需要在响应头中增加 Content-Encoding 字段,用来指明内容压缩格式;或者不输出 Content-Encoding 表明内容未经过压缩就可以了。而缓存服务器,应该针对不同的 Content-Encoding 缓存不同内容,再根据具体请求中的 Accept-Encoding 字段返回最合适的版本。

但是有些实现得有 BUG 的缓存服务器,会忽略响应头中的 Content-Encoding,从而可能给不支持压缩的客户端返回缓存的压缩版本。有两个方案可以避免这种情况发生:

  1. 将响应头中的 Cache-Control 字段设为 private,告诉中间实体不要缓存它;
  2. 增加 Vary: Accept-Encoding 响应头,明确告知缓存服务器按照 Accept-Encoding 字段的内容,分别缓存不同的版本;

通常为了更好的利用中间实体的缓存功能,我们都用第二种方案。

对于 css、js 这样的静态资源,只要客户端支持 gzip,服务端应该总是启用它;同时为了避免有 BUG 的缓存服务器给用户返回错误的版本,还应该输出 Vary: Accept-Encoding。

Nginx 和 SPDY

通常,上面说的这些工作,Web Server 都可以帮我们搞定。对于 Nginx 来说,下面这个配置可以自动给启用了 gzip 的响应加上 Vary: Accept-Encoding:

 
gzip_vary on;

用 curl 验证我博客的 js 文件,响应头如下:

 
jerry@www:~$ curl --
head 
https:
//www
.imququ.com/...
/xx
.js
 
HTTP
/1
.1 200 OK
Server: nginx
Date: Tue, 31 Dec 2013 16:34:48 GMT
Content-Type: application
/x-javascript
Content-Length: 66748
Last-Modified: Tue, 31 Dec 2013 14:30:52 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: 
"52c2d51c-104bc"
Expires: Fri, 29 Dec 2023 16:34:48 GMT
Cache-Control: max-age=315360000
Strict-Transport-Security: max-age=31536000
Accept-Ranges: bytes

可以看到,服务端正确输出了「Vary: Accept-Encoding」,一切正常。

但是用 Chrome 自带抓包工具看下,这个响应头却是这样:

 
HTTP
/1
.1 200 OK
cache-control: max-age=315360000
content-encoding: 
gzip
content-
type
: application
/x-javascript
date
: Tue, 31 Dec 2013 16:35:27 GMT
expires: Fri, 29 Dec 2023 16:35:27 GMT
last-modified: Tue, 31 Dec 2013 14:30:52 GMT
server: nginx
status: 200
strict-transport-security: max-age=31536000
version: HTTP
/1
.1

我的博客支持 SPDY/2 协议,用 Chrome 访问我博客会走 SPDY,所以上面的响应头看上有点不同寻常,例如字段名都变成了小写;多了 status、version 等字段,这些变化下次专门介绍(注:见「SPDY 3.1 中的请求 / 响应头」)。神奇的是尽管服务端没任何变化,但响应中的 Vary: Accept-Encoding 却不见了。

SPDY 规定客户端必须支持压缩,这意味着 SPDY 服务器可以直接启用压缩而不用关心请求头中的 Accept-Encoding 字段。下面这段来自 Nginx 支持的 SPDY/2 协议:

User-agents are expected to support gzip and deflate compression. Regardless of the Accept-Encoding sent by the user-agent, the server may select gzip or deflate encoding at any time. [via]

于是,对于支持 SPDY 的客户端来说,Vary: Accept-Encoding 没有用途,Nginx 选择直接去掉它,可以节省一点流量。curl 或其他不支持 SPDY 协议的客户端还是走 HTTP 协议,所以看到的响应头是常规的。

Nginx 的这个做法是否合适一直有争论,实际上并不是所有支持 SPDY 的 Web Server 都会这么做。例如即使通过 SPDY 协议访问 Google 首页的 js 文件,依然可以看到 vary: Accept-Encoding:

 
HTTP
/1
.1 200 OK
status: 200 OK
version: HTTP
/1
.1
age: 25762
alternate-protocol: 443:quic
cache-control: public, max-age=31536000
content-encoding: 
gzip
content-length: 154614
content-
type
: text
/javascript
; charset=UTF-8
date
: Tue, 31 Dec 2013 23:23:51 GMT
expires: Wed, 31 Dec 2014 23:23:51 GMT
last-modified: Mon, 16 Dec 2013 21:54:35 GMT
server: sffe
vary: Accept-Encoding
x-content-
type
-options: nosniff
x-xss-protection: 1; mode=block

另外,现阶段 Chrome 和 Firefox 都支持 SPDY 协议,但 PageSpeed Chrome 版和 Firefox 版都没有针对 SPDY 协议做特别处理,所以用它们测试我的博客,还是会提示「Specify a Vary: Accept-Encoding header」,这有点让人哭笑不得。不过PageSpeed 在线版 已经更新规则,估计扩展版也快了。

PS:Vary 在 IE 下有很多坑,使用时要格外小心。网上这部分文章比较多,例如 hax 早年写的 IE 与 Vary 头,可以点过去了解下。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/159698.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 四十一、SPSS中的t检验和卡方检验[通俗易懂]

    四十一、SPSS中的t检验和卡方检验[通俗易懂]@Author:ByRunsen@Date:2020/5/14在2020年一月初,也是我大三上的寒假,我开始写书,为什么呢?因为化工原理和化工热力学挂了,我需要重拾自己的自信。对于一个大学三年,每天往死里干的人,竟然挂了两科。虽然,我化工专业已经陷入了绝境,大学我主要学习日语,Python,Java和一系列数据分析软件。所以本专栏数据分析将使用Excel,Powerbi,Python,R,Sql,SPSS,stata以及Tableau,后面还会补充BI。第五章应该是二月份完成的。文章目

  • 阿里云服务器怎么用ip访问不了

    阿里云服务器怎么用ip访问不了

    2021年10月18日
  • 旅行商问题(动态规划方法,超级详细的)

    旅行商问题(动态规划方法,超级详细的)一、题目一个售货员必须访问n个城市,恰好访问每个城市一次,并最终回到出发城市。 售货员从城市i到城市j的旅行费用是一个整数,旅行所需的全部费用是他旅行经过的的各边费用之和,而售货员希望使整个旅行费用最低。 (等价于求图的最短哈密尔顿回路问题)令G=(V,E)是一个带权重的有向图,顶点集V=(v0,v1,…,vn-1)。从图中任一顶点vi出发,经图中所有其他顶点一次且只有一次,最…

  • 什么是5g网络切片_5g网络切片架构三层

    什么是5g网络切片_5g网络切片架构三层2020年,相信很多小伙伴已经用上了5G手机,感受到了5G的网络带来的飞一般的感觉。不过,可能也有小伙伴会发出疑问:我买了5G手机,也体验了5G,好是好,但似乎也没有外界吹得那么神乎啊。不是说好的技术革命吗?就只是网速变快了而已?其实这背后主要有两个原因,一个是目前5G切实落地的应用还比较少,第二个就是目前5G的技术确实也还在演进过程中。我们回想一下5G的三大核心应用场景:增强型移动宽带(eMBB):更大的网络吞吐量、峰值速率和低延时;海量机器通信(mMTC):巨大的连

  • 八数码问题简单解决办法

    八数码问题简单解决办法问题分析:八数码问题是一个经典的BFS问题,把棋局看成一个状态图,共有9!种状态。从初始棋局开始,每次转移到下个状态,直到目标棋局为止。仔细分析可知,八数码的关键是判重,如果不去除重复状态,程序会产生很多无效状态,从而复杂度大大增加解决算法:BFS+Cantor案例分析:(0表示空格所在位置)初始棋局:|1|2|3||0|8|4||7|6|5|目标棋局:|1|0|…

  • 安卓强制恢复出厂recovery无命令_在recovery模式下无法恢复出厂设置

    安卓强制恢复出厂recovery无命令_在recovery模式下无法恢复出厂设置有时候我们在系统正常运行的时候,突然跑到recovery里面了,并且停在了如下界面:Can'tloadAndroidsystem.Yourdatamaybecorrupt.

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号