From c280e31b46bb125661b3c2a3a5f2aa7585cbc6f0 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Wed, 9 Nov 2022 12:16:42 +0100 Subject: [PATCH] Fixes #7117 - Timeout with Expect 100 continue when using ProxyServlet. (#8873) * Fixes #7117 - Timeout with Expect 100 continue when using ProxyServlet. Now getReader() tests whether it has to send a 100 continue in case getInputStream() is not called because the reader is reused. Signed-off-by: Simone Bordet --- .../eclipse/jetty/proxy/ProxyServletTest.java | 33 +++++++++++++++++++ .../org/eclipse/jetty/server/Request.java | 8 ++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java index 1a021a8985c..78c8c18aa61 100644 --- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java +++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java @@ -19,6 +19,7 @@ import java.io.InterruptedIOException; import java.io.OutputStream; import java.io.PrintWriter; +import java.io.Writer; import java.net.ConnectException; import java.net.HttpCookie; import java.nio.ByteBuffer; @@ -1637,4 +1638,36 @@ protected void service(HttpServletRequest request, HttpServletResponse response) assertFalse(contentLatch.await(1, TimeUnit.SECONDS)); assertTrue(clientLatch.await(5, TimeUnit.SECONDS)); } + + @ParameterizedTest + @MethodSource("impls") + public void testExpect100ContinueWithReader(Class proxyServletClass) throws Exception + { + startServer(new EmptyHttpServlet() + { + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException + { + // Calling getReader() should trigger the send of 100 Continue. + IO.copy(request.getReader(), Writer.nullWriter()); + } + }); + startProxy(proxyServletClass); + startClient(); + client.setMaxConnectionsPerDestination(1); + + // Perform consecutive requests to test whether recycling of + // the Request on server side messes up 100 Continue handling. + int count = 8; + for (int i = 0; i < count; ++i) + { + ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort()) + .path("/" + i) + .headers(headers -> headers.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())) + .body(new BytesRequestContent(new byte[]{'h', 'e', 'l', 'l', 'o'})) + .timeout(5, TimeUnit.SECONDS) + .send(); + assertEquals(HttpStatus.OK_200, response.getStatus()); + } + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index e0929a2a191..00575980873 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -1161,17 +1161,23 @@ public BufferedReader getReader() throws IOException if (_reader == null || !encoding.equalsIgnoreCase(_readerEncoding)) { - final ServletInputStream in = getInputStream(); + ServletInputStream in = getInputStream(); _readerEncoding = encoding; _reader = new BufferedReader(new InputStreamReader(in, encoding)) { @Override public void close() throws IOException { + // Do not call super to avoid marking this reader as closed, + // but do close the ServletInputStream that can be reopened. in.close(); } }; } + else if (_channel.isExpecting100Continue()) + { + _channel.continue100(_input.available()); + } _inputState = INPUT_READER; return _reader; }