Learn how to send and handle Gzip compressed requests and responses in a Spring Boot application using RestTemplate.
1. Introduction
In our previous articles, we discussed various aspects of Gzip compression in Spring Boot:
- Gzip Compression in Spring Boot: An introduction to enabling Gzip compression.
- Conditionally Enabling Gzip Compression: How to enable Gzip compression based on specific conditions.
- Decompressing Gzip Requests in Spring Boot: A detailed guide on handling Gzip compressed requests.
Building upon these foundations, this article will guide you on how to send Gzip compressed requests and handle Gzip compressed responses using RestTemplate in a Spring Boot application.
2. Sending Gzip Compressed Requests
First, let’s set up a RestTemplate to send Gzip compressed requests. We’ll create an interceptor to compress the request body. Assume we have an existing Spring Boot application that accepts Gzip compressed requests and sends Gzip compressed responses.
Steps:
1. RestTemplate Configuration
We’ll configure a RestTemplate bean with an interceptor for compressing request bodies.
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPOutputStream;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.getInterceptors().add(gzipRequestInterceptor());
return restTemplate;
}
private ClientHttpRequestInterceptor gzipRequestInterceptor() {
return (request, body, execution) -> {
request.getHeaders().add("Content-Encoding", "gzip");
request.getHeaders().remove("Content-Length");
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzipStream = new GZIPOutputStream(byteStream)) {
gzipStream.write(body);
} catch (IOException e) {
throw new IOException("Failed to compress request body", e);
}
byte[] compressedBody = byteStream.toByteArray();
return execution.execute(request, compressedBody);
};
}
}
RestTemplateConfig.javaThe RestTemplateConfig
class configures a RestTemplate
bean with a custom interceptor that compresses the request body using Gzip.
The Content-Encoding
header in the request indicates to the server that the body is compressed using Gzip, prompting the server to decompress it before processing. The removal of the Content-Length
header is necessary because compression changes the size of the request body, making the original length inaccurate. GZIPOutputStream
is used to compress the request body by writing the original data into a stream that outputs compressed bytes, ensuring that the data is sent in a Gzip compressed format.
2. Sending a Gzip Compressed Request
Now, let’s use the configured RestTemplate to send a Gzip compressed request. We’ll create a simple controller that sends a request to another application endpoint.
For our demo purpose, we will send a Gzip compressed request to the application that we created in our previous article, “Conditionally Enabling Gzip Compression“. This app accepts Gzip compressed requests and, for our example, we have enabled Gzip compression for responses using simple Spring Boot application properties.
import com.bootcamptoprod.dto.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class GzipController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/gzipRequestResponse")
public Employee gzipRequestResponse() {
String url = "http://localhost:8080/decompress";
HttpHeaders headers = new HttpHeaders();
headers.add("Accept-Encoding", "gzip");
Employee employee = new Employee(1, "John Doe");
HttpEntity<Employee> entity = new HttpEntity<>(employee, headers);
ResponseEntity<Employee> response = restTemplate.exchange(url, HttpMethod.POST, entity, Employee.class);
response.getHeaders().forEach((s, strings) -> {
System.out.println("Header name and value: " + s + " : " + strings);
});
return response.getBody();
}
}
GzipController.javapublic class Employee {
private int id;
private String name;
// No args constructor
// All args constructor
// Getters and setters
}
Employee.java3. Checking Gzip compressed request in the destination app:
For demonstration purposes, we have added logging in the destination app where the Gzip compressed request is sent using RestTemplate
. This logging captures and displays all request headers, including Content-Encoding: gzip
, which confirms that the request body is compressed in Gzip format.
3. Handling Gzip Compressed Responses
There are two main approaches to handle Gzip compressed responses in case of Rest template: using a custom response interceptor and using the Apache HttpClient library.
3.1 Approach 1: Using a Response Interceptor
We will create a custom ClientHttpResponse
to handle Gzip decompression.
What is ClientHttpResponse?
ClientHttpResponse
is an interface in Spring’s HTTP client library that represents the response returned from an HTTP request. It provides methods to access the response’s headers, status code, and body. This interface is essential when creating custom response interceptors, as it allows you to inspect and manipulate the response before it reaches the client.
For instance, we can use ClientHttpResponse
to handle Gzip decompression by reading the compressed data from the response’s body, decompressing it, and processing the decompressed content as needed.
Steps:
1. Custom ClientHttpResponse
Implementation
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
public class GzipDecompressingClientHttpResponse implements ClientHttpResponse {
private final ClientHttpResponse response;
public GzipDecompressingClientHttpResponse(ClientHttpResponse response) {
this.response = response;
}
@Override
public InputStream getBody() throws IOException {
InputStream body = response.getBody();
if (isGzipped(response)) {
return new GZIPInputStream(new BufferedInputStream(body));
}
return body;
}
private boolean isGzipped(ClientHttpResponse response) {
String contentEncoding = response.getHeaders().getFirst("Content-Encoding");
return contentEncoding != null && contentEncoding.toLowerCase().contains("gzip");
}
@Override
public HttpStatusCode getStatusCode() throws IOException {
return response.getStatusCode();
}
@Override
public String getStatusText() throws IOException {
return response.getStatusText();
}
@Override
public void close() {
response.close();
}
@Override
public org.springframework.http.HttpHeaders getHeaders() {
return response.getHeaders();
}
}
GzipDecompressingClientHttpResponse.javaThe class GzipDecompressingClientHttpResponse
wraps an existing ClientHttpResponse
to handle Gzip decompression of the response body. When the getBody()
method is called, it checks if the response is Gzip encoded by examining the Content-Encoding
header. If the response is Gzip compressed, it wraps the input stream of the response body with a GZIPInputStream
to decompress the data; otherwise, it returns the body as-is. The class also provides standard methods for accessing the status code, status text, headers, and closing the response, all of which are delegated to the wrapped ClientHttpResponse
instance.
2. Adding the Response Interceptor
Update the RestTemplate configuration to include the response interceptor.
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RestTemplate;
import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPOutputStream;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.getInterceptors().add(gzipRequestInterceptor()); // already covered above
restTemplate.getInterceptors().add(gzipResponseInterceptor());
return restTemplate;
}
private ClientHttpRequestInterceptor gzipResponseInterceptor() {
return (request, body, execution) -> {
ClientHttpResponse response = execution.execute(request, body);
return new GzipDecompressingClientHttpResponse(response);
};
}
// gzipRequestInterceptor() remains the same as covered above
}
RestTemplateConfig.javaThe RestTemplateConfig
class adds a gzipResponseInterceptor
to the RestTemplate
configuration, which is responsible for handling GZIP compressed responses. This interceptor wraps the ClientHttpResponse
received from the server with the GzipDecompressingClientHttpResponse
class. This custom response wrapper checks if the response is Gzip compressed and, if so, uses GZIPInputStream
to decompress the response body, allowing the application to process the data in its original, uncompressed form.
Output:
When a request is sent to /gzipRequestResponse
(endpoint we created earlier), it requests a Gzip response by including the Accept-Encoding: gzip
header. The response headers, which are printed out, show that the rest. template response is indeed Gzip compressed, as indicated by the Content-Encoding: gzip
header. The output will include details such as Content-Encoding: gzip
, confirming the compression, along with other headers like Content-Type
, Transfer-Encoding
, and Date
, providing additional context about the response.
3.2 Approach 2: Using Apache HttpClient
Apache HttpClient can automatically handle Gzip responses, making it a convenient alternative. In this case, we don’t have to create or add any additional response interceptor.
What is Apache HttpClient?
Apache HttpClient is a powerful Java library for handling HTTP requests and responses, part of the Apache HttpComponents project. It simplifies making various types of HTTP requests and managing responses, supports advanced features like connection pooling, authentication, and automatic cookie handling.
Notably, it can automatically handle Gzip compressed responses, meaning it will decompress Gzip encoded data seamlessly, making it a convenient choice for applications that need to manage HTTP communication efficiently and with minimal manual handling of compression.
Steps:
1. Add Dependency
Add the Apache HttpClient dependency to your pom.xml
.
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
pom.xml2. Configuring RestTemplate with Apache HttpClient
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPOutputStream;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.getInterceptors().add(gzipRequestInterceptor()); // already covered above
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create().build()));
return restTemplate;
}
// gzipRequestInterceptor() remains the same as covered above
}
RestTemplateConfig.javaThe HttpComponentsClientHttpRequestFactory
allows RestTemplate
to utilize Apache HttpClient for making HTTP requests. This configuration enables automatic handling of features such as Gzip decompression, meaning the RestTemplate
can seamlessly manage compressed responses without additional custom interceptors for that purpose.
4. Source Code
The complete source code of the above examples can be found here.
5. Things to Consider
Here are some important considerations to keep in mind when working with gzip:
- Accept-Encoding Header: Ensure the request includes Accept-Encoding: gzip to indicate support for compressed responses.
- Content-Encoding Header: Set Content-Encoding: gzip in the request to inform the server that the request body is compressed.
- Remove Content-Length Header: Remove the Content-Length header before sending a Gzip compressed request, as it will be recalculated after compression.
- Check Content-Encoding for Responses: Verify the Content-Encoding header in the response to confirm if it’s Gzip compressed before decompression.
- Decompression: Use GZIPInputStream to properly handle and decompress the response body.
- Performance: Monitor and manage CPU and memory usage for compression and decompression, and ensure performance is maintained under load.
- Testing: Validate Gzip handling with various payloads using tools like curl and Postman to ensure correct implementation.
- Exception Handling: Implement robust handling for errors such as ZipException, IOException, and EOFException to avoid application crashes and provide clear error messages.
6. FAQs
What headers are important for Gzip compression?
For Gzip compression, the Content-Encoding and Accept-Encoding headers play crucial roles. The Content-Encoding header is used to indicate that the request or response body is compressed, with the value set to gzip. In requests, this header tells the server that the request body is compressed, while in responses, it informs the client that the response body is compressed. The Accept-Encoding header, on the other hand, is used in requests to signal to the server that the client can handle compressed responses, typically including gzip as a value.
7. Conclusion
In this article, we demonstrated how to send and handle Gzip compressed requests and responses using RestTemplate in a Spring Boot application. This can improve data transfer efficiency by reducing payload sizes.
8. Learn More
Interested in learning more?
Checkout our blog on How to Decompress Gzip Requests in Spring Boot
Add a Comment