In a previous blog we looked at building a RESTful client using the Spring Framework. In this blog we are going to build on this topic and show how to enable Spring’s RestTemplate to use gzip compression.
Gzip is a software application used for file compression and decompression. The Content-Encoding/Accept-Encoding headers of the HTTP/1.1 specification allows clients and servers to send and receive compressed HTTP responses.
Why use gzip?
Multi-core CPU’s are commonplace and processing time is cheap! On the other hand bandwidth is expensive! Compression is a simple way to speed up your application.
The interaction goes something like this:
Client : Hey server I’d love to get the resource located at ‘robby/orders/32’,
and by the way I can accept gzip encoded responses.
Server : Hey, here’s the resource you requested. It’s gzipped. Thanks for
saving us both time and resources!
We are going to build a client that supports gzip encoding. JavaDoc for RestTemplate shows a constructor that takes a ClientHttpRequestFactory as a parameter. A ClientHttpRequestFactory is an interface, whose implementers create ClientHttpRequests that are executed by the
RestTemplate. When any of the RestTemplate get*, post*, put*, or delete* methods are called, a ClientHttpRequest is created behind the scenes by the default implementation (SimpleClientHttpRequestFactory
) which uses standard J2SE facilities.
The ClientHttpRequestFactory interface includes one method whose signature is below:
ClientHttpRequest createRequest(URI uri,HttpMethod httpMethod) throws IOException
The contract for the interface is simple: Create a new ClientHttpRequest for the specified URI and HTTP method. Our implemented method is below:
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
return new GzipClientHttpRequest(connection);
}
ClientHttpRequest is an interface but looking at the Spring documentation we see that there is an AbstractClientHttpRequest class that we can extend to provide the functionality we need. We’ll call this class GzipClientHttpRequest. In our class we will need to implement the lone abstract method:
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput)
This method writes the given headers and content to the HTTP request and returns a ClientHttpResponse object for the executed request. Our implemented version is below along with an overridden constructor:
public GzipClientHttpRequest(HttpURLConnection connection) {
this.connection = connection;
this.getHeaders().add("Accept-Encoding","gzip");
}
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
for (Map.Entry>entry : headers.entrySet()) {
String headerName = entry.getKey();
for (String headerValue : entry.getValue()) {
this.connection.addRequestProperty(headerName, headerValue);
}
}
this.connection.connect();
if (bufferedOutput.length > 0) {
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
}
return new GzipClientHttpResponse(this.connection);
}
Our constructor adds the Accept-Encoding header with a value of gzip, letting the server know that our client can handle gzipped responses. The executeInternal method adds the headers to the connection, connects to the server, and creates a GzipClientHttpResponse from the connection’s OutputStream.
GzipClientHttpResponse i