diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpSender.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpSender.java index 69bade58dd68..8d5b8888cc81 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpSender.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpSender.java @@ -100,7 +100,6 @@ protected boolean queuedToBegin(HttpExchange exchange) LOG.debug("Request begin {}", request); request.notifyBegin(); - contentSender.exchange = exchange; contentSender.expect100 = expects100Continue(request); if (updateRequestState(RequestState.TRANSIENT, RequestState.BEGIN)) @@ -390,7 +389,10 @@ private void internalAbort(Throwable failure) { HttpExchange exchange = getHttpExchange(); if (exchange == null) + { + LOG.info("ISSUE-11841 no exchange in internalAbort - {}", this); return; + } anyToFailure(failure); abortRequest(exchange); } @@ -424,10 +426,11 @@ protected String relativize(String path) @Override public String toString() { - return String.format("%s@%x(req=%s,failure=%s)", + return String.format("%s@%x(req=%s,cs=%s,failure=%s)", getClass().getSimpleName(), hashCode(), requestState, + contentSender, failure); } @@ -469,7 +472,6 @@ private enum RequestState private class ContentSender extends IteratingCallback { // Fields that are set externally. - private volatile HttpExchange exchange; private volatile Runnable proceedAction; private volatile boolean expect100; // Fields only used internally. @@ -484,7 +486,6 @@ private class ContentSender extends IteratingCallback @Override public boolean reset() { - exchange = null; proceedAction = null; expect100 = false; chunk = null; @@ -500,7 +501,12 @@ public boolean reset() @Override protected Action process() throws Throwable { - HttpExchange exchange = this.exchange; + HttpExchange exchange = getHttpExchange(); + if (exchange == null) + { + LOG.info("ISSUE-11841 no exchange in process - {}", HttpSender.this); + return Action.IDLE; + } if (complete) { if (success) @@ -581,6 +587,19 @@ protected void onSuccess() } else { + HttpExchange exchange = getHttpExchange(); + if (exchange == null) + { + LOG.info("ISSUE-11841 no exchange in onSuccess - {}", HttpSender.this); + if (chunk != null) + { + LOG.info("ISSUE-11841 releasing chunk - {}", HttpSender.this); + chunk.release(); + chunk = null; + } + return; + } + boolean proceed = true; if (committed) { @@ -641,5 +660,20 @@ public InvocationType getInvocationType() { return InvocationType.NON_BLOCKING; } + + @Override + public String toString() + { + return super.toString() + + " proceedAction=" + proceedAction + + " expect100=" + expect100 + + " chunk=" + chunk + + " contentBuffer=" + BufferUtil.toDetailString(contentBuffer) + + " committed=" + committed + + " success=" + success + + " complete=" + complete + + " abort=" + abort + + " demanded=" + demanded; + } } } diff --git a/jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServletTest.java b/jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServletTest.java index c1218587ab0b..b32b99487ad6 100644 --- a/jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServletTest.java +++ b/jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServletTest.java @@ -61,6 +61,7 @@ import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; +import org.eclipse.jetty.client.StringRequestContent; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.http.HttpHeader; @@ -79,6 +80,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.ajax.JSON; +import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -163,11 +165,32 @@ private void startClient() throws Exception } @AfterEach - public void dispose() throws Exception + public void dispose() { - client.stop(); - proxy.stop(); - server.stop(); + LifeCycle.stop(client); + LifeCycle.stop(proxy); + LifeCycle.stop(server); + } + + @Test + public void testExpect100WithBody() throws Exception + { + startServer(new EchoHttpServlet()); + startProxy(new AsyncMiddleManServlet()); + startClient(); + + for (int i = 0; i < 100; i++) + { + String body = Character.toString('a' + (i % 26)); // only use 'a' to 'z' + ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort()) + .path("/" + body) + .headers(h -> h.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE)) + .timeout(5, TimeUnit.SECONDS) + .body(new StringRequestContent(body)) + .send(); + assertEquals(200, response.getStatus()); + assertEquals(body, response.getContentAsString()); + } } @Test diff --git a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServletTest.java b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServletTest.java index 064ac6687455..433e59a41f2c 100644 --- a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServletTest.java +++ b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServletTest.java @@ -61,6 +61,7 @@ import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; +import org.eclipse.jetty.client.StringRequestContent; import org.eclipse.jetty.ee9.servlet.ServletContextHandler; import org.eclipse.jetty.ee9.servlet.ServletHolder; import org.eclipse.jetty.http.HttpHeader; @@ -163,11 +164,32 @@ private void startClient() throws Exception } @AfterEach - public void dispose() throws Exception + public void dispose() { LifeCycle.stop(client); LifeCycle.stop(proxy); - LifeCycle.stop(proxy); + LifeCycle.stop(server); + } + + @Test + public void testExpect100WithBody() throws Exception + { + startServer(new EchoHttpServlet()); + startProxy(new AsyncMiddleManServlet()); + startClient(); + + for (int i = 0; i < 100; i++) + { + String body = Character.toString('a' + (i % 26)); // only use 'a' to 'z' + ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort()) + .path("/" + body) + .headers(h -> h.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE)) + .timeout(5, TimeUnit.SECONDS) + .body(new StringRequestContent(body)) + .send(); + assertEquals(200, response.getStatus()); + assertEquals(body, response.getContentAsString()); + } } @Test