-
Bug
-
Resolution: Done
-
Critical
-
4.0.0.Beta1
1st case
Let's considerer following SSE resource:
@Path("/server-sent-events-leak") public class ResourceLeakSseResource { private final Object outputLock = new Object(); private volatile SseEventSink eventSink; @GET @Produces(MediaType.SERVER_SENT_EVENTS) public void getMessageQueue(@Context SseEventSink eventSink, @Context Sse s) { synchronized (this.outputLock) { if (this.eventSink != null) { throw new IllegalStateException("Server sink already served."); } } this.eventSink = eventSink; this.eventSink.send(s.newEvent("data")); throw new RuntimeException(); } @DELETE public void close() { synchronized (this.outputLock) { if (this.eventSink != null) { this.eventSink.close(); this.eventSink = null; } } } }
- Running on a server that does not support async
When calling the getMessageQueue(...) resource the server will send back to the client a 200 response followed by the "data" SseEvent. Then on throwing the RuntimeException
resteasy core methods org.jboss.resteasy.core.SynchronousExecutionContext.SynchronousAsynchronousResponse.resume(Throwable exc) will be invoked. This method is supposed to countdown the latch on complete but since the HttpResponse as already been commited (which is a good thing) when sending previous 200 response, the latch will never be counted down. See org.jboss.resteasy.core.SynchronousDispatcher.writeException(...):
if (response.isCommitted()) { LogMessages.LOGGER.debug(Messages.MESSAGES.responseIsCommitted()); return; }
So the state of the current org.jboss.resteasy.core.SynchronousExecutionContext.SynchronousAsynchronousResponse will be now "done" and future invocation by another thread of whichever method supposed to counted down the latch will be useless since the state is already set to "done".
So as a result the calling thread will wait forever on method org.jboss.resteasy.core.SynchronousDispatcher.invoke(HttpRequest request, HttpResponse response, ResourceInvoker invoker):
public void invoke(HttpRequest request, HttpResponse response, ResourceInvoker invoker) { Response jaxrsResponse = null; try { jaxrsResponse = invoker.invoke(request, response); if (request.getAsyncContext().isSuspended()) { /** * Callback by the initial calling thread. This callback will probably do nothing in an asynchronous environment * but will be used to simulate AsynchronousResponse in vanilla Servlet containers that do not support * asychronous HTTP. * */ request.getAsyncContext().getAsyncResponse().initialRequestThreadFinished(); jaxrsResponse = null; // we're handing response asynchronously } } ... }
- Running on a server that support async
The same will occur leading us to javax.servlet.AsyncContext.complete() never invoked and maybe the connection and resources associated to the async execution never released.
For this case see org.jboss.resteasy.plugins.server.servlet.Servlet3ExecutionContext.Servle3AsychronousResponse.resume(Throwable) and org.jboss.resteasy.plugins.server.netty.NettyHttpRequest.NettyExecutionContext.NettyHttpAsyncResponse.resume(Throwable)
2nd case
Let's considerer following SSE resource:
@Path("async") @GET @Produces(MediaType.APPLICATION_JSON) public void async(final @Suspended AsyncResponse async) { async.cancel(); }
- Running on a server that does not support async
Just like the previous case a simple call to this resource will lead to a thread waiting forever on the same exact line of org.jboss.resteasy.core.SynchronousDispatcher.invoke(HttpRequest request, HttpResponse response, ResourceInvoker invoker) since org.jboss.resteasy.core.SynchronousExecutionContext.SynchronousAsynchronousResponse.cancel(..) does not counted down the latch.
- is caused by
-
RESTEASY-1701 Extend server-side async support / RxJava integration
- Closed