从CloseableHttpAsyncClient获取Gziped内容

时间:2018-03-06 22:25:33

标签: apache http nio apache-httpasyncclient

我正在一个我目前只需要使用apache hc库的环境中工作。更具体地说,

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpasyncclient</artifactId>
        <version>4.1.3</version>
    </dependency>

虽然我尝试使用CloseableHttpAsyncClient的实例作为Spring的AsyncRestTemplate的客户端。我编写了以下示例来试验从本地服务器获取gzip压缩内容。

我可以使用httpie验证服务器是否支持压缩响应,如下所示。

$ http --print=Hhb :8081/hello
GET /hello HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8081
User-Agent: HTTPie/0.9.9

HTTP/1.1 200
Content-Encoding: gzip
Content-Type: application/json;charset=UTF-8
Date: Tue, 06 Mar 2018 22:05:25 GMT
Transfer-Encoding: chunked
Vary: Accept-Encoding

[
    1,
    2,
    3,
    4,
    5
]

现在,当我尝试使用CloseableHttpAsyncClient的示例代码进行读取时,它无法正常工作。即使在添加了ResponseInterceptor之后,我也需要解压缩gzip内容。

// org.slf4j.Logger LOGGER

// Imports
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;


// Demo Code

CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom()
    .setDefaultRequestConfig(
        RequestConfig.custom()
            .setContentCompressionEnabled(false)
            .build())
    .setDefaultHeaders(Collections.singleton(new BasicHeader("Accept-Encoding", "gzip")))
    .setProxy(new HttpHost("localhost", 8080))
    .addInterceptorFirst((HttpResponseInterceptor) (response, context) -> {
        HttpEntity entity = response.getEntity();
        Header contentEncodingHeader = entity.getContentEncoding();

        if (contentEncodingHeader != null) {
            HeaderElement[] encodings = contentEncodingHeader.getElements();
            for (int i = 0; i < encodings.length; i++) {
                if (encodings[i].getName().equalsIgnoreCase("gzip")) {
                    response.setEntity(new GzipDecompressingEntity(entity));
                    break;
                }
            }
        }

    })
    .build();

httpAsyncClient.start();

HttpGet request = new HttpGet("http://localhost:8081/hello");
Future<HttpResponse> future = httpAsyncClient.execute(request, null);
HttpResponse httpResponse = future.get();

LOGGER.info(decompress(EntityUtils.toByteArray(httpResponse.getEntity())));

// Output:
// [1,2,3,4,5]

其中解压缩是我写的一个小实用程序来进行解压缩。

public static String decompress(byte[] compressedBytes) throws IOException {
    ByteArrayInputStream bis = new ByteArrayInputStream(compressedBytes);
    GZIPInputStream gis = new GZIPInputStream(bis);
    BufferedReader br = new BufferedReader(new InputStreamReader(gis, "UTF-8"));
    StringBuilder sb = new StringBuilder();
    String line;
    while((line = br.readLine()) != null) {
        sb.append(line);
    }
    br.close();
    gis.close();
    bis.close();
    return sb.toString();
}

现在我期望的是EntityUtils.toString(httpResponse.getEntity())应该直接返回字符串中的未压缩响应,但它没有,我必须手动解压缩内容。我怀疑减压是GzipDecompressingEntity的工作,但由于某些原因它不能很好地发挥作用(可能是因为响应被分块了吗?)

有没有办法可以通过正确地使用GzipDecompressingEntity或者以异步方式手动执行解压缩来以异步方式进行解压缩,这样当我future.get()时,它的响应就是没压缩?

0 个答案:

没有答案