2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.server;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.concurrent.TimeUnit;
25 import javax.servlet.AsyncListener;
26 import javax.servlet.RequestDispatcher;
27 import javax.servlet.ServletContext;
28 import javax.servlet.ServletResponse;
30 import org.eclipse.jetty.server.handler.ContextHandler;
31 import org.eclipse.jetty.server.handler.ContextHandler.Context;
32 import org.eclipse.jetty.util.log.Log;
33 import org.eclipse.jetty.util.log.Logger;
34 import org.eclipse.jetty.util.thread.Scheduler;
37 * Implementation of AsyncContext interface that holds the state of request-response cycle.
39 public class HttpChannelState
41 private static final Logger LOG = Log.getLogger(HttpChannelState.class);
43 private final static long DEFAULT_TIMEOUT=30000L;
45 /** The dispatched state of the HttpChannel, used to control the overall livecycle
50 DISPATCHED, // Request dispatched to filter/servlet
51 ASYNC_WAIT, // Suspended and parked
52 ASYNC_WOKEN, // A thread has been dispatch to handle from ASYNCWAIT
53 ASYNC_IO, // Has been dispatched for async IO
54 COMPLETING, // Request is completable
55 COMPLETED // Request is complete
59 * The actions to take as the channel moves from state to state.
63 REQUEST_DISPATCH, // handle a normal request dispatch
64 ASYNC_DISPATCH, // handle an async request dispatch
65 ASYNC_EXPIRED, // handle an async timeout
66 WRITE_CALLBACK, // handle an IO write callback
67 READ_CALLBACK, // handle an IO read callback
68 WAIT, // Wait for further events
69 COMPLETE // Complete the channel
73 * The state of the servlet async API. This can lead or follow the
74 * channel dispatch state and also includes reasons such as expired,
75 * dispatched or completed.
86 private final boolean DEBUG=LOG.isDebugEnabled();
87 private final HttpChannel<?> _channel;
89 private List<AsyncListener> _asyncListeners;
92 private boolean _initial;
93 private boolean _asyncRead;
94 private boolean _asyncWrite;
95 private long _timeoutMs=DEFAULT_TIMEOUT;
96 private AsyncContextEvent _event;
98 protected HttpChannelState(HttpChannel<?> channel)
106 public State getState()
114 public void addListener(AsyncListener listener)
118 if (_asyncListeners==null)
119 _asyncListeners=new ArrayList<>();
120 _asyncListeners.add(listener);
124 public void setTimeout(long ms)
132 public long getTimeout()
140 public AsyncContextEvent getAsyncContextEvent()
149 public String toString()
153 return String.format("%s@%x{s=%s i=%b a=%s}",getClass().getSimpleName(),hashCode(),_state,_initial,_async);
157 public String getStatusString()
161 return String.format("s=%s i=%b a=%s",_state,_initial,_async);
166 * @return Next handling of the request should proceed
168 protected Action handling()
173 LOG.debug("{} handling {}",this,_state);
178 _state=State.DISPATCHED;
179 return Action.REQUEST_DISPATCH;
182 return Action.COMPLETE;
190 _state=State.ASYNC_IO;
192 return Action.READ_CALLBACK;
196 _state=State.ASYNC_IO;
198 return Action.WRITE_CALLBACK;
207 _state=State.COMPLETING;
208 return Action.COMPLETE;
210 _state=State.DISPATCHED;
212 return Action.ASYNC_DISPATCH;
216 _state=State.DISPATCHED;
218 return Action.ASYNC_EXPIRED;
222 LOG.debug("TODO Fix this double dispatch",new IllegalStateException(this
223 .getStatusString()));
231 throw new IllegalStateException(this.getStatusString());
236 public void startAsync(AsyncContextEvent event)
238 final List<AsyncListener> lastAsyncListeners;
242 if (_state!=State.DISPATCHED || _async!=null)
243 throw new IllegalStateException(this.getStatusString());
245 _async=Async.STARTED;
247 lastAsyncListeners=_asyncListeners;
248 _asyncListeners=null;
251 if (lastAsyncListeners!=null)
253 for (AsyncListener listener : lastAsyncListeners)
257 listener.onStartAsync(event);
267 protected void error(Throwable th)
272 _event.setThrowable(th);
277 * Signal that the HttpConnection has finished handling the request.
278 * For blocking connectors, this call may block if the request has
279 * been suspended (startAsync called).
280 * @return next actions
281 * be handled again (eg because of a resume that happened before unhandle was called)
283 protected Action unhandle()
288 LOG.debug("{} unhandle {}",this,_state);
296 throw new IllegalStateException(this.getStatusString());
301 _state=State.ASYNC_IO;
303 return Action.READ_CALLBACK;
309 _state=State.ASYNC_IO;
310 return Action.WRITE_CALLBACK;
319 _state=State.COMPLETING;
321 return Action.COMPLETE;
323 _state=State.DISPATCHED;
325 return Action.ASYNC_DISPATCH;
327 _state=State.DISPATCHED;
329 return Action.ASYNC_EXPIRED;
333 _state=State.ASYNC_WAIT;
338 _state=State.COMPLETING;
339 return Action.COMPLETE;
343 public void dispatch(ServletContext context, String path)
348 if (_async!=Async.STARTED && _async!=Async.EXPIRING)
349 throw new IllegalStateException("AsyncContext#dispath "+this.getStatusString());
350 _async=Async.DISPATCH;
353 _event.setDispatchContext(context);
355 _event.setDispatchPath(path);
364 _state=State.ASYNC_WOKEN;
371 LOG.warn("async dispatched when complete {}",this);
382 protected void expired()
384 final List<AsyncListener> aListeners;
385 AsyncContextEvent event;
388 if (_async!=Async.STARTED)
390 _async=Async.EXPIRING;
392 aListeners=_asyncListeners;
395 if (aListeners!=null)
397 for (AsyncListener listener : aListeners)
401 listener.onTimeout(event);
406 event.setThrowable(e);
407 _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
413 boolean dispatch=false;
416 if (_async==Async.EXPIRING)
418 _async=Async.EXPIRED;
419 if (_state==State.ASYNC_WAIT)
421 _state=State.ASYNC_WOKEN;
431 public void complete()
433 // just like resume, except don't set _dispatched=true;
434 boolean handle=false;
437 if (_async!=Async.STARTED && _async!=Async.EXPIRING)
438 throw new IllegalStateException(this.getStatusString());
439 _async=Async.COMPLETE;
440 if (_state==State.ASYNC_WAIT)
443 _state=State.ASYNC_WOKEN;
450 ContextHandler handler=getContextHandler();
452 handler.handle(_channel);
458 public void errorComplete()
462 _async=Async.COMPLETE;
463 _event.setDispatchContext(null);
464 _event.setDispatchPath(null);
470 protected void completed()
472 final List<AsyncListener> aListeners;
473 final AsyncContextEvent event;
479 _state=State.COMPLETED;
480 aListeners=_asyncListeners;
485 throw new IllegalStateException(this.getStatusString());
491 if (aListeners!=null)
493 if (event.getThrowable()!=null)
495 event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
496 event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage());
499 for (AsyncListener listener : aListeners)
503 if (event.getThrowable()!=null)
504 listener.onError(event);
506 listener.onComplete(event);
519 protected void recycle()
527 throw new IllegalStateException(getStatusString());
531 _asyncListeners=null;
537 _timeoutMs=DEFAULT_TIMEOUT;
543 protected void scheduleDispatch()
545 _channel.execute(_channel);
548 protected void scheduleTimeout()
550 Scheduler scheduler = _channel.getScheduler();
551 if (scheduler!=null && _timeoutMs>0)
552 _event.setTimeoutTask(scheduler.schedule(_event,_timeoutMs,TimeUnit.MILLISECONDS));
555 protected void cancelTimeout()
557 final AsyncContextEvent event;
563 event.cancelTimeoutTask();
566 public boolean isExpired()
570 return _async==Async.EXPIRED;
574 public boolean isInitial()
582 public boolean isSuspended()
586 return _state==State.ASYNC_WAIT || _state==State.DISPATCHED && _async==Async.STARTED;
590 boolean isCompleting()
594 return _state==State.COMPLETING;
598 boolean isCompleted()
602 return _state == State.COMPLETED;
606 public boolean isAsyncStarted()
610 if (_state==State.DISPATCHED)
612 return _async==Async.STARTED || _async==Async.EXPIRING;
616 public boolean isAsync()
620 return !_initial || _async!=null;
624 public Request getBaseRequest()
626 return _channel.getRequest();
629 public HttpChannel<?> getHttpChannel()
634 public ContextHandler getContextHandler()
636 final AsyncContextEvent event;
644 Context context=((Context)event.getServletContext());
646 return context.getContextHandler();
651 public ServletResponse getServletResponse()
653 final AsyncContextEvent event;
658 if (event!=null && event.getSuppliedResponse()!=null)
659 return event.getSuppliedResponse();
660 return _channel.getResponse();
663 public Object getAttribute(String name)
665 return _channel.getRequest().getAttribute(name);
668 public void removeAttribute(String name)
670 _channel.getRequest().removeAttribute(name);
673 public void setAttribute(String name, Object attribute)
675 _channel.getRequest().setAttribute(name,attribute);
678 public void onReadPossible()
680 boolean handle=false;
685 if (_state==State.ASYNC_WAIT)
687 _state=State.ASYNC_WOKEN;
693 _channel.execute(_channel);
696 public void onWritePossible()
698 boolean handle=false;
703 if (_state==State.ASYNC_WAIT)
705 _state=State.ASYNC_WOKEN;
711 _channel.execute(_channel);