diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java index 11d334d3b94..8c59ef03aed 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java @@ -35,7 +35,7 @@ public class AsyncContextEvent extends AsyncEvent final private AsyncContextState _asyncContext; volatile HttpChannelState _state; private ServletContext _dispatchContext; - private String _pathInContext; + private String _dispatchPath; private Scheduler.Task _timeoutTask; private Throwable _throwable; @@ -98,7 +98,7 @@ public class AsyncContextEvent extends AsyncEvent */ public String getPath() { - return _pathInContext; + return _dispatchPath; } public void setTimeoutTask(Scheduler.Task task) @@ -131,14 +131,15 @@ public class AsyncContextEvent extends AsyncEvent _throwable=throwable; } - public void setDispatchTarget(ServletContext context, String path) + public void setDispatchContext(ServletContext context) { - if (context!=null) - _dispatchContext=context; - if (path!=null) - _pathInContext=path; + _dispatchContext=context; + } + + public void setDispatchPath(String path) + { + _dispatchPath=path; } - public void completed() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index e9933f49ae8..106e0072582 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -262,6 +262,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable HttpChannelState.Action action = _state.handling(); loop: while (action.ordinal() implements HttpParser.RequestHandler, Runnable { String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest()); if (error_page!=null) - _state.getAsyncContextEvent().setDispatchTarget(_state.getContextHandler().getServletContext(),error_page); + _state.getAsyncContextEvent().setDispatchPath(error_page); } getServer().handleAsync(this); @@ -346,10 +347,14 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable if ("ContinuationThrowable".equals(e.getClass().getSimpleName())) LOG.ignore(e); else + { + error=true; throw e; + } } catch (Exception e) { + error=true; if (e instanceof EofException) LOG.debug(e); else @@ -360,6 +365,8 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable } finally { + if (error && _state.isAsyncStarted()) + _state.errorComplete(); action = _state.unhandle(); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java index b4793b632b5..9ea5a900b33 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java @@ -351,7 +351,11 @@ public class HttpChannelState if (_async!=Async.STARTED && _async!=Async.EXPIRING) throw new IllegalStateException("AsyncContext#dispath "+this.getStatusString()); _async=Async.DISPATCH; - _event.setDispatchTarget(context,path); + + if (context!=null) + _event.setDispatchContext(context); + if (path!=null) + _event.setDispatchPath(path); switch(_state) { @@ -439,6 +443,18 @@ public class HttpChannelState } } + public void errorComplete() + { + synchronized (this) + { + _async=Async.COMPLETE; + _event.setDispatchContext(null); + _event.setDispatchPath(null); + } + + cancelTimeout(); + } + protected void completed() { final List aListeners; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index b46fd13b5e9..24677edda83 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -2044,7 +2044,8 @@ public class Request implements HttpServletRequest if (_async==null) _async=new AsyncContextState(state); AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse); - event.setDispatchTarget(getServletContext(),URIUtil.addPaths(getServletPath(),getPathInfo())); + event.setDispatchContext(getServletContext()); + event.setDispatchPath(URIUtil.addPaths(getServletPath(),getPathInfo())); state.startAsync(event); return _async; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java index 045b567c9fb..309dae34150 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java @@ -35,7 +35,7 @@ import org.eclipse.jetty.server.Request; * Can be applied in jetty.xml with *
  *   <Get id='handler' name='Handler'/>
- *   <Set name='Handler>
+ *   <Set name='Handler'>
  *     <New id='idleTimeoutHandler' class='org.eclipse.jetty.server.handler.IdleTimeoutHandler'>
  *       <Set name='Handler'><Ref id='handler'/></Set>
  *       <Set name='IdleTimeoutMs'>5000</Set>
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
index f3739f3de75..4b990f2abc5 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
@@ -613,7 +613,7 @@ public class ServletHandler extends ScopedHandler
         {
             // Complete async errored requests 
             if (th!=null && request.isAsyncStarted())
-                request.getAsyncContext().complete();
+                baseRequest.getHttpChannelState().errorComplete();
             
             if (servlet_holder!=null)
                 baseRequest.setHandled(true);
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
index e8b328944ef..3aefeac8ee6 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
@@ -122,8 +122,11 @@ public class AsyncContextTest
     @Test
     public void testStartThrow() throws Exception
     {
-        String request = "GET /ctx/startthrow HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
-                + "Connection: close\r\n" + "\r\n";
+        String request = 
+          "GET /ctx/startthrow HTTP/1.1\r\n" + 
+          "Host: localhost\r\n" + 
+          "Connection: close\r\n" + 
+          "\r\n";
         String responseString = _connector.getResponses(request);
 
         BufferedReader br = new BufferedReader(new StringReader(responseString));
@@ -137,6 +140,68 @@ public class AsyncContextTest
         Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
         Assert.assertEquals("error servlet","EXCEPTION: org.eclipse.jetty.server.QuietServletException: java.io.IOException: Test",br.readLine());
     }
+
+    @Test
+    public void testStartDispatchThrow() throws Exception
+    {
+        String request = "GET /ctx/startthrow?dispatch=true HTTP/1.1\r\n" + 
+           "Host: localhost\r\n" + 
+           "Content-Type: application/x-www-form-urlencoded\r\n" + 
+           "Connection: close\r\n" + 
+           "\r\n";
+        String responseString = _connector.getResponses(request);
+
+        BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+        assertEquals("HTTP/1.1 500 Server Error",br.readLine());
+        br.readLine();// connection close
+        br.readLine();// server
+        br.readLine();// empty
+        Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+        Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
+        Assert.assertEquals("error servlet","EXCEPTION: org.eclipse.jetty.server.QuietServletException: java.io.IOException: Test",br.readLine());
+    }
+    
+    @Test
+    public void testStartCompleteThrow() throws Exception
+    {
+        String request = "GET /ctx/startthrow?complete=true HTTP/1.1\r\n" + 
+           "Host: localhost\r\n" + 
+           "Content-Type: application/x-www-form-urlencoded\r\n" + 
+           "Connection: close\r\n" + 
+           "\r\n";
+        String responseString = _connector.getResponses(request);
+
+        BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+        assertEquals("HTTP/1.1 500 Server Error",br.readLine());
+        br.readLine();// connection close
+        br.readLine();// server
+        br.readLine();// empty
+        Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+        Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
+        Assert.assertEquals("error servlet","EXCEPTION: org.eclipse.jetty.server.QuietServletException: java.io.IOException: Test",br.readLine());
+    }
+    
+    @Test
+    public void testStartFlushCompleteThrow() throws Exception
+    {
+        String request = "GET /ctx/startthrow?flush=true&complete=true HTTP/1.1\r\n" + 
+           "Host: localhost\r\n" + 
+           "Content-Type: application/x-www-form-urlencoded\r\n" + 
+           "Connection: close\r\n" + 
+           "\r\n";
+        String responseString = _connector.getResponses(request);
+
+        BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+        assertEquals("HTTP/1.1 200 OK",br.readLine());
+        br.readLine();// connection close
+        br.readLine();// server
+        br.readLine();// empty
+
+        Assert.assertEquals("error servlet","completeBeforeThrow",br.readLine());
+    }
     
     @Test
     public void testDispatchAsyncContext() throws Exception
@@ -497,6 +562,20 @@ public class AsyncContextTest
             if (request.getDispatcherType()==DispatcherType.REQUEST)
             {
                 request.startAsync(request, response);
+                
+                if (Boolean.valueOf(request.getParameter("dispatch")))
+                {
+                    request.getAsyncContext().dispatch();
+                }
+
+                if (Boolean.valueOf(request.getParameter("complete")))
+                {
+                    response.getOutputStream().write("completeBeforeThrow".getBytes());
+                    if (Boolean.valueOf(request.getParameter("flush")))
+                        response.flushBuffer();
+                    request.getAsyncContext().complete();
+                }
+                    
                 throw new QuietServletException(new IOException("Test"));
             }
         }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
index 8fa8b7e8f5e..20014a61c26 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
@@ -321,6 +321,17 @@ public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPo
     {
         return _priority;
     }
+    
+    /**
+     * Get the size of the job queue.
+     * 
+     * @return Number of jobs queued waiting for a thread
+     */
+    @ManagedAttribute("Size of the job queue")
+    public int getQueueSize()
+    {
+        return _jobs.size();
+    }
 
     /**
      * Delegated to the named or anonymous Pool.