View Javadoc
1   //
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.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import static javax.servlet.RequestDispatcher.ERROR_EXCEPTION;
22  import static javax.servlet.RequestDispatcher.ERROR_MESSAGE;
23  import static javax.servlet.RequestDispatcher.ERROR_STATUS_CODE;
24  
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.concurrent.TimeUnit;
28  import java.util.concurrent.atomic.AtomicReference;
29  
30  import javax.servlet.AsyncListener;
31  import javax.servlet.RequestDispatcher;
32  import javax.servlet.ServletContext;
33  import javax.servlet.ServletResponse;
34  import javax.servlet.UnavailableException;
35  
36  import org.eclipse.jetty.http.BadMessageException;
37  import org.eclipse.jetty.http.HttpStatus;
38  import org.eclipse.jetty.server.handler.ContextHandler;
39  import org.eclipse.jetty.server.handler.ContextHandler.Context;
40  import org.eclipse.jetty.util.log.Log;
41  import org.eclipse.jetty.util.log.Logger;
42  import org.eclipse.jetty.util.thread.Locker;
43  import org.eclipse.jetty.util.thread.Scheduler;
44  
45  /**
46   * Implementation of AsyncContext interface that holds the state of request-response cycle.
47   */
48  public class HttpChannelState
49  {
50      private static final Logger LOG = Log.getLogger(HttpChannelState.class);
51  
52      private final static long DEFAULT_TIMEOUT=Long.getLong("org.eclipse.jetty.server.HttpChannelState.DEFAULT_TIMEOUT",30000L);
53  
54      /**
55       * The state of the HttpChannel,used to control the overall lifecycle.
56       */
57      public enum State
58      {
59          IDLE,             // Idle request
60          DISPATCHED,       // Request dispatched to filter/servlet
61          THROWN,           // Exception thrown while DISPATCHED
62          ASYNC_WAIT,       // Suspended and waiting
63          ASYNC_WOKEN,      // Dispatch to handle from ASYNC_WAIT
64          ASYNC_IO,         // Dispatched for async IO
65          ASYNC_ERROR,      // Async error from ASYNC_WAIT
66          COMPLETING,       // Response is completable
67          COMPLETED,        // Response is completed
68          UPGRADED          // Request upgraded the connection
69      }
70  
71      /**
72       * The actions to take as the channel moves from state to state.
73       */
74      public enum Action
75      {
76          DISPATCH,         // handle a normal request dispatch
77          ASYNC_DISPATCH,   // handle an async request dispatch
78          ERROR_DISPATCH,   // handle a normal error
79          ASYNC_ERROR,      // handle an async error
80          WRITE_CALLBACK,   // handle an IO write callback
81          READ_CALLBACK,    // handle an IO read callback
82          COMPLETE,         // Complete the response
83          TERMINATED,       // No further actions
84          WAIT,             // Wait for further events
85      }
86  
87      /**
88       * The state of the servlet async API.
89       */
90      public enum Async
91      {
92          NOT_ASYNC,
93          STARTED,          // AsyncContext.startAsync() has been called
94          DISPATCH,         // AsyncContext.dispatch() has been called
95          COMPLETE,         // AsyncContext.complete() has been called
96          EXPIRING,         // AsyncContext timeout just happened
97          EXPIRED,          // AsyncContext timeout has been processed
98          ERRORING,         // An error just happened
99          ERRORED           // The error has been processed
100     }
101 
102     private final boolean DEBUG=LOG.isDebugEnabled();
103     private final Locker _locker=new Locker();
104     private final HttpChannel _channel;
105 
106     private List<AsyncListener> _asyncListeners;
107     private State _state;
108     private Async _async;
109     private boolean _initial;
110     private boolean _asyncReadPossible;
111     private boolean _asyncReadUnready;
112     private boolean _asyncWrite; // TODO refactor same as read
113     private long _timeoutMs=DEFAULT_TIMEOUT;
114     private AsyncContextEvent _event;
115 
116     protected HttpChannelState(HttpChannel channel)
117     {
118         _channel=channel;
119         _state=State.IDLE;
120         _async=Async.NOT_ASYNC;
121         _initial=true;
122     }
123 
124     public State getState()
125     {
126         try(Locker.Lock lock= _locker.lock())
127         {
128             return _state;
129         }
130     }
131 
132     public void addListener(AsyncListener listener)
133     {
134         try(Locker.Lock lock= _locker.lock())
135         {
136             if (_asyncListeners==null)
137                 _asyncListeners=new ArrayList<>();
138             _asyncListeners.add(listener);
139         }
140     }
141 
142     public void setTimeout(long ms)
143     {
144         try(Locker.Lock lock= _locker.lock())
145         {
146             _timeoutMs=ms;
147         }
148     }
149 
150     public long getTimeout()
151     {
152         try(Locker.Lock lock= _locker.lock())
153         {
154             return _timeoutMs;
155         }
156     }
157 
158     public AsyncContextEvent getAsyncContextEvent()
159     {
160         try(Locker.Lock lock= _locker.lock())
161         {
162             return _event;
163         }
164     }
165 
166     @Override
167     public String toString()
168     {
169         try(Locker.Lock lock= _locker.lock())
170         {
171             return toStringLocked();
172         }
173     }
174 
175     public String toStringLocked()
176     {
177         return String.format("%s@%x{s=%s a=%s i=%b r=%s w=%b}",getClass().getSimpleName(),hashCode(),_state,_async,_initial,
178                 _asyncReadPossible?(_asyncReadUnready?"PU":"P!U"):(_asyncReadUnready?"!PU":"!P!U"),
179                 _asyncWrite);
180     }
181     
182 
183     private String getStatusStringLocked()
184     {
185         return String.format("s=%s i=%b a=%s",_state,_initial,_async);
186     }
187 
188     public String getStatusString()
189     {
190         try(Locker.Lock lock= _locker.lock())
191         {
192             return getStatusStringLocked();
193         }
194     }
195 
196     /**
197      * @return Next handling of the request should proceed
198      */
199     protected Action handling()
200     {
201         try(Locker.Lock lock= _locker.lock())
202         {
203             if(DEBUG)
204                 LOG.debug("handling {}",toStringLocked());
205             
206             switch(_state)
207             {
208                 case IDLE:
209                     _initial=true;
210                     _state=State.DISPATCHED;
211                     return Action.DISPATCH;
212 
213                 case COMPLETING:
214                 case COMPLETED:
215                     return Action.TERMINATED;
216 
217                 case ASYNC_WOKEN:
218                     if (_asyncReadPossible)
219                     {
220                         _state=State.ASYNC_IO;
221                         _asyncReadUnready=false;
222                         return Action.READ_CALLBACK;
223                     }
224 
225                     if (_asyncWrite)
226                     {
227                         _state=State.ASYNC_IO;
228                         _asyncWrite=false;
229                         return Action.WRITE_CALLBACK;
230                     }
231 
232                     switch(_async)
233                     {
234                         case COMPLETE:
235                             _state=State.COMPLETING;
236                             return Action.COMPLETE;
237                         case DISPATCH:
238                             _state=State.DISPATCHED;
239                             _async=Async.NOT_ASYNC;
240                             return Action.ASYNC_DISPATCH;
241                         case EXPIRED:
242                         case ERRORED:
243                             _state=State.DISPATCHED;
244                             _async=Async.NOT_ASYNC;
245                             return Action.ERROR_DISPATCH;
246                         case STARTED:
247                             case EXPIRING:
248                         case ERRORING:
249                             return Action.WAIT;
250                         case NOT_ASYNC:
251                             break;
252                         default:
253                             throw new IllegalStateException(getStatusStringLocked());
254                     }
255 
256                     return Action.WAIT;
257 
258                 case ASYNC_ERROR:
259                     return Action.ASYNC_ERROR;
260 
261                 case ASYNC_IO:
262                 case ASYNC_WAIT:
263                 case DISPATCHED:
264                 case UPGRADED:
265                 default:
266                     throw new IllegalStateException(getStatusStringLocked());
267 
268             }
269         }
270     }
271 
272     public void startAsync(AsyncContextEvent event)
273     {
274         final List<AsyncListener> lastAsyncListeners;
275 
276         try(Locker.Lock lock= _locker.lock())
277         {
278             if(DEBUG)
279                 LOG.debug("startAsync {}",toStringLocked());
280             if (_state!=State.DISPATCHED || _async!=Async.NOT_ASYNC)
281                 throw new IllegalStateException(this.getStatusStringLocked());
282 
283             _async=Async.STARTED;
284             _event=event;
285             lastAsyncListeners=_asyncListeners;
286             _asyncListeners=null;            
287         }
288 
289         if (lastAsyncListeners!=null)
290         {
291             Runnable callback=new Runnable()
292             {
293                 @Override
294                 public void run()
295                 {
296                     for (AsyncListener listener : lastAsyncListeners)
297                     {
298                         try
299                         {
300                             listener.onStartAsync(event);
301                         }
302                         catch(Throwable e)
303                         {
304                             // TODO Async Dispatch Error
305                             LOG.warn(e);
306                         }
307                     }
308                 }
309                 @Override
310                 public String toString()
311                 {
312                     return "startAsync";
313                 }
314             };
315                   
316             runInContext(event,callback);
317         }
318     }
319 
320 
321     public void asyncError(Throwable failure)
322     {
323         AsyncContextEvent event = null;
324         try (Locker.Lock lock= _locker.lock())
325         {
326             switch (_state)
327             {
328                 case IDLE:
329                 case DISPATCHED:
330                 case COMPLETING:
331                 case COMPLETED:
332                 case UPGRADED:
333                 case ASYNC_IO:
334                 case ASYNC_WOKEN:
335                 case ASYNC_ERROR:
336                 {
337                     break;
338                 }
339                 case ASYNC_WAIT:
340                 {
341                     _event.addThrowable(failure);
342                     _state=State.ASYNC_ERROR;
343                     event=_event;
344                     break;
345                 }
346                 default:
347                 {
348                     throw new IllegalStateException(getStatusStringLocked());
349                 }
350             }
351         }
352 
353         if (event != null)
354         {
355             cancelTimeout(event);
356             runInContext(event, _channel);
357         }
358     }
359 
360     /**
361      * Signal that the HttpConnection has finished handling the request.
362      * For blocking connectors,this call may block if the request has
363      * been suspended (startAsync called).
364      * @return next actions
365      * be handled again (eg because of a resume that happened before unhandle was called)
366      */
367     protected Action unhandle()
368     {
369         Action action;
370         boolean read_interested=false;
371 
372         try(Locker.Lock lock= _locker.lock())
373         {
374             if(DEBUG)
375                 LOG.debug("unhandle {}",toStringLocked());
376             
377             switch(_state)
378             {
379                 case COMPLETING:
380                 case COMPLETED:
381                     return Action.TERMINATED;
382 
383                 case THROWN:
384                     _state=State.DISPATCHED;
385                     return Action.ERROR_DISPATCH;
386                     
387                 case DISPATCHED:
388                 case ASYNC_IO:
389                 case ASYNC_ERROR:
390                     break;
391 
392                 default:
393                     throw new IllegalStateException(this.getStatusStringLocked());
394             }
395 
396             _initial=false;
397             switch(_async)
398             {
399                 case COMPLETE:
400                     _state=State.COMPLETING;
401                     _async=Async.NOT_ASYNC;
402                     action=Action.COMPLETE;
403                     break;
404 
405                 case DISPATCH:
406                     _state=State.DISPATCHED;
407                     _async=Async.NOT_ASYNC;
408                     action=Action.ASYNC_DISPATCH;
409                     break;
410 
411                 case STARTED:
412                     if (_asyncReadUnready && _asyncReadPossible)
413                     {
414                         _state=State.ASYNC_IO;
415                         _asyncReadUnready=false;
416                         action = Action.READ_CALLBACK;
417                     }
418                     else if (_asyncWrite) // TODO refactor same as read
419                     {
420                         _asyncWrite=false;
421                         _state=State.ASYNC_IO;
422                         action=Action.WRITE_CALLBACK;
423                     }
424                     else
425                     {
426                         _state=State.ASYNC_WAIT;
427                         action=Action.WAIT; 
428                         if (_asyncReadUnready)
429                             read_interested=true;
430                         Scheduler scheduler=_channel.getScheduler();
431                         if (scheduler!=null && _timeoutMs>0)
432                             _event.setTimeoutTask(scheduler.schedule(_event,_timeoutMs,TimeUnit.MILLISECONDS));
433                     }
434                     break;
435 
436                 case EXPIRING:
437                     // onTimeout callbacks still being called, so just WAIT
438                     _state=State.ASYNC_WAIT;
439                     action=Action.WAIT;
440                     break;
441 
442                 case EXPIRED:
443                     // onTimeout handling is complete, but did not dispatch as
444                     // we were handling.  So do the error dispatch here
445                     _state=State.DISPATCHED;
446                     _async=Async.NOT_ASYNC;
447                     action=Action.ERROR_DISPATCH;
448                     break;
449 
450                 case ERRORED:
451                     _state=State.DISPATCHED;
452                     _async=Async.NOT_ASYNC;
453                     action=Action.ERROR_DISPATCH;
454                     break;
455 
456                 case NOT_ASYNC:
457                     _state=State.COMPLETING;
458                     action=Action.COMPLETE;
459                     break;
460 
461                 default:
462                     _state=State.COMPLETING;
463                     action=Action.COMPLETE;
464                     break;
465             }
466         }
467 
468         if (read_interested)
469             _channel.asyncReadFillInterested();
470 
471         return action;
472     }
473 
474     public void dispatch(ServletContext context, String path)
475     {
476         boolean dispatch=false;
477         AsyncContextEvent event;
478         try(Locker.Lock lock= _locker.lock())
479         {
480             if(DEBUG)
481                 LOG.debug("dispatch {} -> {}",toStringLocked(),path);
482             
483             boolean started=false;
484             event=_event;
485             switch(_async)
486             {
487                 case STARTED:
488                     started=true;
489                     break;
490                 case EXPIRING:
491                 case ERRORING:
492                 case ERRORED:
493                     break;
494                 default:
495                     throw new IllegalStateException(this.getStatusStringLocked());
496             }
497             _async=Async.DISPATCH;
498 
499             if (context!=null)
500                 _event.setDispatchContext(context);
501             if (path!=null)
502                 _event.setDispatchPath(path);
503 
504             if (started)
505             {
506                 switch(_state)
507                 {
508                     case DISPATCHED:
509                     case ASYNC_IO:
510                     case ASYNC_WOKEN:
511                         break;
512                     case ASYNC_WAIT:
513                         _state=State.ASYNC_WOKEN;
514                         dispatch=true;
515                         break;
516                     default:
517                         LOG.warn("async dispatched when complete {}",this);
518                         break;
519                 }
520             }
521         }
522 
523         cancelTimeout(event);
524         if (dispatch)
525             scheduleDispatch();
526     }
527 
528     protected void onTimeout()
529     {
530         final List<AsyncListener> listeners;
531         AsyncContextEvent event;
532         try(Locker.Lock lock= _locker.lock())
533         {
534             if(DEBUG)
535                 LOG.debug("onTimeout {}",toStringLocked());
536             
537             if (_async!=Async.STARTED)
538                 return;
539             _async=Async.EXPIRING;
540             event=_event;
541             listeners=_asyncListeners;
542 
543         }
544 
545         final AtomicReference<Throwable> error=new AtomicReference<>();
546         if (listeners!=null)
547         {
548             Runnable task=new Runnable()
549             {
550                 @Override
551                 public void run()
552                 {
553                     for (AsyncListener listener : listeners)
554                     {
555                         try
556                         {
557                             listener.onTimeout(event);
558                         }
559                         catch(Throwable x)
560                         {
561                             LOG.warn(x+" while invoking onTimeout listener " + listener);
562                             LOG.debug(x);
563                             if (error.get()==null)
564                                 error.set(x);
565                             else
566                                 error.get().addSuppressed(x);
567                         }
568                     }
569                 }
570                 @Override
571                 public String toString()
572                 {
573                     return "onTimeout";
574                 }
575             };
576             
577             runInContext(event,task);
578         }
579 
580         Throwable th=error.get();
581         boolean dispatch=false;
582         try(Locker.Lock lock= _locker.lock())
583         {
584             switch(_async)
585             {
586                 case EXPIRING:
587                     _async=th==null ? Async.EXPIRED : Async.ERRORING;
588                     break;
589 
590                 case COMPLETE:
591                 case DISPATCH:
592                     if (th!=null)
593                     {
594                         LOG.ignore(th);
595                         th=null;
596                     }
597                     break;
598 
599                 default:
600                     throw new IllegalStateException();
601             }
602 
603             if (_state==State.ASYNC_WAIT)
604             {
605                 _state=State.ASYNC_WOKEN;
606                 dispatch=true;
607             }
608         }
609 
610         if (th!=null)
611         {
612             if (LOG.isDebugEnabled())
613                 LOG.debug("Error after async timeout {}",this,th);
614             onError(th);
615         }
616         
617         if (dispatch)
618         {
619             if (LOG.isDebugEnabled())
620                 LOG.debug("Dispatch after async timeout {}",this);
621             scheduleDispatch();
622         }
623     }
624 
625     public void complete()
626     {
627 
628         // just like resume, except don't set _dispatched=true;
629         boolean handle=false;
630         AsyncContextEvent event;
631         try(Locker.Lock lock= _locker.lock())
632         {
633             if(DEBUG)
634                 LOG.debug("complete {}",toStringLocked());
635             
636             boolean started=false;
637             event=_event;
638             
639             switch(_async)
640             {
641                 case STARTED:
642                     started=true;
643                     break;
644                 case EXPIRING:
645                 case ERRORING:
646                 case ERRORED:
647                     break;
648                 case COMPLETE:
649                     return;
650                 default:
651                     throw new IllegalStateException(this.getStatusStringLocked());
652             }
653             _async=Async.COMPLETE;
654             
655             if (started && _state==State.ASYNC_WAIT)
656             {
657                 handle=true;
658                 _state=State.ASYNC_WOKEN;
659             }
660         }
661 
662         cancelTimeout(event);
663         if (handle)
664             runInContext(event,_channel);
665     }
666 
667     public void errorComplete()
668     {
669         try(Locker.Lock lock= _locker.lock())
670         {
671             if(DEBUG)
672                 LOG.debug("error complete {}",toStringLocked());
673             
674             _async=Async.COMPLETE;
675             _event.setDispatchContext(null);
676             _event.setDispatchPath(null);
677         }
678 
679         cancelTimeout();
680     }
681     
682     protected void onError(Throwable failure)
683     {
684         final List<AsyncListener> listeners;
685         final AsyncContextEvent event;
686         final Request baseRequest = _channel.getRequest();
687         
688         int code=HttpStatus.INTERNAL_SERVER_ERROR_500;
689         String reason=null;
690         if (failure instanceof BadMessageException)
691         {
692             BadMessageException bme = (BadMessageException)failure;
693             code = bme.getCode();
694             reason = bme.getReason();
695         }
696         else if (failure instanceof UnavailableException)
697         {
698             if (((UnavailableException)failure).isPermanent())
699                 code = HttpStatus.NOT_FOUND_404;
700             else
701                 code = HttpStatus.SERVICE_UNAVAILABLE_503;
702         }
703         
704         try(Locker.Lock lock= _locker.lock())
705         {
706             if(DEBUG)
707                 LOG.debug("onError {} {}",toStringLocked(),failure);
708             
709             // Set error on request.
710             if(_event!=null)
711             {
712                 _event.addThrowable(failure);
713                 _event.getSuppliedRequest().setAttribute(ERROR_STATUS_CODE,code);
714                 _event.getSuppliedRequest().setAttribute(ERROR_EXCEPTION,failure);
715                 _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,failure==null?null:failure.getClass());
716                     
717                 _event.getSuppliedRequest().setAttribute(ERROR_MESSAGE,reason!=null?reason:null);
718             }
719             else
720             {
721                 Throwable error = (Throwable)baseRequest.getAttribute(ERROR_EXCEPTION);
722                 if (error!=null)
723                     throw new IllegalStateException("Error already set",error);
724                 baseRequest.setAttribute(ERROR_STATUS_CODE,code);
725                 baseRequest.setAttribute(ERROR_EXCEPTION,failure);
726                 baseRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,failure==null?null:failure.getClass());
727                 baseRequest.setAttribute(ERROR_MESSAGE,reason!=null?reason:null);
728             }
729             
730             // Are we blocking?
731             if (_async==Async.NOT_ASYNC)
732             {
733                 // Only called from within HttpChannel Handling, so much be dispatched, let's stay dispatched!
734                 if (_state==State.DISPATCHED)
735                 {
736                     _state=State.THROWN;
737                     return;
738                 }
739                 throw new IllegalStateException(this.getStatusStringLocked());
740             }
741             
742             // We are Async
743             _async=Async.ERRORING;
744             listeners=_asyncListeners;
745             event=_event;
746         }
747 
748         if(listeners!=null)
749         {
750             Runnable task=new Runnable()
751             {
752                 @Override
753                 public void run()
754                 {
755                     for (AsyncListener listener : listeners)
756                     {
757                         try
758                         {
759                             listener.onError(event);
760                         }
761                         catch (Throwable x)
762                         {
763                             LOG.warn(x+" while invoking onError listener " + listener);
764                             LOG.debug(x);
765                         }
766                     }
767                 }
768 
769                 @Override
770                 public String toString()
771                 {
772                     return "onError";
773                 }
774             };
775             runInContext(event,task);
776         }
777 
778         boolean dispatch=false;
779         try(Locker.Lock lock= _locker.lock())
780         {
781             switch(_async)
782             {
783                 case ERRORING:
784                 {
785                     // Still in this state ? The listeners did not invoke API methods
786                     // and the container must provide a default error dispatch.
787                     _async=Async.ERRORED;
788                     break;
789                 }
790                 case DISPATCH:
791                 case COMPLETE:
792                 {
793                     // The listeners called dispatch() or complete().
794                     break;
795                 }
796                 default:
797                 {
798                     throw new IllegalStateException(toString());
799                 }
800             }
801 
802             if(_state==State.ASYNC_WAIT)
803             {
804                 _state=State.ASYNC_WOKEN;
805                 dispatch=true;
806             }
807         }
808 
809         if(dispatch)
810         {
811             if(LOG.isDebugEnabled())
812                 LOG.debug("Dispatch after error {}",this);
813             scheduleDispatch();
814         }
815     }
816 
817     protected void onComplete()
818     {
819         final List<AsyncListener> aListeners;
820         final AsyncContextEvent event;
821 
822         try(Locker.Lock lock= _locker.lock())
823         {
824             if(DEBUG)
825                 LOG.debug("onComplete {}",toStringLocked());
826             
827             switch(_state)
828             {
829                 case COMPLETING:
830                     aListeners=_asyncListeners;
831                     event=_event;
832                     _state=State.COMPLETED;
833                     _async=Async.NOT_ASYNC;
834                     break;
835 
836                 default:
837                     throw new IllegalStateException(this.getStatusStringLocked());
838             }
839         }
840 
841         if (event!=null)
842         {
843             if (aListeners!=null)
844             {
845                 Runnable callback = new Runnable()
846                 {
847                     @Override
848                     public void run()
849                     {
850                         for (AsyncListener listener : aListeners)
851                         {
852                             try
853                             {
854                                 listener.onComplete(event);
855                             }
856                             catch(Throwable e)
857                             {
858                                 LOG.warn(e+" while invoking onComplete listener " + listener);
859                                 LOG.debug(e);
860                             }
861                         }
862                     }    
863                     @Override
864                     public String toString()
865                     {
866                         return "onComplete";
867                     }
868                 };
869                 
870                 runInContext(event,callback);                
871             }
872             event.completed();
873         }
874     }
875 
876     protected void recycle()
877     {
878         cancelTimeout();
879         try(Locker.Lock lock= _locker.lock())
880         {
881             if(DEBUG)
882                 LOG.debug("recycle {}",toStringLocked());
883             
884             switch(_state)
885             {
886                 case DISPATCHED:
887                 case ASYNC_IO:
888                     throw new IllegalStateException(getStatusStringLocked());
889                 case UPGRADED:
890                     return;
891                 default:
892                     break;
893             }
894             _asyncListeners=null;
895             _state=State.IDLE;
896             _async=Async.NOT_ASYNC;
897             _initial=true;
898             _asyncReadPossible=_asyncReadUnready=false;
899             _asyncWrite=false;
900             _timeoutMs=DEFAULT_TIMEOUT;
901             _event=null;
902         }
903     }
904 
905     public void upgrade()
906     {
907         cancelTimeout();
908         try(Locker.Lock lock= _locker.lock())
909         {
910             if(DEBUG)
911                 LOG.debug("upgrade {}",toStringLocked());
912             
913             switch(_state)
914             {
915                 case IDLE:
916                 case COMPLETED:
917                     break;
918                 default:
919                     throw new IllegalStateException(getStatusStringLocked());
920             }
921             _asyncListeners=null;
922             _state=State.UPGRADED;
923             _async=Async.NOT_ASYNC;
924             _initial=true;
925             _asyncReadPossible=_asyncReadUnready=false;
926             _asyncWrite=false;
927             _timeoutMs=DEFAULT_TIMEOUT;
928             _event=null;
929         }
930     }
931 
932     protected void scheduleDispatch()
933     {
934         _channel.execute(_channel);
935     }
936 
937     protected void cancelTimeout()
938     {
939         final AsyncContextEvent event;
940         try(Locker.Lock lock= _locker.lock())
941         {
942             event=_event;
943         }
944         cancelTimeout(event);
945     }
946 
947     protected void cancelTimeout(AsyncContextEvent event)
948     {
949         if (event!=null)
950             event.cancelTimeoutTask();
951     }
952     
953     public boolean isIdle()
954     {
955         try(Locker.Lock lock= _locker.lock())
956         {
957             return _state==State.IDLE;
958         }
959     }
960 
961     public boolean isExpired()
962     {
963         try(Locker.Lock lock= _locker.lock())
964         {
965             return _async==Async.EXPIRED;
966         }
967     }
968 
969     public boolean isInitial()
970     {
971         try(Locker.Lock lock= _locker.lock())
972         {
973             return _initial;
974         }
975     }
976 
977     public boolean isSuspended()
978     {
979         try(Locker.Lock lock= _locker.lock())
980         {
981             return _state==State.ASYNC_WAIT || _state==State.DISPATCHED && _async==Async.STARTED;
982         }
983     }
984 
985     boolean isCompleting()
986     {
987         try(Locker.Lock lock= _locker.lock())
988         {
989             return _state==State.COMPLETING;
990         }
991     }
992 
993     boolean isCompleted()
994     {
995         try(Locker.Lock lock= _locker.lock())
996         {
997             return _state == State.COMPLETED;
998         }
999     }
1000 
1001     public boolean isAsyncStarted()
1002     {
1003         try(Locker.Lock lock= _locker.lock())
1004         {
1005             if (_state==State.DISPATCHED)
1006                 return _async!=Async.NOT_ASYNC;
1007             return _async==Async.STARTED || _async==Async.EXPIRING;
1008         }
1009     }
1010 
1011     public boolean isAsyncComplete()
1012     {
1013         try(Locker.Lock lock= _locker.lock())
1014         {
1015             return _async==Async.COMPLETE;
1016         }
1017     }
1018 
1019     public boolean isAsync()
1020     {
1021         try(Locker.Lock lock= _locker.lock())
1022         {
1023             return !_initial || _async!=Async.NOT_ASYNC;
1024         }
1025     }
1026 
1027     public Request getBaseRequest()
1028     {
1029         return _channel.getRequest();
1030     }
1031 
1032     public HttpChannel getHttpChannel()
1033     {
1034         return _channel;
1035     }
1036 
1037     public ContextHandler getContextHandler()
1038     {
1039         final AsyncContextEvent event;
1040         try(Locker.Lock lock= _locker.lock())
1041         {
1042             event=_event;
1043         }
1044         return getContextHandler(event);
1045     }
1046 
1047     ContextHandler getContextHandler(AsyncContextEvent event)
1048     {
1049         if (event!=null)
1050         {
1051             Context context=((Context)event.getServletContext());
1052             if (context!=null)
1053                 return context.getContextHandler();
1054         }
1055         return null;
1056     }
1057 
1058     public ServletResponse getServletResponse()
1059     {
1060         final AsyncContextEvent event;
1061         try(Locker.Lock lock= _locker.lock())
1062         {
1063             event=_event;
1064         }
1065         return getServletResponse(event);
1066     }
1067     
1068     public ServletResponse getServletResponse(AsyncContextEvent event)
1069     {
1070         if (event!=null && event.getSuppliedResponse()!=null)
1071             return event.getSuppliedResponse();
1072         return _channel.getResponse();
1073     }
1074     
1075     void runInContext(AsyncContextEvent event,Runnable runnable)
1076     {
1077         ContextHandler contextHandler = getContextHandler(event);
1078         if (contextHandler==null)
1079             runnable.run();
1080         else
1081             contextHandler.handle(_channel.getRequest(),runnable);
1082     }
1083     
1084     public Object getAttribute(String name)
1085     {
1086         return _channel.getRequest().getAttribute(name);
1087     }
1088 
1089     public void removeAttribute(String name)
1090     {
1091         _channel.getRequest().removeAttribute(name);
1092     }
1093 
1094     public void setAttribute(String name, Object attribute)
1095     {
1096         _channel.getRequest().setAttribute(name,attribute);
1097     }
1098 
1099 
1100     /* ------------------------------------------------------------ */
1101     /** Called to signal async read isReady() has returned false.
1102      * This indicates that there is no content available to be consumed
1103      * and that once the channel enteres the ASYNC_WAIT state it will
1104      * register for read interest by calling {@link HttpChannel#asyncReadFillInterested()}
1105      * either from this method or from a subsequent call to {@link #unhandle()}.
1106      */
1107     public void onReadUnready()
1108     {
1109         boolean interested=false;
1110         try(Locker.Lock lock= _locker.lock())
1111         {
1112             if(DEBUG)
1113                 LOG.debug("onReadUnready {}",toStringLocked());
1114             
1115             // We were already unready, this is not a state change, so do nothing
1116             if (!_asyncReadUnready)
1117             {
1118                 _asyncReadUnready=true;
1119                 _asyncReadPossible=false; // Assumes this has been checked in isReady() with lock held
1120                 if (_state==State.ASYNC_WAIT)
1121                     interested=true;
1122             }
1123         }
1124 
1125         if (interested)
1126             _channel.asyncReadFillInterested();
1127     }
1128 
1129     /* ------------------------------------------------------------ */
1130     /** Called to signal that content is now available to read.
1131      * If the channel is in ASYNC_WAIT state and unready (ie isReady() has
1132      * returned false), then the state is changed to ASYNC_WOKEN and true
1133      * is returned.
1134      * @return True IFF the channel was unready and in ASYNC_WAIT state
1135      */
1136     public boolean onReadPossible()
1137     {
1138         boolean woken=false;
1139         try(Locker.Lock lock= _locker.lock())
1140         {
1141             if(DEBUG)
1142                 LOG.debug("onReadPossible {}",toStringLocked());
1143             
1144             _asyncReadPossible=true;
1145             if (_state==State.ASYNC_WAIT && _asyncReadUnready)
1146             {
1147                 woken=true;
1148                 _state=State.ASYNC_WOKEN;
1149             }
1150         }
1151         return woken;
1152     }
1153 
1154     /* ------------------------------------------------------------ */
1155     /** Called to signal that the channel is ready for a callback.
1156      * This is similar to calling {@link #onReadUnready()} followed by
1157      * {@link #onReadPossible()}, except that as content is already
1158      * available, read interest is never set.
1159      * @return true if woken
1160      */
1161     public boolean onReadReady()
1162     {
1163         boolean woken=false;
1164         try(Locker.Lock lock= _locker.lock())
1165         {
1166             if(DEBUG)
1167                 LOG.debug("onReadReady {}",toStringLocked());
1168             
1169             _asyncReadUnready=true;
1170             _asyncReadPossible=true;
1171             if (_state==State.ASYNC_WAIT)
1172             {
1173                 woken=true;
1174                 _state=State.ASYNC_WOKEN;
1175             }
1176         }
1177         return woken;
1178     }
1179     
1180     /* ------------------------------------------------------------ */
1181     /** Called to signal that a read has read -1.
1182      * Will wake if the read was called while in ASYNC_WAIT state
1183      * @return true if woken
1184      */
1185     public boolean onReadEof()
1186     {
1187         boolean woken=false;
1188         try(Locker.Lock lock= _locker.lock())
1189         {
1190             if(DEBUG)
1191                 LOG.debug("onReadEof {}",toStringLocked());
1192             
1193             if (_state==State.ASYNC_WAIT)
1194             {
1195                 _state=State.ASYNC_WOKEN;
1196                 _asyncReadUnready=true;
1197                 _asyncReadPossible=true;
1198                 woken=true;
1199             }
1200         }
1201         return woken;
1202     }
1203 
1204 
1205     public boolean isReadPossible()
1206     {
1207         try(Locker.Lock lock= _locker.lock())
1208         {
1209             return _asyncReadPossible;
1210         }
1211     }
1212 
1213     public boolean onWritePossible()
1214     {
1215         boolean handle=false;
1216 
1217         try(Locker.Lock lock= _locker.lock())
1218         {
1219             if(DEBUG)
1220                 LOG.debug("onWritePossible {}",toStringLocked());
1221             
1222             _asyncWrite=true;
1223             if (_state==State.ASYNC_WAIT)
1224             {
1225                 _state=State.ASYNC_WOKEN;
1226                 handle=true;
1227             }
1228         }
1229 
1230         return handle;
1231     }
1232 
1233 }