JBoss.orgCommunity Documentation

Chapter 32. Content encoding

32.1. GZIP Compression/Decompression
32.1.1. Configuring GZIP compression / decompression
32.2. General content encoding

RESTEasy supports (though not by default - see below) GZIP decompression. If properly configured, the client framework or a JAX-RS service, upon receiving a message body with a Content-Encoding of "gzip", will automatically decompress it. The client framework can (though not by default - see below) automatically set the Accept-Encoding header to be "gzip, deflate" so you do not have to set this header yourself.

RESTEasy also supports (though not by default - see below) automatic compression. If the client framework is sending a request or the server is sending a response with the Content-Encoding header set to "gzip", RESTEasy will (if properly configured) do the compression. So that you do not have to set the Content-Encoding header directly, you can use the @org.jboss.resteasy.annotation.GZIP annotation.

@Path("/")
public interface MyProxy {

   @Consumes("application/xml")
   @PUT
   public void put(@GZIP Order order);
}

In the above example, we tag the outgoing message body, order, to be gzip compressed. You can use the same annotation to tag server responses

@Path("/")
public class MyService {

   @GET
   @Produces("application/xml")
   @GZIP
   public String getData() {...}
}

Note. Decompression carries a risk of attack from a bad actor that can package an entity that will expand greatly. Consequently, RESTEasy disables GZIP compression / decompression by default.

There are three interceptors that are relevant to GZIP compression / decompression:

  1. org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor: If the Content-Encoding header is present and has the value "gzip", GZIPDecodingInterceptor will install an InputStream that decompresses the message body.
  2. org.jboss.resteasy.plugins.interceptors.encoding.GZIPEncodingInterceptor: If the Content-Encoding header is present and has the value "gzip", GZIPEncodingInterceptor will install an OutputStream that compresses the message body.
  3. org.jboss.resteasy.plugins.interceptors.encoding.AcceptEncodingGZIPFilter: If the Accept-Encoding header does not exist, AcceptEncodingGZIPFilter will add Accept-Encoding with the value "gzip, deflate". If the Accept-Encoding header exists but does not contain "gzip", AcceptEncodingGZIPFilter will append ", gzip". Note that enabling GZIP compression / decompression does not depend on the presence of this interceptor.

If GZIP decompression is enabled, an upper limit is imposed on the number of bytes GZIPDecodingInterceptor will extract from a compressed message body. The default limit is 10,000,000, but a different value can be configured. See below.

The designation of a compressible entity by the use of the @GZIP annotation is a built in, specific instance of a more general facility supported by RESTEasy. There are three components to this facility.

  1. The annotation org.jboss.resteasy.annotations.ContentEncoding is a "meta-annotation" used on other annotations to indicate that they represent a Content-Encoding. For example, @GZIP is defined
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @ContentEncoding("gzip")
    public @interface GZIP
    {
    }
          
    The value of @ContentEncoding indicates the represented Content-Encoding. For @GZIP it is "gzip".
  2. ClientContentEncodingAnnotationFeature and ServerContentEncodingAnnotationFeature, two DynamicFeatures in package org.jboss.resteasy.plugins.interceptors, examine resource methods for annotations decorated with @ContentEncoding.
  3. For each value found in a @ContentEncoding decorated annotation on a resource method, an instance of ClientContentEncodingAnnotationFilter or ServerContentEncodingAnnotationFilter, javax.ws.rs.ext.WriterInterceptors in package org.jboss.resteasy.plugins.interceptors, is registered. They are responsible for adding an appropriate Content-Encoding header. For example, ClientContentEncodingAnnotationFilter is defined
    @ConstrainedTo(RuntimeType.CLIENT)
    @Priority(Priorities.HEADER_DECORATOR)
    public class ClientContentEncodingAnnotationFilter implements WriterInterceptor
    {
       protected String encoding;
    
       public ClientContentEncodingAnnotationFilter(String encoding)
       {
          this.encoding = encoding;
       }
    
       @Override
       public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException
       {
          context.getHeaders().putSingle(HttpHeaders.CONTENT_ENCODING, encoding);
          context.proceed();
       }
    }
          
    When it is created, ClientContentEncodingAnnotationFeature passes in the value to be used for Content-Encoding headers.

The annotation @GZIP is built into RESTEasy, but ClientContentEncodingAnnotationFeature and ServerContentEncodingAnnotationFeature will also recognize application defined annotations. For example,

   @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
   @Retention(RetentionPolicy.RUNTIME)
   @ContentEncoding("compress")
   public @interface Compress
   {
   }
   
   @Path("")
   public static class TestResource {
      
      @GET
      @Path("a")
      @Compress
      public String a() {
         return "a";
      }
   }
   

If TestResource.a() is invoked as follows

   @Test
   public void testCompress() throws Exception
   {
      ResteasyClient client = new ResteasyClientBuilder().build();
      Invocation.Builder request = client.target("http://localhost:8081/a").request();
      request.acceptEncoding("gzip,compress");
      Response response = request.get();
      System.out.println("content-encoding: "+ response.getHeaderString("Content-Encoding"));
      client.close();
   }
   

the output will be

content-encoding: compress