Author: markt Date: Wed Jun 29 12:57:50 2011 New Revision: 1141079 URL: http://svn.apache.org/viewvc?rev=1141079&view=rev Log: Ensure that if asyncDispatch() is called during an onTimeout event and the target Servlet does not call startAsync() or complete() that Tomcat calls complete() (or does the equivalent) once the target Servlet exits.
Modified: tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java?rev=1141079&r1=1141078&r2=1141079&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java Wed Jun 29 12:57:50 2011 @@ -312,6 +312,9 @@ public class AsyncContextImpl implements } try { dispatch.run(); + if (!request.isAsync()) { + fireOnComplete(); + } } catch (RuntimeException x) { // doInternalComplete(true); if (x.getCause() instanceof ServletException) { Modified: tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java?rev=1141079&r1=1141078&r2=1141079&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java (original) +++ tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java Wed Jun 29 12:57:50 2011 @@ -365,18 +365,26 @@ public class TestAsyncContextImpl extend } } - public void testTimeoutListenerComplete() throws Exception { + public void testTimeoutListenerCompleteNoDispatch() throws Exception { + // Should work doTestTimeout(true, null); } - public void testTimeoutListenerNoComplete() throws Exception { + public void testTimeoutListenerNoCompleteNoDispatch() throws Exception { + // Should trigger an error - must do one or other doTestTimeout(false, null); } - public void testTimeoutListenerDispatch() throws Exception { + public void testTimeoutListenerCompleteDispatch() throws Exception { + // Should trigger an error - can't do both doTestTimeout(true, "/nonasync"); } - + + public void testTimeoutListenerNoCompleteDispatch() throws Exception { + // Should work + doTestTimeout(false, "/nonasync"); + } + private void doTestTimeout(boolean completeOnTimeout, String dispatchUrl) throws Exception { @@ -413,23 +421,44 @@ public class TestAsyncContextImpl extend ctx.getPipeline().addValve(alv); tomcat.start(); - ByteChunk res = getUrl("http://localhost:" + getPort() + "/async"); + ByteChunk res = new ByteChunk(); + try { + getUrl("http://localhost:" + getPort() + "/async", res, null); + } catch (IOException ioe) { + // Ignore - expected for some error conditions + } StringBuilder expected = new StringBuilder("requestInitialized-"); expected.append("TimeoutServletGet-onTimeout-"); - if (!completeOnTimeout) { - expected.append("onError-"); - } - if (dispatchUrl == null) { - expected.append("onComplete-"); + if (completeOnTimeout) { + if (dispatchUrl == null) { + expected.append("onComplete-"); + expected.append("requestDestroyed"); + } else { + // Error - no further output + // There is no onComplete- since the complete event would be + // fired during post processing but since there is an error that + // never happens. + } } else { - expected.append("NonAsyncServletGet-"); + if (dispatchUrl == null) { + expected.append("onError-"); + } else { + expected.append("NonAsyncServletGet-"); + } + expected.append("onComplete-"); + expected.append("requestDestroyed"); } - expected.append("requestDestroyed"); assertEquals(expected.toString(), res.toString()); // Check the access log - validateAccessLog(alv, 1, 200, TimeoutServlet.ASYNC_TIMEOUT, - TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME); + if (completeOnTimeout && dispatchUrl != null) { + validateAccessLog(alv, 1, 500, 0, TimeoutServlet.ASYNC_TIMEOUT + + TIMEOUT_MARGIN + REQUEST_TIME); + } else { + validateAccessLog(alv, 1, 200, TimeoutServlet.ASYNC_TIMEOUT, + TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + + REQUEST_TIME); + } } private static class TimeoutServlet extends HttpServlet { @@ -695,11 +724,10 @@ public class TestAsyncContextImpl extend resp.getWriter().write("onTimeout-"); resp.flushBuffer(); if (completeOnTimeout){ - if (dispatchUrl == null) { - event.getAsyncContext().complete(); - } else { - event.getAsyncContext().dispatch(dispatchUrl); - } + event.getAsyncContext().complete(); + } + if (dispatchUrl != null) { + event.getAsyncContext().dispatch(dispatchUrl); } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1141079&r1=1141078&r2=1141079&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Wed Jun 29 12:57:50 2011 @@ -167,6 +167,11 @@ asynchronous request processing and the socket is immediately closed. (markt) </fix> + <fix> + Ensure that if asyncDispatch() is called during an onTimeout event and + the target Servlet does not call startAsync() or complete() that Tomcat + calls complete() once the target Servlet exits. (markt) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org