I am working in an environment where I am currently required to only use apache hc libraries. More specifically,
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.1.3</version>
</dependency>
Although I am trying to use an instance of CloseableHttpAsyncClient as the client for Spring's AsyncRestTemplate. I wrote the following example to experiment with fetching gzipped content from a local server.
I am able to verify that server supports compressed response using httpie as you can see below.
$ 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
]
Now when I try to read using a sample code for CloseableHttpAsyncClient, it does not work very clearly. I am required to decompress the gzip content even after adding the ResponseInterceptor.
// 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]
where decompress is a small utility i wrote to do the decompression.
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();
}
Now what I expected is EntityUtils.toString(httpResponse.getEntity())
should directly return me the uncompressed response in string but it does not and I have to manually decompress the content. I suspected the decompression is the job of GzipDecompressingEntity but for some reason it doesn't play well (could it be because the response is chunked?)
Is there a way i can do the decompression in the async fashion either by using GzipDecompressingEntity
correctly or manually doing the decompression in an async fashion so that when i do future.get()
it has the response that is uncompressed ?