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.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.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.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 interceptors may be enabled by including their classnames in a META-INF/services/javax.ws.rs.ext.Providers file on the classpath. The upper limit on deflated files may be configured by setting the parameter "resteasy.gzip.max.input". [See Section 3.4, “Configuration” for more information about application configuration.] If the limit is exceeded on the server side, GZIPDecodingInterceptor will return a Response with status 413 ("Request Entity Too Large") and a message specifying the upper limit.

Note. As of release 3.1.0.Final, the GZIP interceptors have moved from package org.jboss.resteasy.plugins.interceptors.encoding to org.jboss.resteasy.plugins.interceptors. and they should be named accordingly in javax.ws.rs.ext.Providers.

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
   {
      Client client = ClientBuilder.newClient();
      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