1 //
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.
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 javax.servlet.AsyncContext;
22 import javax.servlet.AsyncEvent;
23 import javax.servlet.AsyncListener;
24 import javax.servlet.RequestDispatcher;
25 import javax.servlet.ServletException;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 
30 import javax.servlet.ServletContext;
31 import javax.servlet.ServletRequest;
32 import javax.servlet.ServletResponse;
33 import javax.servlet.http.HttpServletRequest;
34 
35 import org.eclipse.jetty.continuation.Continuation;
36 import org.eclipse.jetty.continuation.ContinuationThrowable;
37 import org.eclipse.jetty.continuation.ContinuationListener;
38 import org.eclipse.jetty.io.AsyncEndPoint;
39 import org.eclipse.jetty.io.EndPoint;
40 import org.eclipse.jetty.server.handler.ContextHandler;
41 import org.eclipse.jetty.server.handler.ContextHandler.Context;
42 import org.eclipse.jetty.util.URIUtil;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
45 import org.eclipse.jetty.util.thread.Timeout;
46 
47 /* ------------------------------------------------------------ */
48 /** Implementation of Continuation and AsyncContext interfaces
49  *
50  */
51 public class AsyncContinuation implements AsyncContext, Continuation
52 {
53     private static final Logger LOG = Log.getLogger(AsyncContinuation.class);
54 
55     private final static long DEFAULT_TIMEOUT=30000L;
56 
57     private final static ContinuationThrowable __exception = new ContinuationThrowable();
58 
59     // STATES:
60     //               handling()    suspend()     unhandle()    resume()       complete()  doComplete()
61     //                             startAsync()                dispatch()
62     // IDLE          DISPATCHED
63     // DISPATCHED                  ASYNCSTARTED  UNCOMPLETED
64     // ASYNCSTARTED                              ASYNCWAIT     REDISPATCHING  COMPLETING
65     // REDISPATCHING                             REDISPATCHED
66     // ASYNCWAIT                                               REDISPATCH     COMPLETING
67     // REDISPATCH    REDISPATCHED
68     // REDISPATCHED                ASYNCSTARTED  UNCOMPLETED
69     // COMPLETING    UNCOMPLETED                 UNCOMPLETED
70     // UNCOMPLETED                                                                        COMPLETED
71     // COMPLETED
72     private static final int __IDLE=0;         // Idle request
73     private static final int __DISPATCHED=1;   // Request dispatched to filter/servlet
74     private static final int __ASYNCSTARTED=2; // Suspend called, but not yet returned to container
75     private static final int __REDISPATCHING=3;// resumed while dispatched
76     private static final int __ASYNCWAIT=4;    // Suspended and parked
77     private static final int __REDISPATCH=5;   // Has been scheduled
78     private static final int __REDISPATCHED=6; // Request redispatched to filter/servlet
79     private static final int __COMPLETING=7;   // complete while dispatched
80     private static final int __UNCOMPLETED=8;  // Request is completable
81     private static final int __COMPLETED=9;    // Request is complete
82 
83     /* ------------------------------------------------------------ */
84     protected AbstractHttpConnection _connection;
85     private List<AsyncListener> _lastAsyncListeners;
86     private List<AsyncListener> _asyncListeners;
87     private List<ContinuationListener> _continuationListeners;
88 
89     /* ------------------------------------------------------------ */
90     private int _state;
91     private boolean _initial;
92     private boolean _resumed;
93     private boolean _expired;
94     private volatile boolean _responseWrapped;
95     private long _timeoutMs=DEFAULT_TIMEOUT;
96     private AsyncEventState _event;
97     private volatile long _expireAt;
98     private volatile boolean _continuation;
99 
100     /* ------------------------------------------------------------ */
AsyncContinuation()101     protected AsyncContinuation()
102     {
103         _state=__IDLE;
104         _initial=true;
105     }
106 
107     /* ------------------------------------------------------------ */
setConnection(final AbstractHttpConnection connection)108     protected void setConnection(final AbstractHttpConnection connection)
109     {
110         synchronized(this)
111         {
112             _connection=connection;
113         }
114     }
115 
116     /* ------------------------------------------------------------ */
addListener(AsyncListener listener)117     public void addListener(AsyncListener listener)
118     {
119         synchronized(this)
120         {
121             if (_asyncListeners==null)
122                 _asyncListeners=new ArrayList<AsyncListener>();
123             _asyncListeners.add(listener);
124         }
125     }
126 
127     /* ------------------------------------------------------------ */
addListener(AsyncListener listener,ServletRequest request, ServletResponse response)128     public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response)
129     {
130         synchronized(this)
131         {
132             // TODO handle the request/response ???
133             if (_asyncListeners==null)
134                 _asyncListeners=new ArrayList<AsyncListener>();
135             _asyncListeners.add(listener);
136         }
137     }
138 
139     /* ------------------------------------------------------------ */
addContinuationListener(ContinuationListener listener)140     public void addContinuationListener(ContinuationListener listener)
141     {
142         synchronized(this)
143         {
144             if (_continuationListeners==null)
145                 _continuationListeners=new ArrayList<ContinuationListener>();
146             _continuationListeners.add(listener);
147         }
148     }
149 
150     /* ------------------------------------------------------------ */
setTimeout(long ms)151     public void setTimeout(long ms)
152     {
153         synchronized(this)
154         {
155             _timeoutMs=ms;
156         }
157     }
158 
159     /* ------------------------------------------------------------ */
getTimeout()160     public long getTimeout()
161     {
162         synchronized(this)
163         {
164             return _timeoutMs;
165         }
166     }
167 
168     /* ------------------------------------------------------------ */
getAsyncEventState()169     public AsyncEventState getAsyncEventState()
170     {
171         synchronized(this)
172         {
173             return _event;
174         }
175     }
176 
177     /* ------------------------------------------------------------ */
178     /**
179      * @see org.eclipse.jetty.continuation.Continuation#keepWrappers()
180      */
181 
182     /* ------------------------------------------------------------ */
183     /**
184      * @see org.eclipse.jetty.continuation.Continuation#isResponseWrapped()
185      */
isResponseWrapped()186     public boolean isResponseWrapped()
187     {
188         return _responseWrapped;
189     }
190 
191     /* ------------------------------------------------------------ */
192     /* (non-Javadoc)
193      * @see javax.servlet.ServletRequest#isInitial()
194      */
isInitial()195     public boolean isInitial()
196     {
197         synchronized(this)
198         {
199             return _initial;
200         }
201     }
202 
isContinuation()203     public boolean isContinuation()
204     {
205         return _continuation;
206     }
207 
208     /* ------------------------------------------------------------ */
209     /* (non-Javadoc)
210      * @see javax.servlet.ServletRequest#isSuspended()
211      */
isSuspended()212     public boolean isSuspended()
213     {
214         synchronized(this)
215         {
216             switch(_state)
217             {
218                 case __ASYNCSTARTED:
219                 case __REDISPATCHING:
220                 case __COMPLETING:
221                 case __ASYNCWAIT:
222                     return true;
223 
224                 default:
225                     return false;
226             }
227         }
228     }
229 
230     /* ------------------------------------------------------------ */
isSuspending()231     public boolean isSuspending()
232     {
233         synchronized(this)
234         {
235             switch(_state)
236             {
237                 case __ASYNCSTARTED:
238                 case __ASYNCWAIT:
239                     return true;
240 
241                 default:
242                     return false;
243             }
244         }
245     }
246 
247     /* ------------------------------------------------------------ */
isDispatchable()248     public boolean isDispatchable()
249     {
250         synchronized(this)
251         {
252             switch(_state)
253             {
254                 case __REDISPATCH:
255                 case __REDISPATCHED:
256                 case __REDISPATCHING:
257                 case __COMPLETING:
258                     return true;
259 
260                 default:
261                     return false;
262             }
263         }
264     }
265 
266     /* ------------------------------------------------------------ */
267     @Override
toString()268     public String toString()
269     {
270         synchronized (this)
271         {
272             return super.toString()+"@"+getStatusString();
273         }
274     }
275 
276     /* ------------------------------------------------------------ */
getStatusString()277     public String getStatusString()
278     {
279         synchronized (this)
280         {
281             return
282             ((_state==__IDLE)?"IDLE":
283                 (_state==__DISPATCHED)?"DISPATCHED":
284                     (_state==__ASYNCSTARTED)?"ASYNCSTARTED":
285                         (_state==__ASYNCWAIT)?"ASYNCWAIT":
286                             (_state==__REDISPATCHING)?"REDISPATCHING":
287                                 (_state==__REDISPATCH)?"REDISPATCH":
288                                     (_state==__REDISPATCHED)?"REDISPATCHED":
289                                         (_state==__COMPLETING)?"COMPLETING":
290                                             (_state==__UNCOMPLETED)?"UNCOMPLETED":
291                                                 (_state==__COMPLETED)?"COMPLETE":
292                                                     ("UNKNOWN?"+_state))+
293             (_initial?",initial":"")+
294             (_resumed?",resumed":"")+
295             (_expired?",expired":"");
296         }
297     }
298 
299     /* ------------------------------------------------------------ */
300     /**
301      * @return false if the handling of the request should not proceed
302      */
handling()303     protected boolean handling()
304     {
305         synchronized (this)
306         {
307             _continuation=false;
308 
309             switch(_state)
310             {
311                 case __IDLE:
312                     _initial=true;
313                     _state=__DISPATCHED;
314                     if (_lastAsyncListeners!=null)
315                         _lastAsyncListeners.clear();
316                     if (_asyncListeners!=null)
317                         _asyncListeners.clear();
318                     else
319                     {
320                         _asyncListeners=_lastAsyncListeners;
321                         _lastAsyncListeners=null;
322                     }
323                     return true;
324 
325                 case __COMPLETING:
326                     _state=__UNCOMPLETED;
327                     return false;
328 
329                 case __ASYNCWAIT:
330                     return false;
331 
332                 case __REDISPATCH:
333                     _state=__REDISPATCHED;
334                     return true;
335 
336                 default:
337                     throw new IllegalStateException(this.getStatusString());
338             }
339         }
340     }
341 
342     /* ------------------------------------------------------------ */
343     /* (non-Javadoc)
344      * @see javax.servlet.ServletRequest#suspend(long)
345      */
doSuspend(final ServletContext context, final ServletRequest request, final ServletResponse response)346     private void doSuspend(final ServletContext context,
347             final ServletRequest request,
348             final ServletResponse response)
349     {
350         synchronized (this)
351         {
352             switch(_state)
353             {
354                 case __DISPATCHED:
355                 case __REDISPATCHED:
356                     _resumed=false;
357                     _expired=false;
358 
359                     if (_event==null || request!=_event.getSuppliedRequest() || response != _event.getSuppliedResponse() || context != _event.getServletContext())
360                         _event=new AsyncEventState(context,request,response);
361                     else
362                     {
363                         _event._dispatchContext=null;
364                         _event._pathInContext=null;
365                     }
366                     _state=__ASYNCSTARTED;
367                     List<AsyncListener> recycle=_lastAsyncListeners;
368                     _lastAsyncListeners=_asyncListeners;
369                     _asyncListeners=recycle;
370                     if (_asyncListeners!=null)
371                         _asyncListeners.clear();
372                     break;
373 
374                 default:
375                     throw new IllegalStateException(this.getStatusString());
376             }
377         }
378 
379         if (_lastAsyncListeners!=null)
380         {
381             for (AsyncListener listener : _lastAsyncListeners)
382             {
383                 try
384                 {
385                     listener.onStartAsync(_event);
386                 }
387                 catch(Exception e)
388                 {
389                     LOG.warn(e);
390                 }
391             }
392         }
393     }
394 
395     /* ------------------------------------------------------------ */
396     /**
397      * Signal that the HttpConnection has finished handling the request.
398      * For blocking connectors, this call may block if the request has
399      * been suspended (startAsync called).
400      * @return true if handling is complete, false if the request should
401      * be handled again (eg because of a resume that happened before unhandle was called)
402      */
unhandle()403     protected boolean unhandle()
404     {
405         synchronized (this)
406         {
407             switch(_state)
408             {
409                 case __REDISPATCHED:
410                 case __DISPATCHED:
411                     _state=__UNCOMPLETED;
412                     return true;
413 
414                 case __IDLE:
415                     throw new IllegalStateException(this.getStatusString());
416 
417                 case __ASYNCSTARTED:
418                     _initial=false;
419                     _state=__ASYNCWAIT;
420                     scheduleTimeout(); // could block and change state.
421                     if (_state==__ASYNCWAIT)
422                         return true;
423                     else if (_state==__COMPLETING)
424                     {
425                         _state=__UNCOMPLETED;
426                         return true;
427                     }
428                     _initial=false;
429                     _state=__REDISPATCHED;
430                     return false;
431 
432                 case __REDISPATCHING:
433                     _initial=false;
434                     _state=__REDISPATCHED;
435                     return false;
436 
437                 case __COMPLETING:
438                     _initial=false;
439                     _state=__UNCOMPLETED;
440                     return true;
441 
442                 default:
443                     throw new IllegalStateException(this.getStatusString());
444             }
445         }
446     }
447 
448     /* ------------------------------------------------------------ */
dispatch()449     public void dispatch()
450     {
451         boolean dispatch=false;
452         synchronized (this)
453         {
454             switch(_state)
455             {
456                 case __ASYNCSTARTED:
457                     _state=__REDISPATCHING;
458                     _resumed=true;
459                     return;
460 
461                 case __ASYNCWAIT:
462                     dispatch=!_expired;
463                     _state=__REDISPATCH;
464                     _resumed=true;
465                     break;
466 
467                 case __REDISPATCH:
468                     return;
469 
470                 default:
471                     throw new IllegalStateException(this.getStatusString());
472             }
473         }
474 
475         if (dispatch)
476         {
477             cancelTimeout();
478             scheduleDispatch();
479         }
480     }
481 
482     /* ------------------------------------------------------------ */
expired()483     protected void expired()
484     {
485         final List<ContinuationListener> cListeners;
486         final List<AsyncListener> aListeners;
487         synchronized (this)
488         {
489             switch(_state)
490             {
491                 case __ASYNCSTARTED:
492                 case __ASYNCWAIT:
493                     cListeners=_continuationListeners;
494                     aListeners=_asyncListeners;
495                     break;
496                 default:
497                     cListeners=null;
498                     aListeners=null;
499                     return;
500             }
501             _expired=true;
502         }
503 
504         if (aListeners!=null)
505         {
506             for (AsyncListener listener : aListeners)
507             {
508                 try
509                 {
510                     listener.onTimeout(_event);
511                 }
512                 catch(Exception e)
513                 {
514                     LOG.debug(e);
515                     _connection.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
516                     break;
517                 }
518             }
519         }
520         if (cListeners!=null)
521         {
522             for (ContinuationListener listener : cListeners)
523             {
524                 try
525                 {
526                     listener.onTimeout(this);
527                 }
528                 catch(Exception e)
529                 {
530                     LOG.warn(e);
531                 }
532             }
533         }
534 
535         synchronized (this)
536         {
537             switch(_state)
538             {
539                 case __ASYNCSTARTED:
540                 case __ASYNCWAIT:
541                     dispatch();
542                     break;
543 
544                 default:
545                     if (!_continuation)
546                         _expired=false;
547             }
548         }
549 
550         scheduleDispatch();
551     }
552 
553     /* ------------------------------------------------------------ */
554     /* (non-Javadoc)
555      * @see javax.servlet.ServletRequest#complete()
556      */
complete()557     public void complete()
558     {
559         // just like resume, except don't set _resumed=true;
560         boolean dispatch=false;
561         synchronized (this)
562         {
563             switch(_state)
564             {
565                 case __DISPATCHED:
566                 case __REDISPATCHED:
567                     throw new IllegalStateException(this.getStatusString());
568 
569                 case __ASYNCSTARTED:
570                     _state=__COMPLETING;
571                     return;
572 
573                 case __ASYNCWAIT:
574                     _state=__COMPLETING;
575                     dispatch=!_expired;
576                     break;
577 
578                 default:
579                     throw new IllegalStateException(this.getStatusString());
580             }
581         }
582 
583         if (dispatch)
584         {
585             cancelTimeout();
586             scheduleDispatch();
587         }
588     }
589 
590     /* ------------------------------------------------------------ */
591     /* (non-Javadoc)
592      * @see javax.servlet.ServletRequest#complete()
593      */
errorComplete()594     public void errorComplete()
595     {
596         // just like complete except can overrule a prior dispatch call;
597         synchronized (this)
598         {
599             switch(_state)
600             {
601                 case __REDISPATCHING:
602                 case __ASYNCSTARTED:
603                     _state=__COMPLETING;
604                     _resumed=false;
605                     return;
606 
607                 case __COMPLETING:
608                     return;
609 
610                 default:
611                     throw new IllegalStateException(this.getStatusString());
612             }
613         }
614     }
615 
616     /* ------------------------------------------------------------ */
617     @Override
createListener(Class<T> clazz)618     public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
619     {
620         try
621         {
622             // TODO inject
623             return clazz.newInstance();
624         }
625         catch(Exception e)
626         {
627             throw new ServletException(e);
628         }
629     }
630 
631 
632     /* ------------------------------------------------------------ */
633     /* (non-Javadoc)
634      * @see javax.servlet.ServletRequest#complete()
635      */
doComplete(Throwable ex)636     protected void doComplete(Throwable ex)
637     {
638         final List<ContinuationListener> cListeners;
639         final List<AsyncListener> aListeners;
640         synchronized (this)
641         {
642             switch(_state)
643             {
644                 case __UNCOMPLETED:
645                     _state=__COMPLETED;
646                     cListeners=_continuationListeners;
647                     aListeners=_asyncListeners;
648                     break;
649 
650                 default:
651                     cListeners=null;
652                     aListeners=null;
653                     throw new IllegalStateException(this.getStatusString());
654             }
655         }
656 
657         if (aListeners!=null)
658         {
659             for (AsyncListener listener : aListeners)
660             {
661                 try
662                 {
663                     if (ex!=null)
664                     {
665                         _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex);
666                         _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,ex.getMessage());
667                         listener.onError(_event);
668                     }
669                     else
670                         listener.onComplete(_event);
671                 }
672                 catch(Exception e)
673                 {
674                     LOG.warn(e);
675                 }
676             }
677         }
678         if (cListeners!=null)
679         {
680             for (ContinuationListener listener : cListeners)
681             {
682                 try
683                 {
684                     listener.onComplete(this);
685                 }
686                 catch(Exception e)
687                 {
688                     LOG.warn(e);
689                 }
690             }
691         }
692     }
693 
694     /* ------------------------------------------------------------ */
recycle()695     protected void recycle()
696     {
697         synchronized (this)
698         {
699             switch(_state)
700             {
701                 case __DISPATCHED:
702                 case __REDISPATCHED:
703                     throw new IllegalStateException(getStatusString());
704                 default:
705                     _state=__IDLE;
706             }
707             _initial = true;
708             _resumed=false;
709             _expired=false;
710             _responseWrapped=false;
711             cancelTimeout();
712             _timeoutMs=DEFAULT_TIMEOUT;
713             _continuationListeners=null;
714         }
715     }
716 
717     /* ------------------------------------------------------------ */
cancel()718     public void cancel()
719     {
720         synchronized (this)
721         {
722             cancelTimeout();
723             _continuationListeners=null;
724         }
725     }
726 
727     /* ------------------------------------------------------------ */
scheduleDispatch()728     protected void scheduleDispatch()
729     {
730         EndPoint endp=_connection.getEndPoint();
731         if (!endp.isBlocking())
732         {
733             ((AsyncEndPoint)endp).asyncDispatch();
734         }
735     }
736 
737     /* ------------------------------------------------------------ */
scheduleTimeout()738     protected void scheduleTimeout()
739     {
740         EndPoint endp=_connection.getEndPoint();
741         if (_timeoutMs>0)
742         {
743             if (endp.isBlocking())
744             {
745                 synchronized(this)
746                 {
747                     _expireAt = System.currentTimeMillis()+_timeoutMs;
748                     long wait=_timeoutMs;
749                     while (_expireAt>0 && wait>0 && _connection.getServer().isRunning())
750                     {
751                         try
752                         {
753                             this.wait(wait);
754                         }
755                         catch (InterruptedException e)
756                         {
757                             LOG.ignore(e);
758                         }
759                         wait=_expireAt-System.currentTimeMillis();
760                     }
761 
762                     if (_expireAt>0 && wait<=0 && _connection.getServer().isRunning())
763                     {
764                         expired();
765                     }
766                 }
767             }
768             else
769             {
770                 ((AsyncEndPoint)endp).scheduleTimeout(_event._timeout,_timeoutMs);
771             }
772         }
773     }
774 
775     /* ------------------------------------------------------------ */
cancelTimeout()776     protected void cancelTimeout()
777     {
778         EndPoint endp=_connection.getEndPoint();
779         if (endp.isBlocking())
780         {
781             synchronized(this)
782             {
783                 _expireAt=0;
784                 this.notifyAll();
785             }
786         }
787         else
788         {
789             final AsyncEventState event=_event;
790             if (event!=null)
791             {
792                 ((AsyncEndPoint)endp).cancelTimeout(event._timeout);
793             }
794         }
795     }
796 
797     /* ------------------------------------------------------------ */
isCompleting()798     public boolean isCompleting()
799     {
800         synchronized (this)
801         {
802             return _state==__COMPLETING;
803         }
804     }
805 
806     /* ------------------------------------------------------------ */
isUncompleted()807     boolean isUncompleted()
808     {
809         synchronized (this)
810         {
811             return _state==__UNCOMPLETED;
812         }
813     }
814 
815     /* ------------------------------------------------------------ */
isComplete()816     public boolean isComplete()
817     {
818         synchronized (this)
819         {
820             return _state==__COMPLETED;
821         }
822     }
823 
824 
825     /* ------------------------------------------------------------ */
isAsyncStarted()826     public boolean isAsyncStarted()
827     {
828         synchronized (this)
829         {
830             switch(_state)
831             {
832                 case __ASYNCSTARTED:
833                 case __REDISPATCHING:
834                 case __REDISPATCH:
835                 case __ASYNCWAIT:
836                     return true;
837 
838                 default:
839                     return false;
840             }
841         }
842     }
843 
844 
845     /* ------------------------------------------------------------ */
isAsync()846     public boolean isAsync()
847     {
848         synchronized (this)
849         {
850             switch(_state)
851             {
852                 case __IDLE:
853                 case __DISPATCHED:
854                 case __UNCOMPLETED:
855                 case __COMPLETED:
856                     return false;
857 
858                 default:
859                     return true;
860             }
861         }
862     }
863 
864     /* ------------------------------------------------------------ */
dispatch(ServletContext context, String path)865     public void dispatch(ServletContext context, String path)
866     {
867         _event._dispatchContext=context;
868         _event.setPath(path);
869         dispatch();
870     }
871 
872     /* ------------------------------------------------------------ */
dispatch(String path)873     public void dispatch(String path)
874     {
875         _event.setPath(path);
876         dispatch();
877     }
878 
879     /* ------------------------------------------------------------ */
getBaseRequest()880     public Request getBaseRequest()
881     {
882         return _connection.getRequest();
883     }
884 
885     /* ------------------------------------------------------------ */
getRequest()886     public ServletRequest getRequest()
887     {
888         if (_event!=null)
889             return _event.getSuppliedRequest();
890         return _connection.getRequest();
891     }
892 
893     /* ------------------------------------------------------------ */
getResponse()894     public ServletResponse getResponse()
895     {
896         if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
897             return _event.getSuppliedResponse();
898         return _connection.getResponse();
899     }
900 
901     /* ------------------------------------------------------------ */
start(final Runnable run)902     public void start(final Runnable run)
903     {
904         final AsyncEventState event=_event;
905         if (event!=null)
906         {
907             _connection.getServer().getThreadPool().dispatch(new Runnable()
908             {
909                 public void run()
910                 {
911                     ((Context)event.getServletContext()).getContextHandler().handle(run);
912                 }
913             });
914         }
915     }
916 
917     /* ------------------------------------------------------------ */
hasOriginalRequestAndResponse()918     public boolean hasOriginalRequestAndResponse()
919     {
920         synchronized (this)
921         {
922             return (_event!=null && _event.getSuppliedRequest()==_connection._request && _event.getSuppliedResponse()==_connection._response);
923         }
924     }
925 
926     /* ------------------------------------------------------------ */
getContextHandler()927     public ContextHandler getContextHandler()
928     {
929         final AsyncEventState event=_event;
930         if (event!=null)
931             return ((Context)event.getServletContext()).getContextHandler();
932         return null;
933     }
934 
935 
936     /* ------------------------------------------------------------ */
937     /**
938      * @see Continuation#isResumed()
939      */
isResumed()940     public boolean isResumed()
941     {
942         synchronized (this)
943         {
944             return _resumed;
945         }
946     }
947     /* ------------------------------------------------------------ */
948     /**
949      * @see Continuation#isExpired()
950      */
isExpired()951     public boolean isExpired()
952     {
953         synchronized (this)
954         {
955             return _expired;
956         }
957     }
958 
959     /* ------------------------------------------------------------ */
960     /**
961      * @see Continuation#resume()
962      */
resume()963     public void resume()
964     {
965         dispatch();
966     }
967 
968 
969 
970     /* ------------------------------------------------------------ */
startAsync(final ServletContext context, final ServletRequest request, final ServletResponse response)971     protected void startAsync(final ServletContext context,
972             final ServletRequest request,
973             final ServletResponse response)
974     {
975         synchronized (this)
976         {
977             _responseWrapped=!(response instanceof Response);
978             doSuspend(context,request,response);
979             if (request instanceof HttpServletRequest)
980             {
981                 _event._pathInContext = URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo());
982             }
983         }
984     }
985 
986     /* ------------------------------------------------------------ */
startAsync()987     protected void startAsync()
988     {
989         _responseWrapped=false;
990         _continuation=false;
991         doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
992     }
993 
994 
995     /* ------------------------------------------------------------ */
996     /**
997      * @see Continuation#suspend()
998      */
suspend(ServletResponse response)999     public void suspend(ServletResponse response)
1000     {
1001         _continuation=true;
1002         _responseWrapped=!(response instanceof Response);
1003         doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),response);
1004     }
1005 
1006     /* ------------------------------------------------------------ */
1007     /**
1008      * @see Continuation#suspend()
1009      */
suspend()1010     public void suspend()
1011     {
1012         _responseWrapped=false;
1013         _continuation=true;
1014         doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
1015     }
1016 
1017     /* ------------------------------------------------------------ */
1018     /**
1019      * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
1020      */
getServletResponse()1021     public ServletResponse getServletResponse()
1022     {
1023         if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
1024             return _event.getSuppliedResponse();
1025         return _connection.getResponse();
1026     }
1027 
1028     /* ------------------------------------------------------------ */
1029     /**
1030      * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
1031      */
getAttribute(String name)1032     public Object getAttribute(String name)
1033     {
1034         return _connection.getRequest().getAttribute(name);
1035     }
1036 
1037     /* ------------------------------------------------------------ */
1038     /**
1039      * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
1040      */
removeAttribute(String name)1041     public void removeAttribute(String name)
1042     {
1043         _connection.getRequest().removeAttribute(name);
1044     }
1045 
1046     /* ------------------------------------------------------------ */
1047     /**
1048      * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
1049      */
setAttribute(String name, Object attribute)1050     public void setAttribute(String name, Object attribute)
1051     {
1052         _connection.getRequest().setAttribute(name,attribute);
1053     }
1054 
1055     /* ------------------------------------------------------------ */
1056     /**
1057      * @see org.eclipse.jetty.continuation.Continuation#undispatch()
1058      */
undispatch()1059     public void undispatch()
1060     {
1061         if (isSuspended())
1062         {
1063             if (LOG.isDebugEnabled())
1064                 throw new ContinuationThrowable();
1065             else
1066                 throw __exception;
1067         }
1068         throw new IllegalStateException("!suspended");
1069     }
1070 
1071     /* ------------------------------------------------------------ */
1072     /* ------------------------------------------------------------ */
1073     public class AsyncTimeout extends Timeout.Task implements Runnable
1074     {
1075             @Override
expired()1076             public void expired()
1077             {
1078                 AsyncContinuation.this.expired();
1079             }
1080 
1081             @Override
run()1082             public void run()
1083             {
1084                 AsyncContinuation.this.expired();
1085             }
1086     }
1087 
1088     /* ------------------------------------------------------------ */
1089     /* ------------------------------------------------------------ */
1090     public class AsyncEventState extends AsyncEvent
1091     {
1092         private final ServletContext _suspendedContext;
1093         private ServletContext _dispatchContext;
1094         private String _pathInContext;
1095         private Timeout.Task _timeout=  new AsyncTimeout();
1096 
AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)1097         public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
1098         {
1099             super(AsyncContinuation.this, request,response);
1100             _suspendedContext=context;
1101             // Get the base request So we can remember the initial paths
1102             Request r=_connection.getRequest();
1103 
1104             // If we haven't been async dispatched before
1105             if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
1106             {
1107                 // We are setting these attributes during startAsync, when the spec implies that
1108                 // they are only available after a call to AsyncContext.dispatch(...);
1109 
1110                 // have we been forwarded before?
1111                 String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
1112                 if (uri!=null)
1113                 {
1114                     r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
1115                     r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
1116                     r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
1117                     r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
1118                     r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
1119                 }
1120                 else
1121                 {
1122                     r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI());
1123                     r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath());
1124                     r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath());
1125                     r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo());
1126                     r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString());
1127                 }
1128             }
1129         }
1130 
getSuspendedContext()1131         public ServletContext getSuspendedContext()
1132         {
1133             return _suspendedContext;
1134         }
1135 
getDispatchContext()1136         public ServletContext getDispatchContext()
1137         {
1138             return _dispatchContext;
1139         }
1140 
getServletContext()1141         public ServletContext getServletContext()
1142         {
1143             return _dispatchContext==null?_suspendedContext:_dispatchContext;
1144         }
1145 
setPath(String path)1146         public void setPath(String path)
1147         {
1148             _pathInContext=path;
1149         }
1150 
1151         /* ------------------------------------------------------------ */
1152         /**
1153          * @return The path in the context
1154          */
getPath()1155         public String getPath()
1156         {
1157             return _pathInContext;
1158         }
1159     }
1160 }
1161