Author: markt Date: Wed Jan 3 20:03:15 2018 New Revision: 1820003 URL: http://svn.apache.org/viewvc?rev=1820003&view=rev Log: Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=61932 Allow a call to AsyncContext.dispatch() to terminate non-blocking I/O.
Modified: tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java?rev=1820003&r1=1820002&r2=1820003&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java [UTF-8] (original) +++ tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java [UTF-8] Wed Jan 3 20:03:15 2018 @@ -350,6 +350,7 @@ public class AsyncStateMachine { private synchronized boolean doDispatch() { + clearNonBlockingListeners(); boolean doDispatch = false; if (state == AsyncState.STARTING || state == AsyncState.TIMING_OUT || Modified: tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java?rev=1820003&r1=1820002&r2=1820003&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java (original) +++ tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java Wed Jan 3 20:03:15 2018 @@ -37,6 +37,7 @@ import javax.net.SocketFactory; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; +import javax.servlet.DispatcherType; import javax.servlet.ReadListener; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; @@ -51,7 +52,6 @@ import org.junit.Ignore; import org.junit.Test; import org.apache.catalina.Context; -import org.apache.catalina.core.StandardContext; import org.apache.catalina.startup.BytesStreamer; import org.apache.catalina.startup.TesterServlet; import org.apache.catalina.startup.Tomcat; @@ -100,9 +100,8 @@ public class TestNonBlockingAPI extends private void doTestNonBlockingRead(boolean ignoreIsReady) throws Exception { Tomcat tomcat = getTomcatInstance(); - // Must have a real docBase - just use temp - StandardContext ctx = (StandardContext) tomcat.addContext("", - System.getProperty("java.io.tmpdir")); + // No file system docBase required + Context ctx = tomcat.addContext("", null); NBReadServlet servlet = new NBReadServlet(ignoreIsReady); String servletName = NBReadServlet.class.getName(); @@ -913,7 +912,89 @@ public class TestNonBlockingAPI extends }); } + } + + + /* + * https://bz.apache.org/bugzilla/show_bug.cgi?id=61932 + */ + @Test + public void testNonBlockingReadWithDispathch() throws Exception { + Tomcat tomcat = getTomcatInstance(); + // No file system docBase required + Context ctx = tomcat.addContext("", null); + + NBReadWithDispatchServlet servlet = new NBReadWithDispatchServlet(); + String servletName = NBReadWithDispatchServlet.class.getName(); + Tomcat.addServlet(ctx, servletName, servlet); + ctx.addServletMappingDecoded("/", servletName); + + tomcat.start(); + + Map<String, List<String>> resHeaders = new HashMap<>(); + int rc = postUrl(true, new DataWriter(500), "http://localhost:" + + getPort() + "/", new ByteChunk(), resHeaders, null); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); } + + @WebServlet(asyncSupported = true) + private final class NBReadWithDispatchServlet extends TesterServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + CountDownLatch latch = new CountDownLatch(1); + + // Dispatch to "/error" will end up here + if (req.getDispatcherType().equals(DispatcherType.ASYNC)) { + // Return without writing anything. This will generate the + // expected 200 response. + return; + } + + AsyncContext asyncCtx = req.startAsync(); + ServletInputStream is = req.getInputStream(); + is.setReadListener(new ReadListener() { + + @Override + public void onDataAvailable() { + + try { + byte buffer[] = new byte[1 * 1024]; + while (is.isReady() && !is.isFinished()) { + is.read(buffer); + } + + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Override + public void onAllDataRead() { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + } + }); + + new Thread(() -> { + try { + latch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + asyncCtx.dispatch("/error"); + }).start(); + + } + } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1820003&r1=1820002&r2=1820003&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Wed Jan 3 20:03:15 2018 @@ -84,6 +84,10 @@ cipher mapping. (markt) </add> <fix> + <bug>61932</bug>: Allow a call to <code>AsyncContext.dispatch()</code> + to terminate non-blocking I/O. (markt) + </fix> + <fix> <bug>61948</bug>: Improve the handling of malformed ClientHello messages in the code that extracts the SNI information from a TLS handshake for the JSSE based NIO and NIO2 connectors. (markt) --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org