Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cancelling a request with a chunked response results in IllegalStateException: UT000127: Response has already been sent #47005

Open
jin-harmoney opened this issue Mar 26, 2025 · 0 comments
Labels
area/undertow kind/bug Something isn't working

Comments

@jin-harmoney
Copy link

jin-harmoney commented Mar 26, 2025

Describe the bug

When an api requests is cancelled by the client (for example due to a browser refresh), the following error is thrown in Quarkus:

java.lang.IllegalStateException: UT010019: Response already commited

This happens with quarkus-resteasy-jackson and quarkus-undertow, but only when the response is a chunked response (see reproducer).

I'm assuming Quarkus is trying to send the second chunk even though the response was already closed/sent when sending the first chunk failed.

Expected behavior

Exception and stacktrace is not expected in the logs. Trying to respond to a closed connection should not result in any errors.

Actual behavior

The following stacktrace can be observed:

2025-03-26 10:53:34,069 ERROR [io.und.request] (executor-thread-1) UT005071: Undertow request failed HttpServerExchange{ GET /hello delegate io.undertow.vertx.VertxHttpExchange@18c5a985}: java.lang.IllegalStateException: UT010019: Response already commited
	at io.undertow.servlet.spec.ServletOutputStreamImpl.resetBuffer(ServletOutputStreamImpl.java:263)
	at io.undertow.servlet.spec.HttpServletResponseImpl.reset(HttpServletResponseImpl.java:482)
	at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:271)
	at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:111)
	at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:108)
	at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
	at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$8$1.call(UndertowDeploymentRecorder.java:643)
	at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227)
	at io.undertow.servlet.handlers.ServletInitialHandler.handleRequest(ServletInitialHandler.java:152)
	at io.undertow.server.handlers.CanonicalPathHandler.handleRequest(CanonicalPathHandler.java:49)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$1.handleRequest(UndertowDeploymentRecorder.java:126)
	at io.undertow.server.Connectors.executeRootHandler(Connectors.java:284)
	at io.undertow.server.DefaultExchangeHandler.handle(DefaultExchangeHandler.java:18)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$4$2.run(UndertowDeploymentRecorder.java:443)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:638)
	at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
	at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:1583)

The has no impact on the client, since the client is no longer listening.

How to Reproduce?

Reproducer: https://github.com/jin-harmoney/quarkus-47005

Quarkus version: 3.19.4 (latest at the time of writing)

Extensions:

  • quarkus-resteasy-jackson (note that I cannot reproduce this bug using quarkus-rest-jackson)
  • quarkus-undertow

Resource:

@Path("/hello")
public class GreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello(
            @QueryParam("size") Integer size
    ) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "A".repeat(size != null ? size : 1);
    }
}

Steps to reproduce:

The endpoint: /hello?size=1:

  • The endpoint returns the character A a certain amount of times (dictated by the size query param)
  • The endpoint has a Thread.sleep(1000), which gives us some time to cancel the request using curl --max-time
  • curl -v "localhost:8080/hello?size=16400"
    • Response size is larger than 16KB
    • Note Transfer-Encoding: chunked in the response headers
  • curl -v "localhost:8080/hello?size=16000"
    • Response size is smaller than 16KB
    • Note Content-Length: 16000 in the response headers

Now the bug (cancelling the request using --max-time):

  • curl -v --max-time 0.5 "localhost:8080/hello?size=16400"
    • Stacktrace can be seen in the logs -> bug
  • curl -v --max-time 0.5 "localhost:8080/hello?size=16000"
    • No stacktrace -> expected behavior

Output of uname -a or ver

No response

Output of java -version

openjdk version "21.0.3" 2024-04-16 LTS

Quarkus version or git rev

3.19.4

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.9.4

Additional information

Related ticket: #15075

FYI, I preferred opening a new ticket since there was no clear reproducer in the original one, but I think it might be the same issue.

@jin-harmoney jin-harmoney added the kind/bug Something isn't working label Mar 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/undertow kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants