Search code examples
jacksonjax-rswildflygzipresteasy

Illegal character in wildfly with GZIP Encoding


I have a client that sends a request to a REST webservice using Encoding: gzip. This triggers an exception in Wildfly:

org.jboss.resteasy.spi.ReaderException: com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens


Solution

  • By default, GZIP encoding/decoding is disabled in Wildfly/RESTEasy for security reasons:

    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.

    Your server receives a zipped stream and tries to parse it as a JSON string.

    There are several ways to enable GZIP handling in Wildfly. One is to include the Encoder/Decoder classnames in a META-INF/services/javax.ws.rs.ext.Providers and amend the deployment-structure.xml file.

    Or you can add providers to your code (kotlin example below):

    import org.jboss.resteasy.plugins.interceptors.GZIPDecodingInterceptor
    import org.jboss.resteasy.plugins.interceptors.GZIPEncodingInterceptor
    import javax.ws.rs.ext.Provider
    
    @Provider class GZIPDecoder : GZIPDecodingInterceptor()
    @Provider class GZIPEncoder : GZIPEncodingInterceptor()
    

    And add RESTEasy core dependency in pom.xml:

    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-core</artifactId>
      <version>5.0.0.Final</version>
      <scope>provided</scope>
    </dependency>
    

    Or you could create your own Interceptors, for example (basic implementation, should only be used in simple cases):

    @Provider
    public class GZIPDecoder implements ReaderInterceptor {
      @Override
      public Object aroundReadFrom(ReaderInterceptorContext ctx) throws IOException, WebApplicationException {
        if (ctx.getHeaders().getOrDefault("Content-Encoding", emptyList()).contains("gzip")) {
          GZIPInputStream is = new GZIPInputStream(ctx.getInputStream());
          ctx.setInputStream(is);
        }
        return ctx.proceed();
      }
    }