Bug 1416472 - GZIPDecodingInterceptor may be registered twice when using JAX-RS and RESTEasy client on the same application
Summary: GZIPDecodingInterceptor may be registered twice when using JAX-RS and RESTEas...
Keywords:
Status: CLOSED DEFERRED
Alias: None
Product: JBoss Enterprise Application Platform 6
Classification: JBoss
Component: RESTEasy
Version: 6.4.11
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
: EAP 6.4.14
Assignee: Peter Palaga
QA Contact: Katerina Odabasi
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2017-01-25 15:20 UTC by William Antônio
Modified: 2020-02-14 18:30 UTC (History)
5 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2017-02-16 16:26:31 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Issue Tracker JBEAP-8428 0 Major Verified [GSS](7.1.0) GZIPDecodingInterceptor may be registered twice when using JAX-RS and REsteasy client on the same applicati... 2017-11-16 18:39:03 UTC

Description William Antônio 2017-01-25 15:20:21 UTC
Description of problem:

If we have a web application that has a JAX-RS resource and another interface not related to JAX-RS (servlet or a JAX-WS web service, for example), but using resteasy client, we might have an issue where:

    If the JAX-RS endpoint is acessed and it is doing the providers registration;
    At the same time, the other endpoint that uses the client request API is accessed;
    The GZIPDecodingInterceptor will be registered twice

The consequence is that two GZIPDecodingInterceptor will try to parse the GZIP requests corrupting the gzipinputstream and leading to the following error everytime we try to use the client again:

Caused by: java.util.zip.ZipException: Not in GZIP format

    at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:164) [rt.jar:1.7.0_97]

    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:78) [rt.jar:1.7.0_97]

    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:90) [rt.jar:1.7.0_97]

    at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor$FinishableGZIPInputStream.<init>(GZIPDecodingInterceptor.java:30)

    at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.read(GZIPDecodingInterceptor.java:47)

    at org.jboss.resteasy.core.interception.MessageBodyReaderContextImpl.proceed(MessageBodyReaderContextImpl.java:109)

    at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.read(GZIPDecodingInterceptor.java:51)

    at org.jboss.resteasy.core.interception.MessageBodyReaderContextImpl.proceed(MessageBodyReaderContextImpl.java:109)

The issue, as greatly found by our colleague Dennis, is that the client request will try to create a wrapped provider during the other providerfactory provider registration. which means that the following synchronized code block from RegisterBuiltin class won't protect us:

public class RegisterBuiltin
{

   public static void register(ResteasyProviderFactory factory)
   {
      synchronized (factory)
      {
         if (factory.isBuiltinsRegistered() || !factory.isRegisterBuiltins()) return;
         try
         {
            registerProviders(factory);
         }
         catch (Exception e)
         {
            throw new RuntimeException(e);
         }
         factory.setBuiltinsRegistered(true);
      }
   }
//....

Version-Release number of selected component (if applicable):


How reproducible:
In "real world" it is really hard to reproduce this, but we can force a stop at the mentioned point to reproduce it;

Steps to Reproduce:
1.Build the attached application;
2. Deploy the attached application in JBoss EAP 6.4.7 with some mechanism to stop the server execution at RegisterBuiltin::register method (I used a debugger, but byteman can be used as well);
3. Access the REST endpoint: **http://localhost:8080/resteasy-provider-threadsafe/servlet**
4. While the thread is stuck at the provider registration, access the servlet that uses the resteasy client library: **http://localhost:8080/resteasy-provider-threadsafe/servlet**

You should see the error

Actual results:

Exception every time when a new request is made;

Expected results:
Not exception.

Additional info:

The reproducer is here:

https://github.com/jesuino/reproducers/tree/master/JBEAP-8428resteasy-provider-threadsafe

I would like also to add that:

    This is not reproducible on EAP 7.0.3 - the reason is that resteasy seems to be started eagerly by default I can't access the servlet when I have the breakpoint on register method
    There's a fixed JIRA that will probably fix this as well - the issue was fixed on EAP 7.0.1, however, I didn't have the change to backport it:

    https://issues.jboss.org/browse/JBEAP-4703

Notice that even if JBEAP-4703 fix the problem, our main issue here is that the provider factory is not thread safe in this circumstance. If you say that the fix for JBEAP-4703 is okay to fix it, we can simply backport it to 6.x branches.

Thanks!

Comment 1 Peter Palaga 2017-02-16 13:50:07 UTC
As noted in the upstream Jira https://issues.jboss.org/browse/JBEAP-8428 , this issue has a workaround that consists in:

1. Making the app get initialized eagerly through adding ResteasyBootstrap listener to the app's web.xml:

<web-app>
  <listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
  </listener>
</web-app>

2. Making the web subsystem to postpone opening the connectors through setting the org.apache.catalina.connector.WAIT_FOR_BEFORE_START system property to the context path of the given application. This can be done either on command line

bin/standalone.sh -Dorg.apache.catalina.connector.WAIT_FOR_BEFORE_START=/my-app1,/my-app2

or in standalone.xml 

<system-properties>
  <property name="org.apache.catalina.connector.WAIT_FOR_BEFORE_START" value="/my-app1,/my-app2"/>
</system-properties>

or via cli: 
bin/jboss-cli.sh --connect command="/system-property=org.apache.catalina.connector.WAIT_FOR_BEFORE_START:add(value=/my-app1,/my-app2)"

Comment 4 Brad Maxwell 2017-02-16 16:27:04 UTC
Closing as there is a workaround, we will fix this upstream


Note You need to log in before you can comment on or make changes to this bug.