Author: markt Date: Wed Apr 24 11:43:30 2013 New Revision: 1471372 URL: http://svn.apache.org/r1471372 Log: Protect against AsyncListeners that throw RuntimeExceptions (they should normally only throw IOExceptions). Includes a test case.
Modified: tomcat/tc7.0.x/trunk/ (props changed) tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/AsyncContextImpl.java tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Propchange: tomcat/tc7.0.x/trunk/ ------------------------------------------------------------------------------ Merged /tomcat/trunk:r1471371 Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/AsyncContextImpl.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/AsyncContextImpl.java?rev=1471372&r1=1471371&r2=1471372&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/AsyncContextImpl.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/AsyncContextImpl.java Wed Apr 24 11:43:30 2013 @@ -117,9 +117,10 @@ public class AsyncContextImpl implements for (AsyncListenerWrapper listener : listenersCopy) { try { listener.fireOnComplete(event); - } catch (IOException ioe) { + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); log.warn("onComplete() failed for listener of type [" + - listener.getClass().getName() + "]", ioe); + listener.getClass().getName() + "]", t); } } } finally { @@ -148,9 +149,10 @@ public class AsyncContextImpl implements for (AsyncListenerWrapper listener : listenersCopy) { try { listener.fireOnTimeout(event); - } catch (IOException ioe) { + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); log.warn("onTimeout() failed for listener of type [" + - listener.getClass().getName() + "]", ioe); + listener.getClass().getName() + "]", t); } } request.getCoyoteRequest().action( @@ -328,9 +330,10 @@ public class AsyncContextImpl implements for (AsyncListenerWrapper listener : listenersCopy) { try { listener.fireOnStartAsync(event); - } catch (IOException ioe) { + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); log.warn("onStartAsync() failed for listener of type [" + - listener.getClass().getName() + "]", ioe); + listener.getClass().getName() + "]", t); } } listeners.clear(); @@ -393,9 +396,10 @@ public class AsyncContextImpl implements for (AsyncListenerWrapper listener : listenersCopy) { try { listener.fireOnError(errorEvent); - } catch (IOException ioe) { + } catch (Throwable t2) { + ExceptionUtils.handleThrowable(t); log.warn("onError() failed for listener of type [" + - listener.getClass().getName() + "]", ioe); + listener.getClass().getName() + "]", t2); } } } Modified: tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java?rev=1471372&r1=1471371&r2=1471372&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java (original) +++ tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java Wed Apr 24 11:43:30 2013 @@ -1189,7 +1189,7 @@ public class TestAsyncContextImpl extend private static final long serialVersionUID = 1L; - private int status = 200; + private int status; public AsyncStatusServlet(int status) { this.status = status; @@ -1629,4 +1629,108 @@ public class TestAsyncContextImpl extend } } } + + @Test + public void testBug54178() throws Exception { + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // Must have a real docBase - just use temp + File docBase = new File(System.getProperty("java.io.tmpdir")); + + Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); + + Bug54178ServletA bug54178ServletA = new Bug54178ServletA(); + Wrapper wrapper = + Tomcat.addServlet(ctx, "bug54178ServletA", bug54178ServletA); + wrapper.setAsyncSupported(true); + ctx.addServletMapping("/bug54178ServletA", "bug54178ServletA"); + + Bug54178ServletB bug54178ServletB = new Bug54178ServletB(); + Tomcat.addServlet(ctx, "bug54178ServletB", bug54178ServletB); + ctx.addServletMapping("/bug54178ServletB", "bug54178ServletB"); + + tomcat.start(); + + ByteChunk body = new ByteChunk(); + int rc = -1; + + try { + rc = getUrl("http://localhost:" + getPort() + "/bug54178ServletA?" + + Bug54178ServletA.PARAM_NAME + "=bar", + body, null); + } catch (IOException ioe) { + // This may happen if test fails. Output the exception in case it is + // useful and let asserts handle the failure + ioe.printStackTrace(); + } + + assertEquals(HttpServletResponse.SC_OK, rc); + + body.recycle(); + + rc = getUrl("http://localhost:" + getPort() + "/bug54178ServletB", + body, null); + + assertEquals(HttpServletResponse.SC_OK, rc); + assertEquals("OK", body.toString()); + } + + private static class Bug54178ServletA extends HttpServlet { + + public static final String PARAM_NAME = "foo"; + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + req.getParameter(PARAM_NAME); + AsyncContext actxt = req.startAsync(); + actxt.addListener(new Bug54178AsyncListener()); + actxt.complete(); + } + } + + private static class Bug54178ServletB extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + resp.setContentType("text/plain"); + PrintWriter pw = resp.getWriter(); + String result = req.getParameter(Bug54178ServletA.PARAM_NAME); + if (result == null) { + pw.write("OK"); + } else { + pw.write("FAIL"); + } + } + } + + private static class Bug54178AsyncListener implements AsyncListener { + + @Override + public void onComplete(AsyncEvent event) throws IOException { + throw new RuntimeException("Testing Bug54178"); + } + + @Override + public void onTimeout(AsyncEvent event) throws IOException { + // NO-OP + } + + @Override + public void onError(AsyncEvent event) throws IOException { + // NO-OP + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException { + // NO-OP + } + } } Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1471372&r1=1471371&r2=1471372&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Wed Apr 24 11:43:30 2013 @@ -63,6 +63,11 @@ (markt) </update> <fix> + <bug>54178</bug>: Protect against <code>AsyncListener</code> + implementations that throw <code>RuntimeException</code>s in response to + an event. (markt) + </fix> + <fix> <bug>54851</bug>: When scanning for web fragments, directories without any web-fragment.xml should not impact the status of distributable element. Patch provided by Trask Stalnaker. (violetagg) --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org