Learn how to send and handle Brotli compressed requests and responses using RestTemplate in Spring Boot.
1. Introduction
In our previous articles, we covered:
- How to Use Brotli Compression in Spring Boot with Brotli4j
- How to Decompress Brotli Requests in Spring Boot with Brotli4j
In this article, we’ll continue with Brotli compression by exploring how to send Brotli compressed requests and handle Brotli compressed responses using RestTemplate
in a Spring Boot application.
What Will You Learn?
- How to configure
RestTemplate
to send Brotli compressed requests. - How to handle Brotli compressed responses received from external services.
2. Setting up the Brotli Compression Library
To use Brotli compression and decompression, we’ll need to add the Brotli4j library dependency in our pom.xml. The latest version of the dependency can be obtained from here.
<dependency>
<groupId>com.aayushatharva.brotli4j</groupId>
<artifactId>brotli4j</artifactId>
<version>1.17.0</version>
</dependency>
pom.xmlThis dependency allows us to easily compress and decompress data using Brotli encoding. Once added, we can start implementing Brotli compression for our requests and responses.
3. Creating the Mock Controller for Brotli Compression Testing
To test our Brotli compression with RestTemplate
, we’ll create a mock controller called MockBrotliController. This controller simulates a server endpoint that expects Brotli compressed request bodies and responds with Brotli compressed data.
import com.aayushatharva.brotli4j.decoder.Decoder;
import com.aayushatharva.brotli4j.encoder.Encoder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
public class MockBrotliController {
@PostMapping(value = "/mock/brotli-request-response")
public ResponseEntity<byte[]> processCompressedData(@RequestBody byte[] compressedRequestBody, @RequestHeader HttpHeaders headers) throws IOException {
// Step 1: Decompress the Brotli request body
byte[] decompressedBody = Decoder.decompress(compressedRequestBody).getDecompressedData();
// Here, you would typically deserialize the decompressed body into a model (e.g., Employee)
// For simplicity, we'll just print out the decompressed string data
String decompressedRequest = new String(decompressedBody);
System.out.println("Decompressed Request: " + decompressedRequest);
// Step 2: Compress the response body using Brotli
byte[] compressedResponseBody = Encoder.compress(decompressedRequest.getBytes());
// Step 3: Return Brotli-compressed response
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Encoding", "br"); // Set Brotli encoding
return ResponseEntity.ok()
.headers(responseHeaders)
.body(compressedResponseBody);
}
}
MockBrotliController.javaExplanation:
- Decompressing Request Data: When a Brotli compressed request arrives, the
Decoder.decompress()
method decompresses the data. - Compressing Response Data: The response data is compressed back using
Encoder.compress()
to Brotli format, and the response header is set toContent-Encoding: br
.
4. Configuring RestTemplate for Brotli Compression
To send Brotli compressed requests, we configure a Brotli request interceptor for RestTemplate
. This interceptor compresses the request body with Brotli encoding and sets the appropriate headers.
Code for Brotli Request Interceptor in RestTemplateConfig
:
import com.aayushatharva.brotli4j.encoder.Encoder;
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;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.getInterceptors().add(brotliRequestInterceptor());
return restTemplate;
}
private ClientHttpRequestInterceptor brotliRequestInterceptor() {
return (request, body, execution) -> {
request.getHeaders().remove("Content-Length");
// Compress the request body using Brotli
byte[] compressedBody = Encoder.compress(body);
// Set the Content-Encoding header to 'br'
request.getHeaders().set("Content-Encoding", "br");
// Proceed with the compressed request
return execution.execute(request, compressedBody);
};
}
}
RestTemplateConfig.javaExplanation:
- Removing Content-Length Header: The
Content-Length
header is removed because the compressed body will have a different length than the original. - Compressing the Body: The interceptor uses
Encoder.compress()
to convert the request body to Brotli format. - Setting Headers: The
Content-Encoding
header is set tobr
, indicating Brotli encoding.
With this interceptor, RestTemplate
will automatically compress request bodies using Brotli whenever this configuration is used.
5. Configuring RestTemplate to Handle Brotli Compressed Responses
To handle Brotli compressed responses, we configure a Brotli response interceptor for RestTemplate
. This interceptor uses a custom ClientHttpResponse
class to decompress Brotli responses.
Code for Brotli Response Interceptor in RestTemplateConfig
:
import com.aayushatharva.brotli4j.encoder.Encoder;
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;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.getInterceptors().add(brotliRequestInterceptor()); // Already covered above
restTemplate.getInterceptors().add(brotliResponseInterceptor());
return restTemplate;
}
public ClientHttpRequestInterceptor brotliResponseInterceptor() {
return (request, body, execution) -> {
ClientHttpResponse response = execution.execute(request, body);
return new BrotliDecompressingClientHttpResponse(response);
};
}
}
RestTemplateConfig.javaWhat 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 Brotli decompression by reading the compressed data from the response’s body, decompressing it, and processing the decompressed content as needed.
Code for BrotliDecompressingClientHttpResponse
:
import com.aayushatharva.brotli4j.decoder.Decoder;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
public class BrotliDecompressingClientHttpResponse implements ClientHttpResponse {
private final ClientHttpResponse response;
private byte[] decompressedBody;
public BrotliDecompressingClientHttpResponse(ClientHttpResponse response) throws IOException {
this.response = response;
// Check if the response has 'Content-Encoding: br' header
if (isBrotliEncoded(response)) {
// Decompress the Brotli response body
this.decompressedBody = Decoder.decompress(response.getBody().readAllBytes()).getDecompressedData();
} else {
// If not Brotli encoded, just pass through the original response body
this.decompressedBody = response.getBody().readAllBytes();
}
}
@Override
public InputStream getBody() {
return new ByteArrayInputStream(decompressedBody);
}
@Override
public org.springframework.http.HttpHeaders getHeaders() {
return response.getHeaders();
}
@Override
public HttpStatusCode getStatusCode() throws IOException {
return response.getStatusCode();
}
@Override
public String getStatusText() throws IOException {
return response.getStatusText();
}
@Override
public void close() {
response.close();
}
private boolean isBrotliEncoded(ClientHttpResponse response) {
return response.getHeaders().getFirst("Content-Encoding") != null &&
response.getHeaders().getFirst("Content-Encoding").equalsIgnoreCase("br");
}
}
BrotliDecompressingClientHttpResponse.javaExplanation:
- Checking for Brotli Encoding: The response interceptor uses
BrotliDecompressingClientHttpResponse
to check ifContent-Encoding
is set tobr
. If it is, the response is Brotli compressed. - Decompressing the Body: The response body is decompressed using
Decoder.decompress()
and stored for easy access. - Returning Decompressed Data: The
getBody()
method returns the decompressed response body as a stream, ready for further processing.
5. Testing Changes with Test Controller
Finally, let’s test our Brotli compression and decompression with a controller called BrotliTestController. This controller uses the configured RestTemplate
to send a Brotli compressed requests and handle a Brotli compressed responses.
Code for BrotliTestController:
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 BrotliTestController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/brotli-rest-template-example")
public Employee gzipRequestResponse() {
String url = "http://localhost:8080/mock/brotli-request-response";
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Encoding", "br");
headers.add("Accept-Encoding", "br");
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);
System.out.println("Rest template response headers");
response.getHeaders().forEach((s, strings) -> {
System.out.println("Header name and value: " + s + " : " + strings);
});
return response.getBody();
}
}
BrotliTestController.javaExplanation:
- Brotli Compressed Requests: The controller sends a Brotli compressed requests to the mock endpoint.
- Handling Compressed Response: The response body is decompressed by the
BrotliDecompressingClientHttpResponse
, demonstrating both Brotli request and response handling.
6. Understanding the Flow and Output
When we access the /brotli/rest-template-test
endpoint, BrotliTestController
sends a Brotli compressed request via Rest Template to the /mock/brotli-test
endpoint on MockBrotliController
. Here’s what happens in each step:
- Request Compression:
- The
BrotliRequestInterceptor
intercepts the outgoing request fromBrotliTestController
Rest Template and compress the request body. - It adds the
Content-Encoding: br
header, informing the server that the body is Brotli compressed.
- The
- Response Decompression:
- When the
MockBrotliController
receives this request, it checks forContent-Encoding: br
. - The controller decompresses the request body, processes it, and then compresses the response body back to Brotli format.
- The response is sent with
Content-Encoding: br
, signaling that the response body is Brotli compressed.
- When the
- Handling the Response:
- When the Brotli compressed response is received by
BrotliTestController
Rest Template, theBrotliDecompressingClientHttpResponse
decompresses the response body. - Any Brotli related headers in the response, such as
Content-Encoding: br
, are accessible and printed in the console for verification.
- When the Brotli compressed response is received by
Output:
When the /brotli/rest-template-test
endpoint is accessed, you’ll see the following output in the console:
Rest template response headers
Header name and value: Content-Encoding : [br]
Header name and value: Content-Type : [application/json]
Header name and value: Content-Length : [30]
Header name and value: Date : [Fri, 25 Oct 2024 20:48:57 GMT]
Header name and value: Keep-Alive : [timeout=60]
Header name and value: Connection : [keep-alive]
ConsoleExplanation of the Console Output
- Content-Encoding: br: This header indicates that the response was Brotli compressed.
- Date and Content-Type: These are typical headers showing the date of the response and the response content type.
The output confirms that the RestTemplate
successfully handled both Brotli compression for outgoing requests and Brotli decompression for incoming responses.
7. Source Code
The complete source code of the above example can be found here.
8. Things to Consider
Here are some important considerations to keep in mind while working with Brotli:
- Compatibility with Client and Server: Ensure that both the client and server support Brotli compression. Verify that any external services you communicate with can handle
Content-Encoding: br
. - Error Handling for Decompression Failures: Implement graceful error handling for decompression failures due to unexpected or corrupted Brotli-encoded data. Ensure fallback mechanisms are in place, such as retrying without compression or logging errors for troubleshooting.
- Monitoring and Logging: Implement logging for Brotli compression and decompression processes. This helps in troubleshooting issues related to data transmission and provides insights into the effectiveness of compression.
- Cache Management: Consider how Brotli-compressed responses are cached. Ensure that your caching strategy accounts for the
Content-Encoding
header, as compressed responses may require different caching policies.
9. FAQs
How do I send Brotli compressed requests using RestTemplate in Spring Boot?
To send Brotli compressed requests, you need to configure a custom interceptor in your RestTemplate that compresses the request body using the Brotli algorithm before sending it to the server.
How can I handle Brotli compressed responses using RestTemplate in Spring Boot?
To handle Brotli compressed responses with RestTemplate, you need to implement a custom ClientHttpResponse interceptor. This interceptor will check the Content-Encoding header of the response. If it indicates Brotli compression, the interceptor will decompress the response body using the Brotli algorithm before passing it back to the calling code. This ensures that your application can seamlessly work with Brotli compressed responses without additional effort from the client-side.
10. Conclusion
With this setup, you can efficiently send and handle Brotli compressed requests and responses in Spring Boot using RestTemplate. This approach provides a streamlined way to reduce data transfer sizes for applications dealing with large payloads or limited bandwidth.
11. Learn More
Interested in learning more?
Checkout our blog on How to Enable Gzip Compression in Spring Boot
Add a Comment