diff options
Diffstat (limited to 'jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java')
-rw-r--r-- | jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java | 464 |
1 files changed, 242 insertions, 222 deletions
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 127bafa3c1..470acc0a02 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,7 +21,6 @@ package org.eclipse.jetty.server; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; - import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.RequestDispatcher; @@ -62,32 +61,41 @@ public class HttpChannelState { IDLE, // Idle request DISPATCHED, // Request dispatched to filter/servlet - ASYNCSTARTED, // Suspend called, but not yet returned to container - REDISPATCHING, // resumed while dispatched ASYNCWAIT, // Suspended and parked - REDISPATCH, // Has been scheduled - REDISPATCHED, // Request redispatched to filter/servlet - COMPLETECALLED,// complete called + ASYNCIO, // Has been dispatched for async IO COMPLETING, // Request is completable COMPLETED // Request is complete } - public enum Next + public enum Action + { + REQUEST_DISPATCH, // handle a normal request dispatch + ASYNC_DISPATCH, // handle an async request dispatch + ASYNC_EXPIRED, // handle an async timeout + WRITE_CALLBACK, // handle an IO write callback + READ_CALLBACK, // handle an IO read callback + WAIT, // Wait for further events + COMPLETE // Complete the channel + } + + public enum Async { - CONTINUE, // Continue handling the channel - WAIT, // Wait for further events - COMPLETE // Complete the channel + STARTED, + DISPATCH, + COMPLETE, + EXPIRING, + EXPIRED } + private final boolean DEBUG=LOG.isDebugEnabled(); private final HttpChannel<?> _channel; - private List<AsyncListener> _lastAsyncListeners; - private List<AsyncListener> _asyncListeners; + private List<AsyncListener> _asyncListeners; private State _state; + private Async _async; private boolean _initial; - private boolean _dispatched; - private boolean _expired; - private volatile boolean _responseWrapped; + private boolean _asyncRead; + private boolean _asyncWrite; private long _timeoutMs=DEFAULT_TIMEOUT; private AsyncContextEvent _event; @@ -95,6 +103,7 @@ public class HttpChannelState { _channel=channel; _state=State.IDLE; + _async=null; _initial=true; } @@ -145,7 +154,7 @@ public class HttpChannelState { synchronized (this) { - return super.toString()+"@"+getStatusString(); + return String.format("%s@%x{s=%s i=%b a=%s}",getClass().getSimpleName(),hashCode(),_state,_initial,_async); } } @@ -153,97 +162,102 @@ public class HttpChannelState { synchronized (this) { - return _state+ - (_initial?",initial":"")+ - (_dispatched?",resumed":"")+ - (_expired?",expired":""); + return String.format("s=%s i=%b a=%s",_state,_initial,_async); } } /** * @return Next handling of the request should proceed */ - protected Next handling() + protected Action handling() { synchronized (this) { + if(DEBUG) + LOG.debug("{} handling {}",this,_state); switch(_state) { case IDLE: _initial=true; _state=State.DISPATCHED; - if (_lastAsyncListeners!=null) - _lastAsyncListeners.clear(); - if (_asyncListeners!=null) - _asyncListeners.clear(); - else - { - _asyncListeners=_lastAsyncListeners; - _lastAsyncListeners=null; - } - break; - - case COMPLETECALLED: - _state=State.COMPLETING; - return Next.COMPLETE; + return Action.REQUEST_DISPATCH; case COMPLETING: - return Next.COMPLETE; - - case ASYNCWAIT: - return Next.WAIT; + return Action.COMPLETE; case COMPLETED: - return Next.WAIT; + return Action.WAIT; - case REDISPATCH: - _state=State.REDISPATCHED; - break; + case ASYNCWAIT: + if (_asyncRead) + { + _state=State.ASYNCIO; + _asyncRead=false; + return Action.READ_CALLBACK; + } + if (_asyncWrite) + { + _state=State.ASYNCIO; + _asyncWrite=false; + return Action.WRITE_CALLBACK; + } + + if (_async!=null) + { + Async async=_async; + switch(async) + { + case COMPLETE: + _state=State.COMPLETING; + return Action.COMPLETE; + case DISPATCH: + _state=State.DISPATCHED; + _async=null; + return Action.ASYNC_DISPATCH; + case EXPIRING: + break; + case EXPIRED: + _state=State.DISPATCHED; + _async=null; + return Action.ASYNC_EXPIRED; + case STARTED: + if (DEBUG) + LOG.debug("TODO Fix this double dispatch",new IllegalStateException(this + .getStatusString())); + return Action.WAIT; + } + } + + return Action.WAIT; default: throw new IllegalStateException(this.getStatusString()); } - - _responseWrapped=false; - return Next.CONTINUE; - } } - public void startAsync(AsyncContextEvent event) { + final List<AsyncListener> lastAsyncListeners; + synchronized (this) { - switch(_state) - { - case DISPATCHED: - case REDISPATCHED: - _dispatched=false; - _expired=false; - _responseWrapped=event.getSuppliedResponse()!=_channel.getResponse(); - _responseWrapped=false; - _event=event; - _state=State.ASYNCSTARTED; - List<AsyncListener> listeners=_lastAsyncListeners; - _lastAsyncListeners=_asyncListeners; - if (listeners!=null) - listeners.clear(); - _asyncListeners=listeners; - break; - - default: - throw new IllegalStateException(this.getStatusString()); - } + if (_state!=State.DISPATCHED || _async!=null) + throw new IllegalStateException(this.getStatusString()); + + _async=Async.STARTED; + _event=event; + lastAsyncListeners=_asyncListeners; + _asyncListeners=null; } - if (_lastAsyncListeners!=null) + if (lastAsyncListeners!=null) { - for (AsyncListener listener : _lastAsyncListeners) + for (AsyncListener listener : lastAsyncListeners) { try { - listener.onStartAsync(_event); + listener.onStartAsync(event); } catch(Exception e) { @@ -269,39 +283,63 @@ public class HttpChannelState * @return next actions * be handled again (eg because of a resume that happened before unhandle was called) */ - protected Next unhandle() + protected Action unhandle() { synchronized (this) { + if(DEBUG) + LOG.debug("{} unhandle {}",this,_state); + switch(_state) { - case REDISPATCHED: case DISPATCHED: - _state=State.COMPLETING; - return Next.COMPLETE; - - case IDLE: + case ASYNCIO: + break; + default: throw new IllegalStateException(this.getStatusString()); + } - case ASYNCSTARTED: - _initial=false; - _state=State.ASYNCWAIT; - scheduleTimeout(); - return Next.WAIT; - - case REDISPATCHING: - _initial=false; - _state=State.REDISPATCHED; - return Next.CONTINUE; - - case COMPLETECALLED: - _initial=false; - _state=State.COMPLETING; - return Next.COMPLETE; + if (_asyncRead) + { + _state=State.ASYNCIO; + _asyncRead=false; + return Action.READ_CALLBACK; + } + + if (_asyncWrite) + { + _asyncWrite=false; + _state=State.ASYNCIO; + return Action.WRITE_CALLBACK; + } - default: - throw new IllegalStateException(this.getStatusString()); + if (_async!=null) + { + _initial=false; + switch(_async) + { + case COMPLETE: + _state=State.COMPLETING; + _async=null; + return Action.COMPLETE; + case DISPATCH: + _state=State.DISPATCHED; + _async=null; + return Action.ASYNC_DISPATCH; + case EXPIRED: + _state=State.DISPATCHED; + _async=null; + return Action.ASYNC_EXPIRED; + case EXPIRING: + case STARTED: + scheduleTimeout(); + _state=State.ASYNCWAIT; + return Action.WAIT; + } } + + _state=State.COMPLETING; + return Action.COMPLETE; } } @@ -310,39 +348,26 @@ public class HttpChannelState boolean dispatch; synchronized (this) { + if (_async!=Async.STARTED && _async!=Async.EXPIRING) + throw new IllegalStateException("AsyncContext#dispath "+this.getStatusString()); + _async=Async.DISPATCH; + _event.setDispatchTarget(context,path); + 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; + case DISPATCHED: + case ASYNCIO: + dispatch=false; break; - default: - throw new IllegalStateException(this.getStatusString()); + dispatch=true; + break; } } + cancelTimeout(); if (dispatch) - { - cancelTimeout(); scheduleDispatch(); - } - } - - public boolean isDispatched() - { - synchronized (this) - { - return _dispatched; - } } protected void expired() @@ -351,17 +376,11 @@ public class HttpChannelState AsyncEvent event; synchronized (this) { - switch(_state) - { - case ASYNCSTARTED: - case ASYNCWAIT: - _expired=true; - event=_event; - aListeners=_asyncListeners; - break; - default: - return; - } + if (_async!=Async.STARTED) + return; + _async=Async.EXPIRING; + event=_event; + aListeners=_asyncListeners; } if (aListeners!=null) @@ -378,22 +397,20 @@ public class HttpChannelState } } } - + + boolean dispatch=false; synchronized (this) { - switch(_state) + if (_async==Async.EXPIRING) { - case ASYNCSTARTED: - case ASYNCWAIT: - _state=State.REDISPATCH; - break; - default: - _expired=false; - break; + _async=Async.EXPIRED; + if (_state==State.ASYNCWAIT) + dispatch=true; } } - scheduleDispatch(); + if (dispatch) + scheduleDispatch(); } public void complete() @@ -402,30 +419,15 @@ public class HttpChannelState boolean handle; synchronized (this) { - switch(_state) - { - case DISPATCHED: - case REDISPATCHED: - throw new IllegalStateException(this.getStatusString()); - - case IDLE: - case ASYNCSTARTED: - _state=State.COMPLETECALLED; - return; - - case ASYNCWAIT: - _state=State.COMPLETECALLED; - handle=!_expired; - break; - - default: - throw new IllegalStateException(this.getStatusString()); - } + if (_async!=Async.STARTED && _async!=Async.EXPIRING) + throw new IllegalStateException(this.getStatusString()); + _async=Async.COMPLETE; + handle=_state==State.ASYNCWAIT; } + cancelTimeout(); if (handle) { - cancelTimeout(); ContextHandler handler=getContextHandler(); if (handler!=null) handler.handle(_channel); @@ -453,30 +455,34 @@ public class HttpChannelState } } - if (aListeners!=null) + if (event!=null) { - for (AsyncListener listener : aListeners) + if (aListeners!=null) { - try + if (event.getThrowable()!=null) { - if (event!=null && event.getThrowable()!=null) - { - event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable()); - event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage()); - listener.onError(event); - } - else - listener.onComplete(event); + event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable()); + event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage()); } - catch(Exception e) + + for (AsyncListener listener : aListeners) { - LOG.warn(e); + try + { + if (event.getThrowable()!=null) + listener.onError(event); + else + listener.onComplete(event); + } + catch(Exception e) + { + LOG.warn(e); + } } } - } - if (event!=null) event.completed(); + } } protected void recycle() @@ -486,17 +492,19 @@ public class HttpChannelState switch(_state) { case DISPATCHED: - case REDISPATCHED: + case ASYNCIO: throw new IllegalStateException(getStatusString()); default: - _state=State.IDLE; + break; } - _initial = true; - _dispatched=false; - _expired=false; - _responseWrapped=false; - cancelTimeout(); + _asyncListeners=null; + _state=State.IDLE; + _async=null; + _initial=true; + _asyncRead=false; + _asyncWrite=false; _timeoutMs=DEFAULT_TIMEOUT; + cancelTimeout(); _event=null; } } @@ -515,7 +523,11 @@ public class HttpChannelState protected void cancelTimeout() { - AsyncContextEvent event=_event; + final AsyncContextEvent event; + synchronized (this) + { + event=_event; + } if (event!=null) event.cancelTimeoutTask(); } @@ -524,7 +536,7 @@ public class HttpChannelState { synchronized (this) { - return _expired; + return _async==Async.EXPIRED; } } @@ -540,17 +552,7 @@ public class HttpChannelState { synchronized(this) { - switch(_state) - { - case ASYNCSTARTED: - case REDISPATCHING: - case COMPLETECALLED: - case ASYNCWAIT: - return true; - - default: - return false; - } + return _state==State.ASYNCWAIT || _state==State.DISPATCHED && _async==Async.STARTED; } } @@ -573,18 +575,10 @@ public class HttpChannelState public boolean isAsyncStarted() { synchronized (this) - { - switch(_state) - { - case ASYNCSTARTED: // Suspend called, but not yet returned to container - case REDISPATCHING: // resumed while dispatched - case COMPLETECALLED: // complete called - case ASYNCWAIT: - return true; - - default: - return false; - } + { + if (_state==State.DISPATCHED) + return _async!=null; + return _async==Async.STARTED || _async==Async.EXPIRING; } } @@ -592,19 +586,7 @@ public class HttpChannelState { synchronized (this) { - switch(_state) - { - case ASYNCSTARTED: - case REDISPATCHING: - case ASYNCWAIT: - case REDISPATCHED: - case REDISPATCH: - case COMPLETECALLED: - return true; - - default: - return false; - } + return !_initial || _async!=null; } } @@ -620,7 +602,12 @@ public class HttpChannelState public ContextHandler getContextHandler() { - final AsyncContextEvent event=_event; + final AsyncContextEvent event; + synchronized (this) + { + event=_event; + } + if (event!=null) { Context context=((Context)event.getServletContext()); @@ -632,8 +619,13 @@ public class HttpChannelState public ServletResponse getServletResponse() { - if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null) - return _event.getSuppliedResponse(); + final AsyncContextEvent event; + synchronized (this) + { + event=_event; + } + if (event!=null && event.getSuppliedResponse()!=null) + return event.getSuppliedResponse(); return _channel.getResponse(); } @@ -652,6 +644,34 @@ public class HttpChannelState _channel.getRequest().setAttribute(name,attribute); } + public void onReadPossible() + { + boolean handle; + + synchronized (this) + { + _asyncRead=true; + handle=_state==State.ASYNCWAIT; + } + + if (handle) + _channel.execute(_channel); + } + + public void onWritePossible() + { + boolean handle; + + synchronized (this) + { + _asyncWrite=true; + handle=_state==State.ASYNCWAIT; + } + + if (handle) + _channel.execute(_channel); + } + public class AsyncTimeout implements Runnable { @Override |