On 13/11/2012 00:57, Konstantin Kolinko wrote:
> 2012/11/12  <ma...@apache.org>:
>> Author: markt
>> Date: Sun Nov 11 23:35:41 2012
>> New Revision: 1408152
>>
>> URL: http://svn.apache.org/viewvc?rev=1408152&view=rev
>> Log:
>> Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=54123
>> There are two things going on here:
>> 1. The reported bug. If there is a async timeout and no async listeners, 
>> trigger a 500 response.
>> 2. Implement "error dispatch". This is used a couple of times in the spec 
>> without any definition. The implication from the part of the spec quoted in 
>> the bug report is:
>>    - The standard error page mechanism should be used to identify the page
>>    - An async request that has been started should be left in async mode 
>> when forwarding to the error page
>>    - The error page may call complete() or dispatch()
>> This commit hooks into the StandardHostValve to access the error page 
>> mechanism. I could have copied and pasted but I preferred the dependency on 
>> StandardHostValve
>> Because the error page may do a dispatch(), need to ensure that when running 
>> the dispatch(), the error page mechanism is not triggered a second time.
>> Depending on what emerges running the full unit tests and the TCK, I mat 
>> still decide to copy the error page code to AsyncContextImpl
>>
>> 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/java/org/apache/catalina/core/StandardHostValve.java
>>     tomcat/tc7.0.x/trunk/java/org/apache/coyote/AsyncStateMachine.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:r1408148
>>
>> 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=1408152&r1=1408151&r2=1408152&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 
>> Sun Nov 11 23:35:41 2012
>> @@ -41,6 +41,8 @@ import javax.servlet.http.HttpServletRes
>>  import org.apache.catalina.AsyncDispatcher;
>>  import org.apache.catalina.Context;
>>  import org.apache.catalina.Globals;
>> +import org.apache.catalina.Host;
>> +import org.apache.catalina.Valve;
>>  import org.apache.catalina.connector.Request;
>>  import org.apache.coyote.ActionCode;
>>  import org.apache.coyote.AsyncContextCallback;
>> @@ -128,8 +130,8 @@ public class AsyncContextImpl implements
>>                              ActionCode.ASYNC_IS_TIMINGOUT, result);
>>                      return !result.get();
>>                  } else {
>> -                    // No listeners, container calls complete
>> -                    complete();
>> +                    // No listeners, trigger error handling
>> +                    return false;
>>                  }
>>
>>              } finally {
>> @@ -372,6 +374,23 @@ public class AsyncContextImpl implements
>>                          listener.getClass().getName() + "]", ioe);
>>              }
>>          }
> 
> 
> The spec in 2.3.3.3 says "If none of the listeners called
> AsyncContext.complete or any of the AsyncContext.dispatch methods,
> then perform an error dispatch...."
> 
> As far as I see, the code below is executed unconditionally, but the
> spec says that it has to be executed only if the listeners did not do
> the calls....

The code below is part of AsyncContextImpl.setErrorState() and that is
only called from timeout() if there is no configured listener that calls
complete() or dispatch().

Aside: Filip has reused setErrorState() for the Servlet 3.1 async work
in trunk. It looks reasonable to me but we'll need to wait for the final
3.1 spec to see if it is consistent with the changes I made.

Mark

> 
>> +
>> +        // SRV.2.3.3.3 (search for "error dispatch")
>> +        if (servletResponse instanceof HttpServletResponse) {
>> +            ((HttpServletResponse) servletResponse).setStatus(
>> +                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
>> +        }
>> +
>> +        Host host = (Host) context.getParent();
>> +        Valve stdHostValve = host.getPipeline().getBasic();
>> +        if (stdHostValve instanceof StandardHostValve) {
>> +            ((StandardHostValve) stdHostValve).throwable(request,
>> +                    request.getResponse(), t);
>> +        }
>> +
>> +        if (isStarted() && !request.isAsyncDispatching()) {
>> +            complete();
>> +        }
>>      }
>>
>>
>>
>> Modified: 
>> tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHostValve.java
>> URL: 
>> http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHostValve.java?rev=1408152&r1=1408151&r2=1408152&view=diff
>> ==============================================================================
>> --- 
>> tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHostValve.java 
>> (original)
>> +++ 
>> tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHostValve.java 
>> Sun Nov 11 23:35:41 2012
>> @@ -161,6 +161,9 @@ final class StandardHostValve extends Va
>>          // If a request init listener throws an exception, the request is
>>          // aborted
>>          boolean asyncAtStart = request.isAsync();
>> +        // An async error page may dispatch to another resource. This flag 
>> helps
>> +        // ensure an infinite error handling loop is not entered
>> +        boolean errorAtStart = response.isError();
>>          if (asyncAtStart || context.fireRequestInitEvent(request)) {
>>
>>              // Ask this Context to process this request
>> @@ -168,29 +171,37 @@ final class StandardHostValve extends Va
>>                  context.getPipeline().getFirst().invoke(request, response);
>>              } catch (Throwable t) {
>>                  ExceptionUtils.handleThrowable(t);
>> -                request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
>> -                throwable(request, response, t);
>> +                if (errorAtStart) {
>> +                    container.getLogger().error("Exception Processing " +
>> +                            request.getRequestURI(), t);
>> +                } else {
>> +                    request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, 
>> t);
>> +                    throwable(request, response, t);
>> +                }
>>              }
>>
>>              // If the request was async at the start and an error occurred 
>> then
>>              // the async error handling will kick-in and that will fire the
>>              // request destroyed event *after* the error handling has taken
>>              // place
>> -            if (!(request.isAsync() || (asyncAtStart && 
>> request.getAttribute(
>> -                        RequestDispatcher.ERROR_EXCEPTION) != null))) {
>> -                // Protect against NPEs if context was destroyed during a 
>> long
>> -                // running request.
>> +            if (!(request.isAsync() || (asyncAtStart &&
>> +                    request.getAttribute(
>> +                            RequestDispatcher.ERROR_EXCEPTION) != null))) {
>> +                // Protect against NPEs if context was destroyed during a
>> +                // long running request.
>>                  if (context.getState().isAvailable()) {
>> -                    // Error page processing
>> -                    response.setSuspended(false);
>> +                    if (!errorAtStart) {
>> +                        // Error page processing
>> +                        response.setSuspended(false);
>>
>> -                    Throwable t = (Throwable) request.getAttribute(
>> -                            RequestDispatcher.ERROR_EXCEPTION);
>> +                        Throwable t = (Throwable) request.getAttribute(
>> +                                RequestDispatcher.ERROR_EXCEPTION);
>>
>> -                    if (t != null) {
>> -                        throwable(request, response, t);
>> -                    } else {
>> -                        status(request, response);
>> +                        if (t != null) {
>> +                            throwable(request, response, t);
>> +                        } else {
>> +                            status(request, response);
>> +                        }
>>                      }
>>
>>                      context.fireRequestDestroyEvent(request);
>> @@ -348,7 +359,7 @@ final class StandardHostValve extends Va
>>       * @param throwable The exception that occurred (which possibly wraps
>>       *  a root cause exception
>>       */
>> -    private void throwable(Request request, Response response,
>> +    protected void throwable(Request request, Response response,
>>                               Throwable throwable) {
>>          Context context = request.getContext();
>>          if (context == null)
>>
>> Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/AsyncStateMachine.java
>> URL: 
>> http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/AsyncStateMachine.java?rev=1408152&r1=1408151&r2=1408152&view=diff
>> ==============================================================================
>> --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/AsyncStateMachine.java 
>> (original)
>> +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/AsyncStateMachine.java Sun 
>> Nov 11 23:35:41 2012
>> @@ -249,7 +249,8 @@ public class AsyncStateMachine<S> {
>>          if (state == AsyncState.STARTING) {
>>              state = AsyncState.MUST_DISPATCH;
>>          } else if (state == AsyncState.STARTED ||
>> -                state == AsyncState.TIMING_OUT) {
>> +                state == AsyncState.TIMING_OUT ||
>> +                state == AsyncState.ERROR) {
>>              state = AsyncState.DISPATCHING;
>>              doDispatch = true;
>>          } else {
>>
>> 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=1408152&r1=1408151&r2=1408152&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 
>> Sun Nov 11 23:35:41 2012
>> @@ -19,6 +19,7 @@ package org.apache.catalina.core;
>>
>>  import java.io.File;
>>  import java.io.IOException;
>> +import java.io.PrintWriter;
>>  import java.util.LinkedHashMap;
>>  import java.util.List;
>>  import java.util.Map;
>> @@ -38,6 +39,8 @@ import javax.servlet.http.HttpServlet;
>>  import javax.servlet.http.HttpServletRequest;
>>  import javax.servlet.http.HttpServletResponse;
>>
>> +import junit.framework.Assert;
>> +
>>  import static org.junit.Assert.assertEquals;
>>  import static org.junit.Assert.assertNotNull;
>>  import static org.junit.Assert.assertTrue;
>> @@ -48,6 +51,7 @@ import org.junit.Test;
>>  import org.apache.catalina.Context;
>>  import org.apache.catalina.Wrapper;
>>  import org.apache.catalina.connector.Request;
>> +import org.apache.catalina.deploy.ErrorPage;
>>  import org.apache.catalina.startup.Tomcat;
>>  import org.apache.catalina.startup.TomcatBaseTest;
>>  import org.apache.catalina.valves.TesterAccessLogValve;
>> @@ -174,7 +178,7 @@ public class TestAsyncContextImpl extend
>>          assertEquals("OK-run2", bc2.toString());
>>
>>          // Check the access log
>> -        alv.validateAccessLog(2, 200,
>> +        alv.validateAccessLog(2, 500,
>>                  AsyncStartNoCompleteServlet.ASYNC_TIMEOUT,
>>                  AsyncStartNoCompleteServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN +
>>                          REQUEST_TIME);
>> @@ -380,29 +384,34 @@ public class TestAsyncContextImpl extend
>>      @Test
>>      public void testTimeoutListenerCompleteNoDispatch() throws Exception {
>>          // Should work
>> -        doTestTimeout(true, null);
>> +        doTestTimeout(Boolean.TRUE, null);
>>      }
>>
>>      @Test
>>      public void testTimeoutListenerNoCompleteNoDispatch() throws Exception {
>>          // Should trigger an error - must do one or other
>> -        doTestTimeout(false, null);
>> +        doTestTimeout(Boolean.FALSE, null);
>>      }
>>
>>      @Test
>>      public void testTimeoutListenerCompleteDispatch() throws Exception {
>>          // Should trigger an error - can't do both
>> -        doTestTimeout(true, "/nonasync");
>> +        doTestTimeout(Boolean.TRUE, "/nonasync");
>>      }
>>
>>      @Test
>>      public void testTimeoutListenerNoCompleteDispatch() throws Exception {
>>          // Should work
>> -        doTestTimeout(false, "/nonasync");
>> +        doTestTimeout(Boolean.FALSE, "/nonasync");
>>      }
>>
>> +    @Test
>> +    public void testTimeoutNoListener() throws Exception {
>> +        // Should work
>> +        doTestTimeout(null, null);
>> +    }
>>
>> -    private void doTestTimeout(boolean completeOnTimeout, String 
>> dispatchUrl)
>> +    private void doTestTimeout(Boolean completeOnTimeout, String 
>> dispatchUrl)
>>      throws Exception {
>>          // Setup Tomcat instance
>>          Tomcat tomcat = getTomcatInstance();
>> @@ -420,7 +429,7 @@ public class TestAsyncContextImpl extend
>>          Context ctx = tomcat.addContext("", docBase.getAbsolutePath());
>>
>>          TimeoutServlet timeout =
>> -            new TimeoutServlet(completeOnTimeout, dispatchUrl);
>> +                new TimeoutServlet(completeOnTimeout, dispatchUrl);
>>
>>          Wrapper wrapper = Tomcat.addServlet(ctx, "time", timeout);
>>          wrapper.setAsyncSupported(true);
>> @@ -447,8 +456,11 @@ public class TestAsyncContextImpl extend
>>              // Ignore - expected for some error conditions
>>          }
>>          StringBuilder expected = new StringBuilder("requestInitialized-");
>> -        expected.append("TimeoutServletGet-onTimeout-");
>> -        if (completeOnTimeout) {
>> +        expected.append("TimeoutServletGet-");
>> +        if (completeOnTimeout == null) {
>> +            expected.append("requestDestroyed");
>> +        } else if (completeOnTimeout.booleanValue()) {
>> +            expected.append("onTimeout-");
>>              if (dispatchUrl == null) {
>>                  expected.append("onComplete-");
>>                  expected.append("requestDestroyed");
>> @@ -459,6 +471,7 @@ public class TestAsyncContextImpl extend
>>                  // never happens.
>>              }
>>          } else {
>> +            expected.append("onTimeout-");
>>              if (dispatchUrl == null) {
>>                  expected.append("onError-");
>>              } else {
>> @@ -470,7 +483,14 @@ public class TestAsyncContextImpl extend
>>          assertEquals(expected.toString(), res.toString());
>>
>>          // Check the access log
>> -        if (completeOnTimeout && dispatchUrl != null) {
>> +        if (completeOnTimeout == null) {
>> +            alvGlobal.validateAccessLog(1, 500, 
>> TimeoutServlet.ASYNC_TIMEOUT,
>> +                    TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN +
>> +                    REQUEST_TIME);
>> +            alv.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT,
>> +                    TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN +
>> +                    REQUEST_TIME);
>> +        } else if (completeOnTimeout.booleanValue() && dispatchUrl != null) 
>> {
>>              // This error is written into Host-level AccessLogValve only
>>              alvGlobal.validateAccessLog(1, 500, 0, 
>> TimeoutServlet.ASYNC_TIMEOUT
>>                      + TIMEOUT_MARGIN + REQUEST_TIME);
>> @@ -488,12 +508,12 @@ public class TestAsyncContextImpl extend
>>      private static class TimeoutServlet extends HttpServlet {
>>          private static final long serialVersionUID = 1L;
>>
>> -        private final boolean completeOnTimeout;
>> +        private final Boolean completeOnTimeout;
>>          private final String dispatchUrl;
>>
>>          public static final long ASYNC_TIMEOUT = 3000;
>>
>> -        public TimeoutServlet(boolean completeOnTimeout, String 
>> dispatchUrl) {
>> +        public TimeoutServlet(Boolean completeOnTimeout, String 
>> dispatchUrl) {
>>              this.completeOnTimeout = completeOnTimeout;
>>              this.dispatchUrl = dispatchUrl;
>>          }
>> @@ -506,8 +526,10 @@ public class TestAsyncContextImpl extend
>>                  final AsyncContext ac = req.startAsync();
>>                  ac.setTimeout(ASYNC_TIMEOUT);
>>
>> -                ac.addListener(new TrackingListener(
>> -                        false, completeOnTimeout, dispatchUrl));
>> +                if (completeOnTimeout != null) {
>> +                    ac.addListener(new TrackingListener(false,
>> +                            completeOnTimeout.booleanValue(), dispatchUrl));
>> +                }
>>              } else {
>>                  resp.getWriter().print("FAIL: Async unsupported");
>>              }
>> @@ -672,7 +694,7 @@ public class TestAsyncContextImpl extend
>>          wrapper.setAsyncSupported(true);
>>          ctx.addServletMapping("/stage1", "tracking");
>>
>> -        TimeoutServlet timeout = new TimeoutServlet(true, null);
>> +        TimeoutServlet timeout = new TimeoutServlet(Boolean.TRUE, null);
>>          Wrapper wrapper2 = Tomcat.addServlet(ctx, "timeout", timeout);
>>          wrapper2.setAsyncSupported(true);
>>          ctx.addServletMapping("/stage2", "timeout");
>> @@ -1454,4 +1476,160 @@ public class TestAsyncContextImpl extend
>>              resp.getWriter().print("OK");
>>          }
>>      }
>> +
>> +    @Test
>> +    public void testTimeoutErrorDispatchNone() throws Exception {
>> +        doTestTimeoutErrorDispatch(null, null);
>> +    }
>> +
>> +    @Test
>> +    public void testTimeoutErrorDispatchNonAsync() throws Exception {
>> +        doTestTimeoutErrorDispatch(Boolean.FALSE, null);
>> +    }
>> +
>> +    @Test
>> +    public void testTimeoutErrorDispatchAsyncStart() throws Exception {
>> +        doTestTimeoutErrorDispatch(
>> +                Boolean.TRUE, ErrorPageAsyncMode.NO_COMPLETE);
>> +    }
>> +
>> +    @Test
>> +    public void testTimeoutErrorDispatchAsyncComplete() throws Exception {
>> +        doTestTimeoutErrorDispatch(Boolean.TRUE, 
>> ErrorPageAsyncMode.COMPLETE);
>> +    }
>> +
>> +    @Test
>> +    public void testTimeoutErrorDispatchAsyncDispatch() throws Exception {
>> +        doTestTimeoutErrorDispatch(Boolean.TRUE, 
>> ErrorPageAsyncMode.DISPATCH);
>> +    }
>> +
>> +    private void doTestTimeoutErrorDispatch(Boolean asyncError,
>> +            ErrorPageAsyncMode mode) 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());
>> +
>> +        TimeoutServlet timeout = new TimeoutServlet(null, null);
>> +        Wrapper w1 = Tomcat.addServlet(ctx, "time", timeout);
>> +        w1.setAsyncSupported(true);
>> +        ctx.addServletMapping("/async", "time");
>> +
>> +        NonAsyncServlet nonAsync = new NonAsyncServlet();
>> +        Wrapper w2 = Tomcat.addServlet(ctx, "nonAsync", nonAsync);
>> +        w2.setAsyncSupported(true);
>> +        ctx.addServletMapping("/error/nonasync", "nonAsync");
>> +
>> +        AsyncErrorPage asyncErrorPage = new AsyncErrorPage(mode);
>> +        Wrapper w3 = Tomcat.addServlet(ctx, "asyncErrorPage", 
>> asyncErrorPage);
>> +        w3.setAsyncSupported(true);
>> +        ctx.addServletMapping("/error/async", "asyncErrorPage");
>> +
>> +        if (asyncError != null) {
>> +            ErrorPage ep = new ErrorPage();
>> +            ep.setErrorCode(500);
>> +            if (asyncError.booleanValue()) {
>> +                ep.setLocation("/error/async");
>> +            } else {
>> +                ep.setLocation("/error/nonasync");
>> +            }
>> +
>> +            ctx.addErrorPage(ep);
>> +        }
>> +
>> +        ctx.addApplicationListener(TrackingRequestListener.class.getName());
>> +
>> +        TesterAccessLogValve alv = new TesterAccessLogValve();
>> +        ctx.getPipeline().addValve(alv);
>> +        TesterAccessLogValve alvGlobal = new TesterAccessLogValve();
>> +        tomcat.getHost().getPipeline().addValve(alvGlobal);
>> +
>> +        tomcat.start();
>> +        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();
>> +        if (asyncError == null) {
>> +            // No error handler - just get the 500 response
>> +            expected.append("requestInitialized-TimeoutServletGet-");
>> +            // Note: With an error handler the response will be reset and 
>> these
>> +            //       will be lost
>> +        }
>> +        if (asyncError != null) {
>> +            if (asyncError.booleanValue()) {
>> +                expected.append("AsyncErrorPageGet-");
>> +                if (mode == ErrorPageAsyncMode.NO_COMPLETE){
>> +                    expected.append("NoOp-");
>> +                } else if (mode == ErrorPageAsyncMode.COMPLETE) {
>> +                    expected.append("Complete-");
>> +                } else if (mode == ErrorPageAsyncMode.DISPATCH) {
>> +                    expected.append("Dispatch-NonAsyncServletGet-");
>> +                }
>> +            } else {
>> +                expected.append("NonAsyncServletGet-");
>> +            }
>> +        }
>> +        expected.append("requestDestroyed");
>> +
>> +        Assert.assertEquals(expected.toString(), res.toString());
>> +
>> +        // Check the access log
>> +        alvGlobal.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT,
>> +                TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN +
>> +                REQUEST_TIME);
>> +        alv.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT,
>> +                TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN +
>> +                REQUEST_TIME);
>> +    }
>> +
>> +    private static enum ErrorPageAsyncMode {
>> +        NO_COMPLETE,
>> +        COMPLETE,
>> +        DISPATCH
>> +    }
>> +
>> +    private static class AsyncErrorPage extends HttpServlet {
>> +
>> +        private static final long serialVersionUID = 1L;
>> +
>> +        private final ErrorPageAsyncMode mode;
>> +
>> +        public AsyncErrorPage(ErrorPageAsyncMode mode) {
>> +            this.mode = mode;
>> +        }
>> +
>> +        @Override
>> +        protected void doGet(HttpServletRequest req, HttpServletResponse 
>> resp)
>> +                throws ServletException, IOException {
>> +            PrintWriter writer = resp.getWriter();
>> +            writer.write("AsyncErrorPageGet-");
>> +            resp.flushBuffer();
>> +
>> +            final AsyncContext ctxt = req.getAsyncContext();
>> +
>> +            switch(mode) {
>> +                case COMPLETE:
>> +                    writer.write("Complete-");
>> +                    ctxt.complete();
>> +                    break;
>> +                case DISPATCH:
>> +                    writer.write("Dispatch-");
>> +                    ctxt.dispatch("/error/nonasync");
>> +                    break;
>> +                case NO_COMPLETE:
>> +                    writer.write("NoOp-");
>> +                    break;
>> +                default:
>> +                    // Impossible
>> +                    break;
>> +            }
>> +        }
>> +    }
>>  }
>>
>> 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=1408152&r1=1408151&r2=1408152&view=diff
>> ==============================================================================
>> --- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original)
>> +++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Sun Nov 11 23:35:41 2012
>> @@ -123,6 +123,11 @@
>>          <code>AsyncListener.complete()</code> are called with the correct
>>          thread context class loader. (fhanik)
>>        </fix>
>> +      <fix>
>> +        <bug>54123</bug>: If an asynchronous request times out without any
>> +        <code>AsyncListener</code>s defined, a 500 error will be triggered.
>> +        (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
>>
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: dev-h...@tomcat.apache.org
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to