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:
parent
ed1555e0c4
commit
1d5635c76c
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue