Brotli Compression in Spring Boot

How to Use Brotli Compression in Spring Boot with Brotli4j

Discover how to use Brotli compression in Spring Boot with Brotli4j. This beginner-friendly guide covers everything from setup to practical examples, ensuring your Spring Boot app performs optimally with Brotli compression.

1. Introduction

In modern web applications, compressing responses is essential for enhancing performance by reducing payload size and improving load times. We’ve previously covered Gzip compression in Spring Boot, which is widely used for similar purposes. If you’re interested in exploring Gzip compression, you can check out the following articles:

While Gzip is a popular choice, Brotli is an advanced compression algorithm offering superior compression rates, making it a great alternative for web applications. In this article, we’ll dive into how you can use Brotli in your Spring Boot app with the help of the Brotli4j library. We’ll also cover what Brotli is and how to configure your application to send Brotli-compressed responses.



2. What is Brotli Compression?

Brotli is a modern lossless compression algorithm developed by Google, optimized for compressing web assets like HTML, CSS, and JavaScript. Brotli offers better compression ratios compared to Gzip, making it an excellent choice for reducing response sizes and improving loading times for users. Major browsers and web servers, including Apache and NGINX, support Brotli.

3. What is Brotli4j?

Brotli4j is a Java library that provides support for Brotli compression and decompression. It’s easy to integrate into Spring Boot applications and helps in sending Brotli-compressed responses to clients. This library supports Brotli’s various compression levels and can be used to compress or decompress data streams, files, or HTTP responses in Spring Boot applications.

4. Does Spring Boot Support Brotli Compression Out of the Box?

Spring Boot provides native support for Gzip compression, but it doesn’t have built-in support for Brotli compression as of now. However, we can leverage Brotli4j to manually configure Brotli compression in a Spring Boot application.



5. How to Send Brotli Compressed Responses in Spring Boot?

To implement Brotli compression in a Spring Boot application, we can use a filter to handle the compression process. The filter checks if the client supports Brotli compression by inspecting the Accept-Encoding header and, if supported, compresses the response accordingly.

Let’s look at how to set up Brotli compression in a Spring Boot application using a filter.

Step 1: Add the Brotli4j Maven Dependency

First, you need to add the brotli4j dependency to your pom.xml file. 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>
application.properties

This library provides the necessary utilities to compress data using Brotli.

Step 2: Load Brotli4j in the Startup Class

In your main application class, ensure that the Brotli4j native library is loaded. You can do this by using the Brotli4jLoader.ensureAvailability() method in the CommandLineRunner interface.

import com.aayushatharva.brotli4j.Brotli4jLoader;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootBrotliCompressionApplication implements CommandLineRunner {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootBrotliCompressionApplication.class, args);
	}

	@Override
	public void run(String... args) {
		// Load the native library
		Brotli4jLoader.ensureAvailability();
	}
}
SpringBootBrotliCompressionApplication.java

This ensures that Brotli4j is ready to be used for Brotli compression.

Step 3: Create a Simple Controller that returns Large Response

To test Brotli compression, let’s create a simple controller that returns a large string response. This will allow us to see the effect of compression more clearly.

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class BrotliController {

    @GetMapping("/large-response")
    public String getLargeResponse() {
        return "Hello, World! ".repeat(1000000);
    }
}
BrotliController.java

This endpoint returns a large text response that will be compressed using Brotli.



Step 4: Using Filters for Brotli Compression in Spring Boot

Filters in Spring Boot can intercept requests and responses before they are handled by the controller. Let’s create a custom filter that compresses the response using Brotli when the client supports it.

a. Create a Brotli Compression Filter

The BrotliCompressionFilter is a custom filter that checks if the client supports Brotli compression by inspecting the Accept-Encoding HTTP header. If the client supports Brotli (i.e., the header contains “br”), the filter captures the response, compresses it using Brotli, and then sends the compressed response back to the client.

import com.aayushatharva.brotli4j.encoder.Encoder;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Order(1)
@Component
public class BrotliCompressionFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // Check if Brotli compression is supported by the client
        String acceptEncoding = httpRequest.getHeader("Accept-Encoding");
        if (acceptEncoding != null && acceptEncoding.contains("br")) {
            System.out.println("Brotli compression request received");
            // Use a custom wrapper to capture the response content
            BrotliHttpServletResponseWrapper wrappedResponse = new BrotliHttpServletResponseWrapper(httpResponse);

            // Proceed with the filter chain
            chain.doFilter(request, wrappedResponse);

            System.out.println("Initiating response compression");
            // Compress the captured response content with Brotli
            byte[] uncompressedData = wrappedResponse.getResponseData();
            byte[] brotliCompressedData = Encoder.compress(uncompressedData);

            // Modify response headers
            httpResponse.setHeader("Content-Encoding", "br");
            httpResponse.setContentLength(brotliCompressedData.length);

            // Write the compressed response back to the output stream
            httpResponse.getOutputStream().write(brotliCompressedData);

        } else {
            // Proceed without Brotli compression if not supported
            chain.doFilter(request, response);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }
}
BrotliCompressionFilter.java

In Spring Boot, filters are automatically registered if annotated with @Component. The @Order(1) annotation specifies the order in which the filter is applied. In this case, it will run before any other filters.

b. Create the BrotliHttpServletResponseWrapper

The BrotliHttpServletResponseWrapper class is a custom implementation of HttpServletResponseWrapper. It acts as a buffer to capture the response content before it’s written to the output stream. This is essential for applying compression after the response has been generated.

import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

public class BrotliHttpServletResponseWrapper extends HttpServletResponseWrapper {

    private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    private final PrintWriter printWriter = new PrintWriter(outputStream);

    public BrotliHttpServletResponseWrapper(HttpServletResponse response) {
        super(response);
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new ServletOutputStream() {
            @Override
            public boolean isReady() {
                // Indicate whether the stream is ready to be written to.
                return true; // Changed to true for better compatibility
            }

            @Override
            public void setWriteListener(WriteListener writeListener) {
                // No-op for synchronous processing
            }

            @Override
            public void write(int b) throws IOException {
                outputStream.write(b); // Write data to buffer
            }
        };
    }

    @Override
    public PrintWriter getWriter() {
        return printWriter; // Use PrintWriter to capture text data
    }

    public byte[] getResponseData() {
        printWriter.flush(); // Ensure all data is written to the buffer
        return outputStream.toByteArray(); // Return buffered response data
    }
}
BrotliHttpServletResponseWrapper.java

It enables capturing the uncompressed response content, which can be compressed and sent to the client later. Without this wrapper, the response would be written directly to the output stream, making it impossible to compress the data after it’s been written.



6. Testing Our Spring Boot Application with Brotli Compression

To test the Brotli compression, start your Spring Boot application and make a GET request to the /large-response endpoint. You can verify the response time, response size, and the Content-Encoding header using Postman.

  1. Open Postman and create a new GET request to http://localhost:8080/large-response.
  2. In the request headers, add Accept-Encoding with the value br to indicate that the client supports Brotli compression.
  3. Send the request.

After you receive the response, check the following:

  • Response Time: The duration it took to complete the request.
  • Response Size: The size of the response payload.
  • Content-Encoding: This should be set to br if Brotli compression has been successfully applied.

Important Note on Postman Version

Ensure you are using Postman version 11.8 or higher, as this version provides detailed insights about compressed and uncompressed size breakdowns.

Additionally, if you want to test the same using a browser’s network tab, you can refer to a similar approach mentioned here.

Output:

Compare response sizes in screenshots with and without Brotli compression enabled. Verify the presence of the ‘Content-Encoding: br’ header in the ‘Response Headers’ section to confirm successful compression.



7. Why You Need Brotli4jLoader.ensureAvailability()?

The Brotli4jLoader.ensureAvailability() method is crucial for loading the native Brotli libraries in your application. If this method is not called, you may encounter the java.lang.UnsatisfiedLinkError error during runtime. This error indicates that the necessary native code for Brotli compression is not available, leading to failure in performing compression operations.

Output:



8. Setting Brotli Compression Levels

You can adjust the Brotli compression level using the Encoder.Parameters.setQuality() method, which can be specified as a parameter in the Encoder.compress() method. Here’s how to implement it:

....
  byte[] brotliCompressedData = Encoder.compress(uncompressedData, Encoder.Parameters.DEFAULT.setQuality(11));
....
    
BrotliCompressionFilter.java

The compression level can range from 0 to 11, where lower levels provide faster response times but with less compression, while higher levels achieve better compression at the cost of slower response times. The acceptable values are from 0 to 11, or -1 for default compression settings.

If you provide a value outside this range, you will encounter an IllegalArgumentException with the message: quality should be in range [0, 11], or -1. This ensures that the compression process only uses valid quality levels.

9. Source Code

The complete source code of the above example can be found here.



10. Things to Consider

Here are some important considerations to keep in mind while working with Brotli compression in Spring Boot:

  • Browser Support: Ensure that clients (browsers or other services) requesting resources support Brotli compression. Most modern browsers, like Chrome, Firefox, and Edge, support Brotli, but it’s good to check compatibility with your target audience.
  • Performance Impact: Brotli compression can be more CPU-intensive than other algorithms (like Gzip) due to its complex compression algorithm. Monitor the performance impact on your server, especially under heavy load, to ensure that compression does not lead to significant latency.
  • Compression Levels: Brotli allows you to configure different compression levels (0-11). Higher levels provide better compression ratios but at the cost of increased CPU usage. Choose an appropriate compression level based on your application’s needs and server capabilities.
  • Testing and Validation: Use tools like Postman or browser developer tools to verify the Content-Encoding header and check the response size.
  • Fallback Mechanism: Implement a fallback mechanism for clients that do not support Brotli compression. This could involve checking the Accept-Encoding header and serving uncompressed or Gzip-compressed responses as a backup.
  • Static Content: If your application serves a lot of static content (e.g., images, CSS, JavaScript), consider pre-compressing these assets with Brotli and serving them directly. This can significantly reduce the load time for static resources.
  • Memory Usage: Brotli’s compression may require more memory than other algorithms, particularly when using higher compression levels. Keep an eye on memory usage to prevent potential out-of-memory errors.

11. FAQs

How do I know if my client supports Brotli compression?

Can Brotli compression slow down my application?

What happens if Brotli is not enabled in my Spring Boot application?

What does Brotli compression quality level mean, and how do I choose the right one?

How can I verify if Brotli compression is working in my Spring Boot app?



12. Conclusion

In conclusion, Brotli compression significantly reduce response sizes in your applications, leading to faster load times and improved performance. By integrating Brotli4j and configuring a simple filter, you can enable Brotli compression in Spring Boot seamlessly for large responses, ensuring optimal user experience. While Brotli provides excellent compression rates, it’s essential to balance the compression level with response speed based on your application’s needs. With the setup discussed in this article, you can start leveraging Brotli compression effectively in your Spring Boot app.

13. Learn More

#

Interested in learning more?

Checkout our blog on How to Print SQL Queries with Values in Spring Boot with Spring Data JPA



Add a Comment

Your email address will not be published.