Apache HttpClient 未显示响应的 Content-Length 和 Content-Encoding 标头
Posted
技术标签:
【中文标题】Apache HttpClient 未显示响应的 Content-Length 和 Content-Encoding 标头【英文标题】:Apache HttpClient is not showing Content-Length and Content-Encoding headers of the response 【发布时间】:2020-07-15 06:35:11 【问题描述】:我安装了Apache httpcomponents-client-5.0.x,在查看http响应的标头时,我很震惊它没有显示Content-Length
和Content-Encoding
标头,这是我用于测试的代码
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import com.sun.net.httpserver.Headers;
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet request = new HttpGet(new URI("https://www.example.com"));
CloseableHttpResponse response = httpclient.execute(request);
Header[] responseHeaders = response.getHeaders();
for(Header header: responseHeaders)
System.out.println(header.getName());
// this prints all the headers except
// status code header
// Content-Length
// Content-Encoding
无论我尝试什么,我都会得到相同的结果,就像这样
Iterator<Header> headersItr = response.headerIterator();
while(headersItr.hasNext())
Header header = headersItr.next();
System.out.println(header.getName());
或者这个
HttpEntity entity = response.getEntity();
System.out.println(entity.getContentEncoding()); // NULL
System.out.println(entity.getContentLength()); // -1
根据 6 年前问过的this question,即使使用旧版本的 Apache HttpClient,这似乎也是一个老问题。
当然,正如 Wireshark 所确认的那样,服务器实际上正在返回这些标头,并且 Apache HttpClient 会自行记录
2020-04-03 07:59:09,106 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << HTTP/1.1 200 OK
2020-04-03 07:59:09,106 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Content-Encoding: gzip
2020-04-03 07:59:09,106 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Accept-Ranges: bytes
2020-04-03 07:59:09,107 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Age: 451956
2020-04-03 07:59:09,107 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Cache-Control: max-age=604800
2020-04-03 07:59:09,107 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Content-Type: text/html; charset=UTF-8
2020-04-03 07:59:09,107 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Date: Fri, 03 Apr 2020 05:59:09 GMT
2020-04-03 07:59:09,108 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Etag: "3147526947+gzip"
2020-04-03 07:59:09,108 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Expires: Fri, 10 Apr 2020 05:59:09 GMT
2020-04-03 07:59:09,108 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
2020-04-03 07:59:09,108 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Server: ECS (dcb/7EEB)
2020-04-03 07:59:09,108 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Vary: Accept-Encoding
2020-04-03 07:59:09,109 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << X-Cache: HIT
2020-04-03 07:59:09,109 DEBUG [org.apache.hc.client5.http.headers] http-outgoing-0 << Content-Length: 648
顺便说一句,java.net.http
库称为 JDK HttpClient
效果很好,可以显示所有标题。
是我做错了什么,还是应该报告一个存在多年的错误?
【问题讨论】:
检查4.x版本是否有同样的问题 HttpClient 4.x 的行为在设计上完全相同。如果有人不想要透明内容压缩,可以在构建 HttpClient 时轻松禁用它 @ok2c 谢谢我已经阅读了你的答案here,是的,这个解决方案会阻止HttpClient自动发送Accept-Encoding
头,如果我手动设置这个头,HttpClient不会解压响应内容,有没有办法让响应解压缩并且响应标头也可以?我应该问另一个问题吗?
@Accountantم 这些标题被删除是有充分理由的。但是,如果您绝对确定可以将标准 ContentCompressionExec
替换为自定义 exec 拦截器。
@ok2c 这似乎是一个痛苦的工作,我会检查它,但如果它真的很难做到,我没有办法,只能牺牲那些标题:(
【参考方案1】:
HttpComponents 提交者在这里...
你没有密切注意戴夫 G 说的话。默认情况下HttpClientBuilder
会开启透明解压,你看不到一些header的原因是here:
if (decoderFactory != null)
response.setEntity(new DecompressingEntity(response.getEntity(), decoderFactory));
response.removeHeaders(HttpHeaders.CONTENT_LENGTH);
response.removeHeaders(HttpHeaders.CONTENT_ENCODING);
response.removeHeaders(HttpHeaders.CONTENT_MD5);
...
对于JDK HttpClient,它不会进行任何透明的解压,因此可以看到压缩流的长度。你必须自己解压。
在这里卷曲提交者...
我也有raised an issue。
【讨论】:
非常感谢 Michael-O 在 HttpComponents 工作的时间以及在 SO 上拥有一个活跃帐户。但迈克尔我很贪婪,需要两个功能(解压缩的内容 + 所有响应标头),例如 curl。我不必牺牲其中之一,你为什么要删除这些标题?它们是从服务器返回的实际标头。 有什么方法可以获取解压后的内容和headers? @Accountantم 正如 ok2c 指出的那样,除非您编写自定义代码,否则您不能同时拥有两者。您应该手动执行解压缩。这将保留所有标题。关于 curl,我认为这是一个错误。我提出了一个问题。 我在 GitHub 上评论了 curl 问题,请 Michael 重新考虑允许您的消费者获取从服务器发送的响应标头,即使是原始字符串。 @Accountantم 您可以访问。禁用自动解压。 Michael :) 你知道我的意思是让响应自动解压缩。无论如何,与此同时,我只能牺牲这些头文件,我们必须在计划的时间内完成项目,我们没有时间实施自己的解压。感谢您在 HttpComponents 中付出的时间和努力,我希望您有一天能改变主意,从另一个角度考虑,作为库的用户,而不是库的制造者。【参考方案2】:在这种情况下可能会忽略内容长度。
HttpGet request = new HttpGet(new URI("https://www.example.com"));
request.setHeader("Accept-Encoding", "identity");
CloseableHttpResponse response = httpclient.execute(request);
我可以看到以下内容
HttpEntity entity = response.getEntity();
System.out.println(entity.getContentLength());
System.out.println(entity.getContentEncoding());
输出
...
2020-04-03 03:04:17.760 DEBUG 34196 --- [ main] org.apache.hc.client5.http.headers : http-outgoing-0 << Content-Length: 1256
...
1256
null
我想请您注意正在发送的此标头:
http-outgoing-0 >> Accept-Encoding: gzip, x-gzip, deflate
这告诉服务器这个客户端可以接受 gzip、x-gzip 和 deflate 内容作为响应。响应说明它是“gzip”编码的。
http-outgoing-0 << Content-Encoding: gzip
我相信 HttpClient 正在内部透明地处理此问题并提供内容。
正如您引用的另一篇文章中所述,其中一个答案表明可以应用方法EntityUtils.toByteArray(httpResponse.getEntity()).length
来获取内容长度。
【讨论】:
好的,是的,它只有在我发送Accept-Encoding: identity
标头时才有效,否则HttpClient
不会显示Content-Length
标头:(。关于计算字节解决方法,我不想这样做是因为我将大量使用该库,并且计算我所做的每个 http 请求的字节数可能会产生性能问题,只有在 HttpClient 告诉我所有标头时才能避免这种问题。非常感谢您的帮助
不客气 - 我希望我有一个更好的解决方案来满足您的需求。如果您对此答案感到满意,请将其标记为已接受。
感谢 Dave G,是的,您的回答对我有很大帮助,我昨天投了赞成票,但是在接受的答案中看到导致这种行为的实际代码行可能对后来的读者有很大帮助,我希望我两个答案都可以接受。以上是关于Apache HttpClient 未显示响应的 Content-Length 和 Content-Encoding 标头的主要内容,如果未能解决你的问题,请参考以下文章
1. 通过apache common封装好的HttpClient
在 Apache HttpClient 4.x 中多次读取响应正文
如何从 Apache HttpClient5 的 HttpResponse 中获取响应体?
Apache Cordova:“加载资源失败:服务器响应状态为 404(未找到)”