Uploaded image for project: 'JBoss Enterprise Application Platform'
  1. JBoss Enterprise Application Platform
  2. JBEAP-19246

[GSS](7.2.z) UNDERTOW-1683 - UT000146 is improperly thrown

XMLWordPrintable

      Customer is moving a spring app from netty to EAP 7 and it hits a new UT000146 exception on EAP 7. They provided a test app that reproduces and this shows spring starts an async request:

      2020-04-14 12:10:09,992 INFO  [stdout] (default task-1) --------------------------->HttpServerExchange.dispatch HttpServerExchange{ POST /testApp/api/v1.0/test} io.undertow.util.SameThreadExecutor@514a0839 io.undertow.servlet.spec.AsyncContextImpl$1@3204dd1c
      2020-04-14 12:10:09,992 INFO  [stdout] (default task-1) io.undertow.server.HttpServerExchange.dispatch(HttpServerExchange.java:-1)
      2020-04-14 12:10:09,992 INFO  [stdout] (default task-1) io.undertow.servlet.spec.AsyncContextImpl.<init>(AsyncContextImpl.java:109)
      2020-04-14 12:10:09,992 INFO  [stdout] (default task-1) io.undertow.servlet.spec.HttpServletRequestImpl.startAsync(HttpServletRequestImpl.java:1036)
      2020-04-14 12:10:09,992 INFO  [stdout] (default task-1) org.springframework.http.server.reactive.ServletHttpHandlerAdapter.service(ServletHttpHandlerAdapter.java:166)
      

      And then right after adds a read listener to the ServletInputStream:

      2020-04-14 12:10:10,079 INFO  [stdout] (default task-1) --------------------------->ServletInputStream.setReadListener org.springframework.http.server.reactive.ServletServerHttpRequest$RequestBodyPublisher$RequestBodyPublisherReadListener@e40187
      2020-04-14 12:10:10,079 INFO  [stdout] (default task-1) io.undertow.servlet.spec.ServletInputStreamImpl.setReadListener(ServletInputStreamImpl.java:140)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) org.springframework.http.server.reactive.ServletServerHttpRequest$RequestBodyPublisher.registerReadListener(ServletServerHttpRequest.java:282)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) org.springframework.http.server.reactive.ServletServerHttpRequest.<init>(ServletServerHttpRequest.java:98)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) org.springframework.http.server.reactive.ServletServerHttpRequest.<init>(ServletServerHttpRequest.java:77)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) org.springframework.http.server.reactive.ServletHttpHandlerAdapter.createRequest(ServletHttpHandlerAdapter.java:200)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) org.springframework.http.server.reactive.ServletHttpHandlerAdapter.service(ServletHttpHandlerAdapter.java:171)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
      2020-04-14 12:10:10,080 INFO  [stdout] (default task-1) io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
      

      It then lets the request go to more reactor.core application code, resulting in a ServletInputStreamImpl.isReady call. Undertow throws this exception as a result since it treats this as a resume after a dispatch within the same request cycle since the app never let the request processing fall out to some actual async processing:

      2020-04-14 12:10:10,238 ERROR [org.springframework.web.server.adapter.HttpWebHandlerAdapter] (default task-1) [42d4430f] 500 Server Error for HTTP POST "/testApp/api/v1.0/test": java.lang.IllegalStateException: UT000146: HttpServerExchange cannot have both async IO resumed and dispatch() called in the same cycle
      	at io.undertow.server.HttpServerExchange$ReadDispatchChannel.resumeReads(HttpServerExchange.java:2142)
      	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
      Error has been observed at the following site(s):
      	|_ checkpoint ⇢ HTTP POST "/testApp/api/v1.0/test" [ExceptionHandlingWebHandler]
      Stack trace:
      		at io.undertow.server.HttpServerExchange$ReadDispatchChannel.resumeReads(HttpServerExchange.java:2142)
      		at io.undertow.servlet.spec.ServletInputStreamImpl.isReady(ServletInputStreamImpl.java:104)
      		at org.springframework.http.server.reactive.ServletServerHttpRequest$RequestBodyPublisher.checkOnDataAvailable(ServletServerHttpRequest.java:287)
      		at org.springframework.http.server.reactive.AbstractListenerReadPublisher.changeToDemandState(AbstractListenerReadPublisher.java:222)
      		at org.springframework.http.server.reactive.AbstractListenerReadPublisher.access$1000(AbstractListenerReadPublisher.java:48)
      		at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State$2.request(AbstractListenerReadPublisher.java:333)
      		at org.springframework.http.server.reactive.AbstractListenerReadPublisher$ReadSubscription.request(AbstractListenerReadPublisher.java:260)
      		at reactor.core.publisher.MonoCollect$CollectSubscriber.onSubscribe(MonoCollect.java:120)
      		at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State$1.subscribe(AbstractListenerReadPublisher.java:301)
      		at org.springframework.http.server.reactive.AbstractListenerReadPublisher.subscribe(AbstractListenerReadPublisher.java:105)
      		at reactor.core.publisher.FluxSource.subscribe(FluxSource.java:58)
      

      So seeing that, I could boil this down to a much simpler async servlet implementation that reproduces this with just those 3 noted operations:

          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              AsyncContext asyncCtx = req.startAsync();
              req.getInputStream().setReadListener(new MyReadListener(asyncCtx, resp, req.getInputStream()));
              System.out.println(req.getInputStream().isReady());
          }
      

      That reproducer is attached for your reference. After deploying the helloworld.war, it can be tested like so:

      curl -v -X POST -d 'test' localhost:8080/helloworld/HelloWorld
      

      Testing elsewhere like on Tomcat 9, I note no issue as well. So is Undertow incorrect to force this error?

              rhn-support-ivassile Ilia Vassilev
              rhn-support-aogburn Aaron Ogburn
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

                Created:
                Updated:
                Resolved: