From c3325e8b04471c594a731cbab3d30b95961ccc76 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 22 Apr 2013 11:33:44 +1000 Subject: [PATCH] 405530 Wrap AsyncContext to throw ISE after complete --- .../jetty/server/AsyncContextEvent.java | 153 +++++++++++ .../jetty/server/AsyncContextState.java | 180 +++++++++++++ .../jetty/server/HttpChannelState.java | 252 ++---------------- .../org/eclipse/jetty/server/Request.java | 30 ++- .../java/org/eclipse/jetty/server/Server.java | 13 +- .../server/handler/StatisticsHandler.java | 8 +- .../jetty/servlet/AsyncContextTest.java | 28 +- 7 files changed, 415 insertions(+), 249 deletions(-) create mode 100644 jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java create mode 100644 jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java 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 new file mode 100644 index 00000000000..11d334d3b94 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java @@ -0,0 +1,153 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server; + + +import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.eclipse.jetty.server.handler.ContextHandler.Context; +import org.eclipse.jetty.util.thread.Scheduler; + +public class AsyncContextEvent extends AsyncEvent +{ + final private Context _context; + final private AsyncContextState _asyncContext; + volatile HttpChannelState _state; + private ServletContext _dispatchContext; + private String _pathInContext; + private Scheduler.Task _timeoutTask; + private Throwable _throwable; + + public AsyncContextEvent(Context context,AsyncContextState asyncContext, HttpChannelState state, Request baseRequest, ServletRequest request, ServletResponse response) + { + super(null,request,response,null); + _context=context; + _asyncContext=asyncContext; + _state=state; + + // If we haven't been async dispatched before + if (baseRequest.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null) + { + // We are setting these attributes during startAsync, when the spec implies that + // they are only available after a call to AsyncContext.dispatch(...); + + // have we been forwarded before? + String uri=(String)baseRequest.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); + if (uri!=null) + { + baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri); + baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,baseRequest.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH)); + baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,baseRequest.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH)); + baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,baseRequest.getAttribute(RequestDispatcher.FORWARD_PATH_INFO)); + baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,baseRequest.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING)); + } + else + { + baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,baseRequest.getRequestURI()); + baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,baseRequest.getContextPath()); + baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,baseRequest.getServletPath()); + baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,baseRequest.getPathInfo()); + baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,baseRequest.getQueryString()); + } + } + } + + public ServletContext getSuspendedContext() + { + return _context; + } + + public Context getContext() + { + return _context; + } + + public ServletContext getDispatchContext() + { + return _dispatchContext; + } + + public ServletContext getServletContext() + { + return _dispatchContext==null?_context:_dispatchContext; + } + + /** + * @return The path in the context + */ + public String getPath() + { + return _pathInContext; + } + + public void setTimeoutTask(Scheduler.Task task) + { + _timeoutTask = task; + } + + public void cancelTimeoutTask() + { + Scheduler.Task task=_timeoutTask; + _timeoutTask=null; + if (task!=null) + task.cancel(); + } + + @Override + public AsyncContext getAsyncContext() + { + return _asyncContext; + } + + @Override + public Throwable getThrowable() + { + return _throwable; + } + + public void setThrowable(Throwable throwable) + { + _throwable=throwable; + } + + public void setDispatchTarget(ServletContext context, String path) + { + if (context!=null) + _dispatchContext=context; + if (path!=null) + _pathInContext=path; + } + + + public void completed() + { + _asyncContext.reset(); + } + + public HttpChannelState getHttpChannelState() + { + return _state; + } + +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java new file mode 100644 index 00000000000..1688ed6c64c --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java @@ -0,0 +1,180 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server; + +import java.io.IOException; + +import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +import javax.servlet.AsyncListener; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + + +public class AsyncContextState implements AsyncContext +{ + volatile HttpChannelState _state; + + public AsyncContextState(HttpChannelState state) + { + _state=state; + } + + private HttpChannelState state() + { + HttpChannelState state=_state; + if (state==null) + throw new IllegalStateException("AsyncContext completed"); + return state; + } + + @Override + public void addListener(final AsyncListener listener, final ServletRequest request, final ServletResponse response) + { + AsyncListener wrap = new AsyncListener() + { + @Override + public void onTimeout(AsyncEvent event) throws IOException + { + listener.onTimeout(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable())); + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException + { + listener.onStartAsync(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable())); + } + + @Override + public void onError(AsyncEvent event) throws IOException + { + listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable())); + } + + @Override + public void onComplete(AsyncEvent event) throws IOException + { + listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable())); + } + }; + state().addListener(wrap); + } + + @Override + public void addListener(AsyncListener listener) + { + state().addListener(listener); + } + + @Override + public void complete() + { + state().complete(); + } + + @Override + public T createListener(Class clazz) throws ServletException + { + try + { + return clazz.newInstance(); + } + catch(Exception e) + { + throw new ServletException(e); + } + } + + @Override + public void dispatch() + { + state().dispatch(null,null); + } + + @Override + public void dispatch(String path) + { + state().dispatch(null,path); + } + + @Override + public void dispatch(ServletContext context, String path) + { + state().dispatch(context,path); + } + + @Override + public ServletRequest getRequest() + { + return state().getAsyncContextEvent().getSuppliedRequest(); + } + + @Override + public ServletResponse getResponse() + { + return state().getAsyncContextEvent().getSuppliedResponse(); + } + + @Override + public long getTimeout() + { + return state().getTimeout(); + } + + @Override + public boolean hasOriginalRequestAndResponse() + { + HttpChannel channel=state().getHttpChannel(); + return channel.getRequest()==getRequest() && channel.getResponse()==getResponse(); + } + + @Override + public void setTimeout(long arg0) + { + state().setTimeout(arg0); + } + + @Override + public void start(final Runnable task) + { + state().getHttpChannel().execute(new Runnable() + { + @Override + public void run() + { + state().getAsyncContextEvent().getContext().getContextHandler().handle(task); + } + }); + } + + public void reset() + { + _state=null; + } + + public HttpChannelState getHttpChannelState() + { + return state(); + } + + + +} 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 e50f77ad4e7..a7f2b2c792c 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 @@ -21,19 +21,14 @@ package org.eclipse.jetty.server; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import javax.servlet.AsyncContext; -import javax.servlet.AsyncEvent; + import javax.servlet.AsyncListener; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandler.Context; -import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Scheduler; @@ -56,7 +51,7 @@ import org.eclipse.jetty.util.thread.Scheduler; * COMPLETED: * */ -public class HttpChannelState implements AsyncContext +public class HttpChannelState { private static final Logger LOG = Log.getLogger(HttpChannelState.class); @@ -86,7 +81,7 @@ public class HttpChannelState implements AsyncContext private boolean _expired; private volatile boolean _responseWrapped; private long _timeoutMs=DEFAULT_TIMEOUT; - private AsyncEventState _event; + private AsyncContextEvent _event; protected HttpChannelState(HttpChannel channel) { @@ -103,7 +98,6 @@ public class HttpChannelState implements AsyncContext } } - @Override public void addListener(AsyncListener listener) { synchronized(this) @@ -114,19 +108,6 @@ public class HttpChannelState implements AsyncContext } } - @Override - public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response) - { - synchronized(this) - { - if (_asyncListeners==null) - _asyncListeners=new ArrayList<>(); - _asyncListeners.add(listener); - } - } - - - @Override public void setTimeout(long ms) { synchronized(this) @@ -135,7 +116,6 @@ public class HttpChannelState implements AsyncContext } } - @Override public long getTimeout() { synchronized(this) @@ -144,7 +124,7 @@ public class HttpChannelState implements AsyncContext } } - public AsyncEventState getAsyncEventState() + public AsyncContextEvent getAsyncContextEvent() { synchronized(this) { @@ -218,7 +198,8 @@ public class HttpChannelState implements AsyncContext } } - public void startAsync() + + public void startAsync(AsyncContextEvent event) { synchronized (this) { @@ -228,56 +209,15 @@ public class HttpChannelState implements AsyncContext case REDISPATCHED: _dispatched=false; _expired=false; + _responseWrapped=event.getSuppliedResponse()!=_channel.getResponse(); _responseWrapped=false; - _event=new AsyncEventState(_channel.getRequest().getServletContext(),_channel.getRequest(),_channel.getResponse()); + _event=event; _state=State.ASYNCSTARTED; List listeners=_lastAsyncListeners; _lastAsyncListeners=_asyncListeners; + if (listeners!=null) + listeners.clear(); _asyncListeners=listeners; - if (_asyncListeners!=null) - _asyncListeners.clear(); - break; - - default: - throw new IllegalStateException(this.getStatusString()); - } - } - - if (_lastAsyncListeners!=null) - { - for (AsyncListener listener : _lastAsyncListeners) - { - try - { - listener.onStartAsync(_event); - } - catch(Exception e) - { - LOG.warn(e); - } - } - } - } - - public void startAsync(final ServletContext context,final ServletRequest request,final ServletResponse response) - { - synchronized (this) - { - switch(_state) - { - case DISPATCHED: - case REDISPATCHED: - _dispatched=false; - _expired=false; - _responseWrapped=response!=_channel.getResponse(); - _event=new AsyncEventState(context,request,response); - _event._pathInContext = (request instanceof HttpServletRequest)?URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo()):null; - _state=State.ASYNCSTARTED; - List listeners=_lastAsyncListeners; - _lastAsyncListeners=_asyncListeners; - _asyncListeners=listeners; - if (_asyncListeners!=null) - _asyncListeners.clear(); break; default: @@ -306,7 +246,7 @@ public class HttpChannelState implements AsyncContext synchronized (this) { if (_event!=null) - _event._cause=th; + _event.setThrowable(th); } } @@ -362,26 +302,29 @@ public class HttpChannelState implements AsyncContext } } - @Override - public void dispatch() + public void dispatch(ServletContext context, String path) { boolean dispatch; synchronized (this) { + switch(_state) { case ASYNCSTARTED: _state=State.REDISPATCHING; + _event.setDispatchTarget(context,path); _dispatched=true; return; case ASYNCWAIT: dispatch=!_expired; _state=State.REDISPATCH; + _event.setDispatchTarget(context,path); _dispatched=true; break; case REDISPATCH: + _event.setDispatchTarget(context,path); return; default: @@ -453,7 +396,6 @@ public class HttpChannelState implements AsyncContext scheduleDispatch(); } - @Override public void complete() { // just like resume, except don't set _dispatched=true; @@ -488,19 +430,6 @@ public class HttpChannelState implements AsyncContext } } - @Override - public T createListener(Class clazz) throws ServletException - { - try - { - return clazz.newInstance(); - } - catch(Exception e) - { - throw new ServletException(e); - } - } - protected void completed() { final List aListeners; @@ -524,10 +453,10 @@ public class HttpChannelState implements AsyncContext { try { - if (_event!=null && _event._cause!=null) + if (_event!=null && _event.getThrowable()!=null) { - _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,_event._cause); - _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,_event._cause.getMessage()); + _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,_event.getThrowable()); + _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,_event.getThrowable().getMessage()); listener.onError(_event); } else @@ -539,6 +468,7 @@ public class HttpChannelState implements AsyncContext } } } + _event.completed(); } protected void recycle() @@ -563,14 +493,6 @@ public class HttpChannelState implements AsyncContext } } - public void cancel() - { - synchronized (this) - { - cancelTimeout(); - } - } - protected void scheduleDispatch() { _channel.execute(_channel); @@ -580,18 +502,14 @@ public class HttpChannelState implements AsyncContext { Scheduler scheduler = _channel.getScheduler(); if (scheduler!=null && _timeoutMs>0) - _event._timeout=scheduler.schedule(new AsyncTimeout(),_timeoutMs,TimeUnit.MILLISECONDS); + _event.setTimeoutTask(scheduler.schedule(new AsyncTimeout(),_timeoutMs,TimeUnit.MILLISECONDS)); } protected void cancelTimeout() { - AsyncEventState event=_event; + AsyncContextEvent event=_event; if (event!=null) - { - Scheduler.Task task=event._timeout; - if (task!=null) - task.cancel(); - } + event.cancelTimeoutTask(); } public boolean isExpired() @@ -656,71 +574,19 @@ public class HttpChannelState implements AsyncContext } } - @Override - public void dispatch(ServletContext context, String path) - { - _event._dispatchContext=context; - _event._pathInContext=path; - dispatch(); - } - - @Override - public void dispatch(String path) - { - _event._pathInContext=path; - dispatch(); - } - public Request getBaseRequest() { return _channel.getRequest(); } - @Override - public ServletRequest getRequest() + public HttpChannel getHttpChannel() { - if (_event!=null) - return _event.getSuppliedRequest(); - return _channel.getRequest(); - } - - @Override - public ServletResponse getResponse() - { - if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null) - return _event.getSuppliedResponse(); - return _channel.getResponse(); - } - - @Override - public void start(final Runnable run) - { - final AsyncEventState event=_event; - if (event!=null) - { - _channel.execute(new Runnable() - { - @Override - public void run() - { - ((Context)event.getServletContext()).getContextHandler().handle(run); - } - }); - } - } - - @Override - public boolean hasOriginalRequestAndResponse() - { - synchronized (this) - { - return (_event!=null && _event.getSuppliedRequest()==_channel.getRequest() && _event.getSuppliedResponse()==_channel.getResponse()); - } + return _channel; } public ContextHandler getContextHandler() { - final AsyncEventState event=_event; + final AsyncContextEvent event=_event; if (event!=null) return ((Context)event.getServletContext()).getContextHandler(); return null; @@ -757,70 +623,4 @@ public class HttpChannelState implements AsyncContext } } - public class AsyncEventState extends AsyncEvent - { - final private ServletContext _suspendedContext; - private String _pathInContext; - private Scheduler.Task _timeout; - private ServletContext _dispatchContext; - private Throwable _cause; - - public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response) - { - super(HttpChannelState.this, request,response); - _suspendedContext=context; - - // Get the base request So we can remember the initial paths - Request r=_channel.getRequest(); - - // If we haven't been async dispatched before - if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null) - { - // We are setting these attributes during startAsync, when the spec implies that - // they are only available after a call to AsyncContext.dispatch(...); - - // have we been forwarded before? - String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); - if (uri!=null) - { - r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri); - r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH)); - r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH)); - r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO)); - r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING)); - } - else - { - r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI()); - r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath()); - r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath()); - r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo()); - r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString()); - } - } - } - - public ServletContext getSuspendedContext() - { - return _suspendedContext; - } - - public ServletContext getDispatchContext() - { - return _dispatchContext; - } - - public ServletContext getServletContext() - { - return _dispatchContext==null?_suspendedContext:_dispatchContext; - } - - /** - * @return The path in the context - */ - public String getPath() - { - return _pathInContext; - } - } } 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 f2518ff90cb..94c98f6c646 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 @@ -203,7 +203,8 @@ public class Request implements HttpServletRequest private long _dispatchTime; private HttpURI _uri; private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime - + private AsyncContextState _async; + /* ------------------------------------------------------------ */ public Request(HttpChannel channel, HttpInput input) { @@ -369,10 +370,11 @@ public class Request implements HttpServletRequest @Override public AsyncContext getAsyncContext() { - HttpChannelState continuation = getHttpChannelState(); - if (continuation.isInitial() && !continuation.isAsync()) - throw new IllegalStateException(continuation.getStatusString()); - return continuation; + HttpChannelState state = getHttpChannelState(); + if (_async==null || state.isInitial() && !state.isAsync()) + throw new IllegalStateException(state.getStatusString()); + + return _async; } /* ------------------------------------------------------------ */ @@ -1506,6 +1508,9 @@ public class Request implements HttpServletRequest setAuthentication(Authentication.NOT_CHECKED); getHttpChannelState().recycle(); + if (_async!=null) + _async.reset(); + _async=null; _asyncSupported = true; _handled = false; if (_context != null) @@ -1989,8 +1994,11 @@ public class Request implements HttpServletRequest if (!_asyncSupported) throw new IllegalStateException("!asyncSupported"); HttpChannelState state = getHttpChannelState(); - state.startAsync(); - return state; + if (_async==null) + _async=new AsyncContextState(state); + AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,this,getResponse()); + state.startAsync(event); + return _async; } /* ------------------------------------------------------------ */ @@ -2000,8 +2008,12 @@ public class Request implements HttpServletRequest if (!_asyncSupported) throw new IllegalStateException("!asyncSupported"); HttpChannelState state = getHttpChannelState(); - state.startAsync(_context, servletRequest, servletResponse); - return state; + if (_async==null) + _async=new AsyncContextState(state); + AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse); + event.setDispatchTarget(getServletContext(),URIUtil.addPaths(getServletPath(),getPathInfo())); + state.startAsync(event); + return _async; } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index e8e03113ade..81023763a35 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -476,16 +476,16 @@ public class Server extends HandlerWrapper implements Attributes */ public void handleAsync(HttpChannel connection) throws IOException, ServletException { - final HttpChannelState async = connection.getRequest().getHttpChannelState(); - final HttpChannelState.AsyncEventState state = async.getAsyncEventState(); + final HttpChannelState state = connection.getRequest().getHttpChannelState(); + final AsyncContextEvent event = state.getAsyncContextEvent(); final Request baseRequest=connection.getRequest(); - final String path=state.getPath(); + final String path=event.getPath(); if (path!=null) { // this is a dispatch with a path - ServletContext context=state.getServletContext(); + ServletContext context=event.getServletContext(); HttpURI uri = new HttpURI(context==null?path:URIUtil.addPaths(context.getContextPath(),path)); baseRequest.setUri(uri); baseRequest.setRequestURI(null); @@ -495,8 +495,8 @@ public class Server extends HandlerWrapper implements Attributes } final String target=baseRequest.getPathInfo(); - final HttpServletRequest request=(HttpServletRequest)async.getRequest(); - final HttpServletResponse response=(HttpServletResponse)async.getResponse(); + final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest(); + final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse(); if (LOG.isDebugEnabled()) { @@ -509,7 +509,6 @@ public class Server extends HandlerWrapper implements Attributes } - /* ------------------------------------------------------------ */ public void join() throws InterruptedException { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java index 7a2ac7052a0..78e8eb71ef3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java @@ -28,6 +28,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.AsyncContextEvent; import org.eclipse.jetty.server.HttpChannelState; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; @@ -76,12 +77,13 @@ public class StatisticsHandler extends HandlerWrapper public void onError(AsyncEvent event) throws IOException { } - + @Override public void onComplete(AsyncEvent event) throws IOException { - HttpChannelState state = (HttpChannelState)event.getAsyncContext(); + HttpChannelState state = ((AsyncContextEvent)event).getHttpChannelState(); + Request request = state.getBaseRequest(); final long elapsed = System.currentTimeMillis()-request.getTimeStamp(); @@ -93,7 +95,7 @@ public class StatisticsHandler extends HandlerWrapper if (!state.isDispatched()) _asyncWaitStats.decrement(); } - + }; /** 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 b66764bc15c..a34d185bd05 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 @@ -103,6 +103,7 @@ public class AsyncContextTest Assert.assertEquals("servlet gets right path", "doGet:getServletPath:/servletPath", br.readLine()); Assert.assertEquals("async context gets right path in get","doGet:async:getServletPath:/servletPath",br.readLine()); Assert.assertEquals("async context gets right path in async","async:run:attr:servletPath:/servletPath",br.readLine()); + } @Test @@ -121,6 +122,16 @@ public class AsyncContextTest Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true",br.readLine()); Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:",br.readLine()); Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/servletPath",br.readLine()); + + try + { + __asyncContext.getRequest(); + Assert.fail(); + } + catch (IllegalStateException e) + { + + } } @Test @@ -193,8 +204,11 @@ public class AsyncContextTest @Test public void testDispatchRequestResponse() throws Exception { - String request = "GET /forward?dispatchRequestResponse=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 request = "GET /forward?dispatchRequestResponse=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); @@ -233,10 +247,12 @@ public class AsyncContextTest } } + public static volatile AsyncContext __asyncContext; + private class AsyncDispatchingServlet extends HttpServlet { private static final long serialVersionUID = 1L; - + @Override protected void doGet(HttpServletRequest req, final HttpServletResponse response) throws ServletException, IOException { @@ -253,13 +269,14 @@ public class AsyncContextTest { wrapped = true; asyncContext = request.startAsync(request, new Wrapper(response)); + __asyncContext=asyncContext; } else { asyncContext = request.startAsync(); + __asyncContext=asyncContext; } - new Thread(new DispatchingRunnable(asyncContext, wrapped)).start(); } } @@ -301,12 +318,14 @@ public class AsyncContextTest if (request.getParameter("dispatch") != null) { AsyncContext asyncContext = request.startAsync(request,response); + __asyncContext=asyncContext; asyncContext.dispatch("/servletPath2"); } else { response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n"); AsyncContext asyncContext = request.startAsync(request,response); + __asyncContext=asyncContext; response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n"); asyncContext.start(new AsyncRunnable(asyncContext)); @@ -323,6 +342,7 @@ public class AsyncContextTest { response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n"); AsyncContext asyncContext = request.startAsync(request, response); + __asyncContext=asyncContext; response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n"); asyncContext.start(new AsyncRunnable(asyncContext)); }