2 // ========================================================================
3 // Copyright (c) 1995-2016 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=Long.getLong("org.eclipse.jetty.server.HttpChannelState.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
56 UPGRADED // Request upgraded the connection
60 * The actions to take as the channel moves from state to state.
64 REQUEST_DISPATCH, // handle a normal request dispatch
65 ASYNC_DISPATCH, // handle an async request dispatch
66 ASYNC_EXPIRED, // handle an async timeout
67 WRITE_CALLBACK, // handle an IO write callback
68 READ_CALLBACK, // handle an IO read callback
69 WAIT, // Wait for further events
70 COMPLETE // Complete the channel
74 * The state of the servlet async API. This can lead or follow the
75 * channel dispatch state and also includes reasons such as expired,
76 * dispatched or completed.
87 private final boolean DEBUG=LOG.isDebugEnabled();
88 private final HttpChannel<?> _channel;
90 private List<AsyncListener> _asyncListeners;
93 private boolean _initial;
94 private boolean _asyncRead;
95 private boolean _asyncWrite;
96 private long _timeoutMs=DEFAULT_TIMEOUT;
97 private AsyncContextEvent _event;
99 protected HttpChannelState(HttpChannel<?> channel)
107 public State getState()
115 public void addListener(AsyncListener listener)
119 if (_asyncListeners==null)
120 _asyncListeners=new ArrayList<>();
121 _asyncListeners.add(listener);
125 public void setTimeout(long ms)
133 public long getTimeout()
141 public AsyncContextEvent getAsyncContextEvent()
150 public String toString()
154 return String.format("%s@%x{s=%s i=%b a=%s}",getClass().getSimpleName(),hashCode(),_state,_initial,_async);
158 public String getStatusString()
162 return String.format("s=%s i=%b a=%s",_state,_initial,_async);
167 * @return Next handling of the request should proceed
169 protected Action handling()
174 LOG.debug("{} handling {}",this,_state);
179 _state=State.DISPATCHED;
180 return Action.REQUEST_DISPATCH;
183 return Action.COMPLETE;
191 _state=State.ASYNC_IO;
193 return Action.READ_CALLBACK;
197 _state=State.ASYNC_IO;
199 return Action.WRITE_CALLBACK;
208 _state=State.COMPLETING;
209 return Action.COMPLETE;
211 _state=State.DISPATCHED;
213 return Action.ASYNC_DISPATCH;
217 _state=State.DISPATCHED;
219 return Action.ASYNC_EXPIRED;
223 LOG.debug("TODO Fix this double dispatch",new IllegalStateException(this
224 .getStatusString()));
232 throw new IllegalStateException(this.getStatusString());
237 public void startAsync(AsyncContextEvent event)
239 final List<AsyncListener> lastAsyncListeners;
243 if (_state!=State.DISPATCHED || _async!=null)
244 throw new IllegalStateException(this.getStatusString());
246 _async=Async.STARTED;
248 lastAsyncListeners=_asyncListeners;
249 _asyncListeners=null;
252 if (lastAsyncListeners!=null)
254 for (AsyncListener listener : lastAsyncListeners)
258 listener.onStartAsync(event);
268 protected void error(Throwable th)
273 _event.setThrowable(th);
278 * Signal that the HttpConnection has finished handling the request.
279 * For blocking connectors, this call may block if the request has
280 * been suspended (startAsync called).
281 * @return next actions
282 * be handled again (eg because of a resume that happened before unhandle was called)
284 protected Action unhandle()
289 LOG.debug("{} unhandle {}",this,_state);
297 throw new IllegalStateException(this.getStatusString());
302 _state=State.ASYNC_IO;
304 return Action.READ_CALLBACK;
310 _state=State.ASYNC_IO;
311 return Action.WRITE_CALLBACK;
320 _state=State.COMPLETING;
322 return Action.COMPLETE;
324 _state=State.DISPATCHED;
326 return Action.ASYNC_DISPATCH;
328 _state=State.DISPATCHED;
330 return Action.ASYNC_EXPIRED;
334 _state=State.ASYNC_WAIT;
339 _state=State.COMPLETING;
340 return Action.COMPLETE;
344 public void dispatch(ServletContext context, String path)
349 if (_async!=Async.STARTED && _async!=Async.EXPIRING)
350 throw new IllegalStateException("AsyncContext#dispath "+this.getStatusString());
351 _async=Async.DISPATCH;
354 _event.setDispatchContext(context);
356 _event.setDispatchPath(path);
365 _state=State.ASYNC_WOKEN;
372 LOG.warn("async dispatched when complete {}",this);
383 protected void expired()
385 final List<AsyncListener> aListeners;
386 AsyncContextEvent event;
389 if (_async!=Async.STARTED)
391 _async=Async.EXPIRING;
393 aListeners=_asyncListeners;
396 if (aListeners!=null)
398 for (AsyncListener listener : aListeners)
402 listener.onTimeout(event);
407 event.setThrowable(e);
408 _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
414 boolean dispatch=false;
417 if (_async==Async.EXPIRING)
419 _async=Async.EXPIRED;
420 if (_state==State.ASYNC_WAIT)
422 _state=State.ASYNC_WOKEN;
432 public void complete()
434 // just like resume, except don't set _dispatched=true;
435 boolean handle=false;
438 if (_async!=Async.STARTED && _async!=Async.EXPIRING)
439 throw new IllegalStateException(this.getStatusString());
440 _async=Async.COMPLETE;
441 if (_state==State.ASYNC_WAIT)
444 _state=State.ASYNC_WOKEN;
451 ContextHandler handler=getContextHandler();
453 handler.handle(_channel);
459 public void errorComplete()
463 _async=Async.COMPLETE;
464 _event.setDispatchContext(null);
465 _event.setDispatchPath(null);
471 protected void completed()
473 final List<AsyncListener> aListeners;
474 final AsyncContextEvent event;
480 _state=State.COMPLETED;
481 aListeners=_asyncListeners;
486 throw new IllegalStateException(this.getStatusString());
492 if (aListeners!=null)
494 if (event.getThrowable()!=null)
496 event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
497 event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage());
500 for (AsyncListener listener : aListeners)
504 if (event.getThrowable()!=null)
505 listener.onError(event);
507 listener.onComplete(event);
520 protected void recycle()
528 throw new IllegalStateException(getStatusString());
534 _asyncListeners=null;
540 _timeoutMs=DEFAULT_TIMEOUT;
546 public void upgrade()
556 throw new IllegalStateException(getStatusString());
558 _asyncListeners=null;
559 _state=State.UPGRADED;
564 _timeoutMs=DEFAULT_TIMEOUT;
571 protected void scheduleDispatch()
573 _channel.execute(_channel);
576 protected void scheduleTimeout()
578 Scheduler scheduler = _channel.getScheduler();
579 if (scheduler!=null && _timeoutMs>0)
580 _event.setTimeoutTask(scheduler.schedule(_event,_timeoutMs,TimeUnit.MILLISECONDS));
583 protected void cancelTimeout()
585 final AsyncContextEvent event;
591 event.cancelTimeoutTask();
594 public boolean isExpired()
598 return _async==Async.EXPIRED;
602 public boolean isInitial()
610 public boolean isSuspended()
614 return _state==State.ASYNC_WAIT || _state==State.DISPATCHED && _async==Async.STARTED;
618 boolean isCompleting()
622 return _state==State.COMPLETING;
626 boolean isCompleted()
630 return _state == State.COMPLETED;
634 public boolean isAsyncStarted()
638 if (_state==State.DISPATCHED)
640 return _async==Async.STARTED || _async==Async.EXPIRING;
644 public boolean isAsync()
648 return !_initial || _async!=null;
652 public Request getBaseRequest()
654 return _channel.getRequest();
657 public HttpChannel<?> getHttpChannel()
662 public ContextHandler getContextHandler()
664 final AsyncContextEvent event;
672 Context context=((Context)event.getServletContext());
674 return context.getContextHandler();
679 public ServletResponse getServletResponse()
681 final AsyncContextEvent event;
686 if (event!=null && event.getSuppliedResponse()!=null)
687 return event.getSuppliedResponse();
688 return _channel.getResponse();
691 public Object getAttribute(String name)
693 return _channel.getRequest().getAttribute(name);
696 public void removeAttribute(String name)
698 _channel.getRequest().removeAttribute(name);
701 public void setAttribute(String name, Object attribute)
703 _channel.getRequest().setAttribute(name,attribute);
706 public void onReadPossible()
708 boolean handle=false;
713 if (_state==State.ASYNC_WAIT)
715 _state=State.ASYNC_WOKEN;
721 _channel.execute(_channel);
724 public void onWritePossible()
726 boolean handle=false;
731 if (_state==State.ASYNC_WAIT)
733 _state=State.ASYNC_WOKEN;
739 _channel.execute(_channel);