]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/HttpChannelState.java
updating jetty to jetty-9.2.16.v2016040
[gigi.git] / lib / jetty / org / eclipse / jetty / server / HttpChannelState.java
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 java.util.ArrayList;
22 import java.util.List;
23 import java.util.concurrent.TimeUnit;
24
25 import javax.servlet.AsyncListener;
26 import javax.servlet.RequestDispatcher;
27 import javax.servlet.ServletContext;
28 import javax.servlet.ServletResponse;
29
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;
35
36 /**
37  * Implementation of AsyncContext interface that holds the state of request-response cycle.
38  */
39 public class HttpChannelState
40 {
41     private static final Logger LOG = Log.getLogger(HttpChannelState.class);
42
43     private final static long DEFAULT_TIMEOUT=Long.getLong("org.eclipse.jetty.server.HttpChannelState.DEFAULT_TIMEOUT",30000L);
44
45     /** The dispatched state of the HttpChannel, used to control the overall livecycle
46      */
47     public enum State
48     {
49         IDLE,             // Idle request
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
57     }
58
59     /**
60      * The actions to take as the channel moves from state to state.
61      */
62     public enum Action
63     {
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
71     }
72     
73     /**
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.
77      */
78     public enum Async
79     {
80         STARTED,
81         DISPATCH,
82         COMPLETE,
83         EXPIRING,
84         EXPIRED
85     }
86
87     private final boolean DEBUG=LOG.isDebugEnabled();
88     private final HttpChannel<?> _channel;
89
90     private List<AsyncListener> _asyncListeners;
91     private State _state;
92     private Async _async;
93     private boolean _initial;
94     private boolean _asyncRead;
95     private boolean _asyncWrite;
96     private long _timeoutMs=DEFAULT_TIMEOUT;
97     private AsyncContextEvent _event;
98
99     protected HttpChannelState(HttpChannel<?> channel)
100     {
101         _channel=channel;
102         _state=State.IDLE;
103         _async=null;
104         _initial=true;
105     }
106
107     public State getState()
108     {
109         synchronized(this)
110         {
111             return _state;
112         }
113     }
114
115     public void addListener(AsyncListener listener)
116     {
117         synchronized(this)
118         {
119             if (_asyncListeners==null)
120                 _asyncListeners=new ArrayList<>();
121             _asyncListeners.add(listener);
122         }
123     }
124
125     public void setTimeout(long ms)
126     {
127         synchronized(this)
128         {
129             _timeoutMs=ms;
130         }
131     }
132
133     public long getTimeout()
134     {
135         synchronized(this)
136         {
137             return _timeoutMs;
138         }
139     }
140
141     public AsyncContextEvent getAsyncContextEvent()
142     {
143         synchronized(this)
144         {
145             return _event;
146         }
147     }
148
149     @Override
150     public String toString()
151     {
152         synchronized (this)
153         {
154             return String.format("%s@%x{s=%s i=%b a=%s}",getClass().getSimpleName(),hashCode(),_state,_initial,_async);
155         }
156     }
157
158     public String getStatusString()
159     {
160         synchronized (this)
161         {
162             return String.format("s=%s i=%b a=%s",_state,_initial,_async);
163         }
164     }
165
166     /**
167      * @return Next handling of the request should proceed
168      */
169     protected Action handling()
170     {
171         synchronized (this)
172         {
173             if(DEBUG)
174                 LOG.debug("{} handling {}",this,_state);
175             switch(_state)
176             {
177                 case IDLE:
178                     _initial=true;
179                     _state=State.DISPATCHED;
180                     return Action.REQUEST_DISPATCH;
181
182                 case COMPLETING:
183                     return Action.COMPLETE;
184
185                 case COMPLETED:
186                     return Action.WAIT;
187
188                 case ASYNC_WOKEN:
189                     if (_asyncRead)
190                     {
191                         _state=State.ASYNC_IO;
192                         _asyncRead=false;
193                         return Action.READ_CALLBACK;
194                     }
195                     if (_asyncWrite)
196                     {
197                         _state=State.ASYNC_IO;
198                         _asyncWrite=false;
199                         return Action.WRITE_CALLBACK;
200                     }
201                     
202                     if (_async!=null)
203                     {
204                         Async async=_async;
205                         switch(async)
206                         {
207                             case COMPLETE:
208                                 _state=State.COMPLETING;
209                                 return Action.COMPLETE;
210                             case DISPATCH:
211                                 _state=State.DISPATCHED;
212                                 _async=null;
213                                 return Action.ASYNC_DISPATCH;
214                             case EXPIRING:
215                                 break;
216                             case EXPIRED:
217                                 _state=State.DISPATCHED;
218                                 _async=null;
219                                 return Action.ASYNC_EXPIRED;
220                             case STARTED:
221                                 // TODO
222                                 if (DEBUG)
223                                     LOG.debug("TODO Fix this double dispatch",new IllegalStateException(this
224                                             .getStatusString()));
225                                 return Action.WAIT;
226                         }
227                     }
228                     
229                     return Action.WAIT;
230
231                 default:
232                     throw new IllegalStateException(this.getStatusString());
233             }
234         }
235     }
236
237     public void startAsync(AsyncContextEvent event)
238     {
239         final List<AsyncListener> lastAsyncListeners;
240         
241         synchronized (this)
242         {
243             if (_state!=State.DISPATCHED || _async!=null)
244                 throw new IllegalStateException(this.getStatusString());
245             
246             _async=Async.STARTED;
247             _event=event;
248             lastAsyncListeners=_asyncListeners;
249             _asyncListeners=null;
250         }
251
252         if (lastAsyncListeners!=null)
253         {
254             for (AsyncListener listener : lastAsyncListeners)
255             {
256                 try
257                 {
258                     listener.onStartAsync(event);
259                 }
260                 catch(Exception e)
261                 {
262                     LOG.warn(e);
263                 }
264             }
265         }
266     }
267
268     protected void error(Throwable th)
269     {
270         synchronized (this)
271         {
272             if (_event!=null)
273                 _event.setThrowable(th);
274         }
275     }
276
277     /**
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)
283      */
284     protected Action unhandle()
285     {
286         synchronized (this)
287         {
288             if(DEBUG)
289                 LOG.debug("{} unhandle {}",this,_state);
290             
291             switch(_state)
292             {
293                 case DISPATCHED:
294                 case ASYNC_IO:
295                     break;
296                 default:
297                     throw new IllegalStateException(this.getStatusString());
298             }
299
300             if (_asyncRead)
301             {
302                 _state=State.ASYNC_IO;
303                 _asyncRead=false;
304                 return Action.READ_CALLBACK;
305             }
306             
307             if (_asyncWrite)
308             {
309                 _asyncWrite=false;
310                 _state=State.ASYNC_IO;
311                 return Action.WRITE_CALLBACK;
312             }
313
314             if (_async!=null)
315             {
316                 _initial=false;
317                 switch(_async)
318                 {
319                     case COMPLETE:
320                         _state=State.COMPLETING;
321                         _async=null;
322                         return Action.COMPLETE;
323                     case DISPATCH:
324                         _state=State.DISPATCHED;
325                         _async=null;
326                         return Action.ASYNC_DISPATCH;
327                     case EXPIRED:
328                         _state=State.DISPATCHED;
329                         _async=null;
330                         return Action.ASYNC_EXPIRED;
331                     case EXPIRING:
332                     case STARTED:
333                         scheduleTimeout();
334                         _state=State.ASYNC_WAIT;
335                         return Action.WAIT;
336                 }
337             }
338             
339             _state=State.COMPLETING;
340             return Action.COMPLETE;
341         }
342     }
343
344     public void dispatch(ServletContext context, String path)
345     {
346         boolean dispatch;
347         synchronized (this)
348         {
349             if (_async!=Async.STARTED && _async!=Async.EXPIRING)
350                 throw new IllegalStateException("AsyncContext#dispath "+this.getStatusString());
351             _async=Async.DISPATCH;
352             
353             if (context!=null)
354                 _event.setDispatchContext(context);
355             if (path!=null)
356                 _event.setDispatchPath(path);
357            
358             switch(_state)
359             {
360                 case DISPATCHED:
361                 case ASYNC_IO:
362                     dispatch=false;
363                     break;
364                 case ASYNC_WAIT:
365                     _state=State.ASYNC_WOKEN;
366                     dispatch=true;
367                     break;
368                 case ASYNC_WOKEN:
369                     dispatch=false;
370                     break;
371                 default:
372                     LOG.warn("async dispatched when complete {}",this);
373                     dispatch=false;
374                     break;
375             }
376         }
377
378         cancelTimeout();
379         if (dispatch)
380             scheduleDispatch();
381     }
382
383     protected void expired()
384     {
385         final List<AsyncListener> aListeners;
386         AsyncContextEvent event;
387         synchronized (this)
388         {
389             if (_async!=Async.STARTED)
390                 return;
391             _async=Async.EXPIRING;
392             event=_event;
393             aListeners=_asyncListeners;
394         }
395
396         if (aListeners!=null)
397         {
398             for (AsyncListener listener : aListeners)
399             {
400                 try
401                 {
402                     listener.onTimeout(event);
403                 }
404                 catch(Exception e)
405                 {
406                     LOG.debug(e);
407                     event.setThrowable(e);
408                     _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
409                     break;
410                 }
411             }
412         }
413         
414         boolean dispatch=false;
415         synchronized (this)
416         {
417             if (_async==Async.EXPIRING)
418             {
419                 _async=Async.EXPIRED;
420                 if (_state==State.ASYNC_WAIT)
421                 {
422                     _state=State.ASYNC_WOKEN;
423                     dispatch=true;
424                 }
425             }
426         }
427
428         if (dispatch)
429             scheduleDispatch();
430     }
431
432     public void complete()
433     {
434         // just like resume, except don't set _dispatched=true;
435         boolean handle=false;
436         synchronized (this)
437         {
438             if (_async!=Async.STARTED && _async!=Async.EXPIRING)
439                 throw new IllegalStateException(this.getStatusString());
440             _async=Async.COMPLETE;
441             if (_state==State.ASYNC_WAIT)
442             {
443                 handle=true;
444                 _state=State.ASYNC_WOKEN;
445             }
446         }
447
448         cancelTimeout();
449         if (handle)
450         {
451             ContextHandler handler=getContextHandler();
452             if (handler!=null)
453                 handler.handle(_channel);
454             else
455                 _channel.handle();
456         }
457     }
458
459     public void errorComplete()
460     {
461         synchronized (this)
462         {
463             _async=Async.COMPLETE;
464             _event.setDispatchContext(null);
465             _event.setDispatchPath(null);
466         }
467
468         cancelTimeout();
469     }
470
471     protected void completed()
472     {
473         final List<AsyncListener> aListeners;
474         final AsyncContextEvent event;
475         synchronized (this)
476         {
477             switch(_state)
478             {
479                 case COMPLETING:
480                     _state=State.COMPLETED;
481                     aListeners=_asyncListeners;
482                     event=_event;
483                     break;
484
485                 default:
486                     throw new IllegalStateException(this.getStatusString());
487             }
488         }
489
490         if (event!=null)
491         {
492             if (aListeners!=null)
493             {
494                 if (event.getThrowable()!=null)
495                 {
496                     event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
497                     event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage());
498                 }
499
500                 for (AsyncListener listener : aListeners)
501                 {
502                     try
503                     {
504                         if (event.getThrowable()!=null)
505                             listener.onError(event);
506                         else
507                             listener.onComplete(event);
508                     }
509                     catch(Exception e)
510                     {
511                         LOG.warn(e);
512                     }
513                 }
514             }
515
516             event.completed();
517         }
518     }
519
520     protected void recycle()
521     {
522         synchronized (this)
523         {
524             switch(_state)
525             {
526                 case DISPATCHED:
527                 case ASYNC_IO:
528                     throw new IllegalStateException(getStatusString());
529                 case UPGRADED:
530                     return;
531                 default:
532                     break;
533             }
534             _asyncListeners=null;
535             _state=State.IDLE;
536             _async=null;
537             _initial=true;
538             _asyncRead=false;
539             _asyncWrite=false;
540             _timeoutMs=DEFAULT_TIMEOUT;
541             cancelTimeout();
542             _event=null;
543         }
544     }
545     
546     public void upgrade()
547     {
548         synchronized (this)
549         {
550             switch(_state)
551             {
552                 case IDLE:
553                 case COMPLETED:
554                     break;
555                 default:
556                     throw new IllegalStateException(getStatusString());
557             }
558             _asyncListeners=null;
559             _state=State.UPGRADED;
560             _async=null;
561             _initial=true;
562             _asyncRead=false;
563             _asyncWrite=false;
564             _timeoutMs=DEFAULT_TIMEOUT;
565             cancelTimeout();
566             _event=null;
567         }
568     }
569
570
571     protected void scheduleDispatch()
572     {
573         _channel.execute(_channel);
574     }
575
576     protected void scheduleTimeout()
577     {
578         Scheduler scheduler = _channel.getScheduler();
579         if (scheduler!=null && _timeoutMs>0)
580             _event.setTimeoutTask(scheduler.schedule(_event,_timeoutMs,TimeUnit.MILLISECONDS));
581     }
582
583     protected void cancelTimeout()
584     {
585         final AsyncContextEvent event;
586         synchronized (this)
587         { 
588             event=_event;
589         }
590         if (event!=null)
591             event.cancelTimeoutTask();
592     }
593
594     public boolean isExpired()
595     {
596         synchronized (this)
597         {
598             return _async==Async.EXPIRED;
599         }
600     }
601
602     public boolean isInitial()
603     {
604         synchronized(this)
605         {
606             return _initial;
607         }
608     }
609
610     public boolean isSuspended()
611     {
612         synchronized(this)
613         {
614             return _state==State.ASYNC_WAIT || _state==State.DISPATCHED && _async==Async.STARTED;
615         }
616     }
617
618     boolean isCompleting()
619     {
620         synchronized (this)
621         {
622             return _state==State.COMPLETING;
623         }
624     }
625
626     boolean isCompleted()
627     {
628         synchronized (this)
629         {
630             return _state == State.COMPLETED;
631         }
632     }
633
634     public boolean isAsyncStarted()
635     {
636         synchronized (this)
637         {    
638             if (_state==State.DISPATCHED)
639                 return _async!=null;
640             return _async==Async.STARTED || _async==Async.EXPIRING;
641         }
642     }
643
644     public boolean isAsync()
645     {
646         synchronized (this)
647         {
648             return !_initial || _async!=null;
649         }
650     }
651
652     public Request getBaseRequest()
653     {
654         return _channel.getRequest();
655     }
656
657     public HttpChannel<?> getHttpChannel()
658     {
659         return _channel;
660     }
661
662     public ContextHandler getContextHandler()
663     {
664         final AsyncContextEvent event;
665         synchronized (this)
666         { 
667             event=_event;
668         }
669        
670         if (event!=null)
671         {
672             Context context=((Context)event.getServletContext());
673             if (context!=null)
674                 return context.getContextHandler();
675         }
676         return null;
677     }
678
679     public ServletResponse getServletResponse()
680     {
681         final AsyncContextEvent event;
682         synchronized (this)
683         { 
684             event=_event;
685         }
686         if (event!=null && event.getSuppliedResponse()!=null)
687             return event.getSuppliedResponse();
688         return _channel.getResponse();
689     }
690
691     public Object getAttribute(String name)
692     {
693         return _channel.getRequest().getAttribute(name);
694     }
695
696     public void removeAttribute(String name)
697     {
698         _channel.getRequest().removeAttribute(name);
699     }
700
701     public void setAttribute(String name, Object attribute)
702     {
703         _channel.getRequest().setAttribute(name,attribute);
704     }
705
706     public void onReadPossible()
707     {
708         boolean handle=false;
709
710         synchronized (this)
711         {
712             _asyncRead=true;
713             if (_state==State.ASYNC_WAIT)
714             {
715                 _state=State.ASYNC_WOKEN;
716                 handle=true;
717             }
718         }
719
720         if (handle)
721             _channel.execute(_channel);
722     }
723     
724     public void onWritePossible()
725     {
726         boolean handle=false;
727
728         synchronized (this)
729         {
730             _asyncWrite=true;
731             if (_state==State.ASYNC_WAIT)
732             {
733                 _state=State.ASYNC_WOKEN;
734                 handle=true;
735             }
736         }
737
738         if (handle)
739             _channel.execute(_channel);
740     }
741     
742 }