420776 complete error pages after startAsync

handle complete and dispatch calls before the thrown exception

Conflicts:
	jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java
	jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java
	jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
	jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
This commit is contained in:
Greg Wilkins 2013-11-08 14:32:50 +11:00
parent ed1555e0c4
commit 1d5635c76c
6 changed files with 132 additions and 19 deletions

View File

@ -35,7 +35,7 @@ public class AsyncContextEvent extends AsyncEvent
final private AsyncContextState _asyncContext; final private AsyncContextState _asyncContext;
volatile HttpChannelState _state; volatile HttpChannelState _state;
private ServletContext _dispatchContext; private ServletContext _dispatchContext;
private String _pathInContext; private String _dispatchPath;
private Scheduler.Task _timeoutTask; private Scheduler.Task _timeoutTask;
private Throwable _throwable; private Throwable _throwable;
@ -98,7 +98,7 @@ public class AsyncContextEvent extends AsyncEvent
*/ */
public String getPath() public String getPath()
{ {
return _pathInContext; return _dispatchPath;
} }
public void setTimeoutTask(Scheduler.Task task) public void setTimeoutTask(Scheduler.Task task)
@ -131,14 +131,15 @@ public class AsyncContextEvent extends AsyncEvent
_throwable=throwable; _throwable=throwable;
} }
public void setDispatchTarget(ServletContext context, String path) public void setDispatchContext(ServletContext context)
{ {
if (context!=null) _dispatchContext=context;
_dispatchContext=context; }
if (path!=null)
_pathInContext=path; public void setDispatchPath(String path)
{
_dispatchPath=path;
} }
public void completed() public void completed()
{ {

View File

@ -256,6 +256,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
HttpChannelState.Next next = _state.handling(); HttpChannelState.Next next = _state.handling();
while (next==Next.CONTINUE && getServer().isRunning()) while (next==Next.CONTINUE && getServer().isRunning())
{ {
boolean error=false;
try try
{ {
_request.setHandled(false); _request.setHandled(false);
@ -294,7 +295,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
{ {
String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest()); String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
if (error_page!=null) if (error_page!=null)
_state.getAsyncContextEvent().setDispatchTarget(_state.getContextHandler().getServletContext(),error_page); _state.getAsyncContextEvent().setDispatchPath(error_page);
} }
} }
else else
@ -307,10 +308,14 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
if ("ContinuationThrowable".equals(e.getClass().getSimpleName())) if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
LOG.ignore(e); LOG.ignore(e);
else else
{
error=true;
throw e; throw e;
}
} }
catch (Exception e) catch (Exception e)
{ {
error=true;
if (e instanceof EofException) if (e instanceof EofException)
LOG.debug(e); LOG.debug(e);
else else
@ -321,6 +326,8 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
} }
finally finally
{ {
if (error && _state.isAsyncStarted())
_state.errorComplete();
next = _state.unhandle(); next = _state.unhandle();
} }
} }

View File

@ -206,7 +206,6 @@ public class HttpChannelState
_responseWrapped=false; _responseWrapped=false;
return Next.CONTINUE; return Next.CONTINUE;
} }
} }
@ -314,20 +313,24 @@ public class HttpChannelState
{ {
case ASYNCSTARTED: case ASYNCSTARTED:
_state=State.REDISPATCHING; _state=State.REDISPATCHING;
_event.setDispatchTarget(context,path);
_dispatched=true; _dispatched=true;
return; dispatch=false;
break;
case ASYNCWAIT: case ASYNCWAIT:
dispatch=!_expired; dispatch=!_expired;
_state=State.REDISPATCH; _state=State.REDISPATCH;
_event.setDispatchTarget(context,path);
_dispatched=true; _dispatched=true;
break; break;
default: default:
throw new IllegalStateException(this.getStatusString()); throw new IllegalStateException(this.getStatusString());
} }
if (context!=null)
_event.setDispatchContext(context);
if (path!=null)
_event.setDispatchPath(path);
} }
if (dispatch) if (dispatch)
@ -437,6 +440,30 @@ public class HttpChannelState
} }
} }
public void errorComplete()
{
synchronized (this)
{
switch(_state)
{
case ASYNCSTARTED:
case REDISPATCHING:
_state=State.COMPLETECALLED;
break;
case COMPLETECALLED:
break;
default:
throw new IllegalStateException(this.getStatusString());
}
_dispatched=false;
_event.setDispatchContext(null);
_event.setDispatchPath(null);
}
}
protected void completed() protected void completed()
{ {
final List<AsyncListener> aListeners; final List<AsyncListener> aListeners;

View File

@ -2023,7 +2023,8 @@ public class Request implements HttpServletRequest
if (_async==null) if (_async==null)
_async=new AsyncContextState(state); _async=new AsyncContextState(state);
AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse); 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); state.startAsync(event);
return _async; return _async;
} }

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.servlet;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
@ -56,7 +55,6 @@ import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.QuietServletException; import org.eclipse.jetty.server.QuietServletException;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
@ -615,7 +613,7 @@ public class ServletHandler extends ScopedHandler
{ {
// Complete async errored requests // Complete async errored requests
if (th!=null && request.isAsyncStarted()) if (th!=null && request.isAsyncStarted())
request.getAsyncContext().complete(); baseRequest.getHttpChannelState().errorComplete();
if (servlet_holder!=null) if (servlet_holder!=null)
baseRequest.setHandled(true); baseRequest.setHandled(true);

View File

@ -122,8 +122,11 @@ public class AsyncContextTest
@Test @Test
public void testStartThrow() throws Exception 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" String request =
+ "Connection: close\r\n" + "\r\n"; "GET /ctx/startthrow HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Connection: close\r\n" +
"\r\n";
String responseString = _connector.getResponses(request); String responseString = _connector.getResponses(request);
BufferedReader br = new BufferedReader(new StringReader(responseString)); 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","PathInfo= /IOE",br.readLine());
Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: Test",br.readLine()); Assert.assertEquals("error servlet","EXCEPTION: 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: 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: 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 @Test
public void testDispatchAsyncContext() throws Exception public void testDispatchAsyncContext() throws Exception
@ -497,6 +562,20 @@ public class AsyncContextTest
if (request.getDispatcherType()==DispatcherType.REQUEST) if (request.getDispatcherType()==DispatcherType.REQUEST)
{ {
request.startAsync(request, response); 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")); throw new QuietServletException(new IOException("Test"));
} }
} }