This is an automated email from the ASF dual-hosted git repository. remm pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push: new 2739565 Fix regression of bugfix 65757 2739565 is described below commit 2739565fa5286623e8bb31823770595de14b6370 Author: remm <r...@apache.org> AuthorDate: Mon Feb 28 17:25:37 2022 +0100 Fix regression of bugfix 65757 With sequential use, the id needs to be reset to be accurate. Test case code submitted by Istvan Szekely. --- .../apache/catalina/connector/CoyoteAdapter.java | 2 + java/org/apache/coyote/Request.java | 4 + .../catalina/nonblocking/TestNonBlockingAPI.java | 164 ++++++++++++++++++++- webapps/docs/changelog.xml | 12 +- 4 files changed, 176 insertions(+), 6 deletions(-) diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java b/java/org/apache/catalina/connector/CoyoteAdapter.java index 50cb5a6..5b56601 100644 --- a/java/org/apache/catalina/connector/CoyoteAdapter.java +++ b/java/org/apache/catalina/connector/CoyoteAdapter.java @@ -297,6 +297,7 @@ public class CoyoteAdapter implements Adapter { } req.getRequestProcessor().setWorkerThreadName(null); + req.clearRequestThread(); // Recycle the wrapper request and response if (!success || !request.isAsync()) { updateWrapperErrorCount(request, response); @@ -429,6 +430,7 @@ public class CoyoteAdapter implements Adapter { } req.getRequestProcessor().setWorkerThreadName(null); + req.clearRequestThread(); // Recycle the wrapper request and response if (!async) { diff --git a/java/org/apache/coyote/Request.java b/java/org/apache/coyote/Request.java index 0cb4dbe..8a28996 100644 --- a/java/org/apache/coyote/Request.java +++ b/java/org/apache/coyote/Request.java @@ -742,6 +742,10 @@ public final class Request { return threadId; } + public void clearRequestThread() { + threadId = 0; + } + public void setRequestThread() { threadId = Thread.currentThread().getId(); } diff --git a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java index df89cd2..0ca63ab 100644 --- a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java +++ b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java @@ -980,6 +980,34 @@ public class TestNonBlockingAPI extends TomcatBaseTest { } } + @Test + public void testDelayedNBReadWrite() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + Context ctx = tomcat.addContext("", null); + CountDownLatch latch1 = new CountDownLatch(2); + DelayedNBReadWriteServlet servlet = new DelayedNBReadWriteServlet(latch1); + String servletName = DelayedNBReadWriteServlet.class.getName(); + Tomcat.addServlet(ctx, servletName, servlet); + ctx.addServletMappingDecoded("/", servletName); + + tomcat.start(); + + CountDownLatch latch2 = new CountDownLatch(1); + List<Throwable> exceptions = new ArrayList<>(); + + Thread t = new Thread( + new RequestPostExecutor("http://localhost:" + getPort() + "/", latch2, exceptions)); + t.start(); + + latch1.await(3000, TimeUnit.MILLISECONDS); + latch2.await(3000, TimeUnit.MILLISECONDS); + + if (exceptions.size() > 0) { + Assert.fail(); + } + } + private static final class RequestExecutor implements Runnable { private final String url; private final CountDownLatch latch; @@ -1008,6 +1036,34 @@ public class TestNonBlockingAPI extends TomcatBaseTest { } + private static final class RequestPostExecutor implements Runnable { + private final String url; + private final CountDownLatch latch; + private final List<Throwable> exceptions; + + public RequestPostExecutor(String url, CountDownLatch latch, List<Throwable> exceptions) { + this.url = url; + this.latch = latch; + this.exceptions = exceptions; + } + + @Override + public void run() { + try { + ByteChunk result = new ByteChunk(); + int rc = postUrl("body".getBytes("utf-8"), url, result, null); + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + Assert.assertTrue(result.toString().contains("OK")); + } catch (Throwable e) { + e.printStackTrace(); + exceptions.add(e); + } finally { + latch.countDown(); + } + } + + } + @WebServlet(asyncSupported = true) private static final class DelayedNBWriteServlet extends TesterServlet { private static final long serialVersionUID = 1L; @@ -1039,6 +1095,105 @@ public class TestNonBlockingAPI extends TomcatBaseTest { } + @WebServlet(asyncSupported = true) + private static final class DelayedNBReadWriteServlet extends TesterServlet { + private static final long serialVersionUID = 1L; + private final transient CountDownLatch latch; + + public DelayedNBReadWriteServlet(CountDownLatch latch) { + this.latch = latch; + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + final AsyncContext ctx = request.startAsync(); + ctx.setTimeout(1000); + + Thread readWriteListener = new Thread(new ReadWriteListener(latch, ctx)); + readWriteListener.start(); + } + } + + private static final class ReadWriteListener implements Runnable { + private final transient CountDownLatch latch; + private final transient AsyncContext ctx; + + public ReadWriteListener(CountDownLatch latch, AsyncContext ctx){ + this.latch = latch; + this.ctx = ctx; + } + + @Override + public void run() { + try { + setListeners(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void setListeners() throws IOException { + final ServletInputStream is = ctx.getRequest().getInputStream(); + final ServletOutputStream os = ctx.getResponse().getOutputStream(); + + is.setReadListener(new ReadListener() { + @Override + public void onDataAvailable() { + + try { + byte buffer[] = new byte[1 * 4]; + while (is.isReady() && !is.isFinished()) { + is.read(buffer); + } + String body = new String(buffer, StandardCharsets.UTF_8); + Assert.assertTrue(body.equals("body")); + + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Override + public void onAllDataRead() { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + } + }); + + os.setWriteListener(new WriteListener() { + private boolean written = false; + + @Override + public void onWritePossible() throws IOException { + ServletOutputStream out = ctx.getResponse().getOutputStream(); + if (out.isReady() && !written) { + out.println("OK"); + written = true; + } + if (out.isReady() && written) { + out.flush(); + if (out.isReady()) { + ctx.complete(); + latch.countDown(); + } + } + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + } + + }); + } + + } + + private static final class Emitter implements Serializable { private static final long serialVersionUID = 1L; @@ -1112,7 +1267,7 @@ public class TestNonBlockingAPI extends TomcatBaseTest { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - CountDownLatch latch = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(1); // Dispatch to "/error" will end up here if (req.getDispatcherType().equals(DispatcherType.ASYNC)) { @@ -1121,8 +1276,8 @@ public class TestNonBlockingAPI extends TomcatBaseTest { return; } - AsyncContext asyncCtx = req.startAsync(); - ServletInputStream is = req.getInputStream(); + final AsyncContext asyncCtx = req.startAsync(); + final ServletInputStream is = req.getInputStream(); is.setReadListener(new ReadListener() { @Override @@ -1157,7 +1312,6 @@ public class TestNonBlockingAPI extends TomcatBaseTest { } asyncCtx.dispatch("/error"); }).start(); - } } @@ -1536,4 +1690,4 @@ public class TestNonBlockingAPI extends TomcatBaseTest { // NO-OP } } -} +} \ No newline at end of file diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index ca5766c..69c4e07 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -114,6 +114,16 @@ </fix> </changelog> </subsection> + <subsection name="Coyote"> + <changelog> + <fix> + Fix regression introduced with <bug>65757</bug> bugfix which better + identified non request threads but which introduced a similar problem + when user code was doing sequential operations in a single thread. + Test case code submitted by Istvan Szekely. (remm) + </fix> + </changelog> + </subsection> <subsection name="Jasper"> <changelog> <fix> @@ -359,7 +369,7 @@ </fix> <fix> <bug>65757</bug>: Missing initial IO listener notification on Servlet - container dispatch to another container thread. (remm) + container dispatch to another container thread. (remm) </fix> <fix> Expand the fix for <bug>65757</bug> so that rather than just checking if --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org