1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 import javax.servlet.AsyncContext;
25 import javax.servlet.AsyncEvent;
26 import javax.servlet.AsyncListener;
27 import javax.servlet.RequestDispatcher;
28 import javax.servlet.ServletContext;
29 import javax.servlet.ServletException;
30 import javax.servlet.ServletRequest;
31 import javax.servlet.ServletResponse;
32 import javax.servlet.http.HttpServletRequest;
33
34 import org.eclipse.jetty.server.handler.ContextHandler;
35 import org.eclipse.jetty.server.handler.ContextHandler.Context;
36 import org.eclipse.jetty.util.URIUtil;
37 import org.eclipse.jetty.util.log.Log;
38 import org.eclipse.jetty.util.log.Logger;
39 import org.eclipse.jetty.util.thread.Scheduler;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class HttpChannelState implements AsyncContext
60 {
61 private static final Logger LOG = Log.getLogger(HttpChannelState.class);
62
63 private final static long DEFAULT_TIMEOUT=30000L;
64
65 public enum State
66 {
67 IDLE,
68 DISPATCHED,
69 ASYNCSTARTED,
70 REDISPATCHING,
71 ASYNCWAIT,
72 REDISPATCH,
73 REDISPATCHED,
74 COMPLETECALLED,
75 COMPLETING,
76 COMPLETED
77 }
78
79 private final HttpChannel<?> _channel;
80 private List<AsyncListener> _lastAsyncListeners;
81 private List<AsyncListener> _asyncListeners;
82
83 private State _state;
84 private boolean _initial;
85 private boolean _dispatched;
86 private boolean _expired;
87 private volatile boolean _responseWrapped;
88 private long _timeoutMs=DEFAULT_TIMEOUT;
89 private AsyncEventState _event;
90
91 protected HttpChannelState(HttpChannel<?> channel)
92 {
93 _channel=channel;
94 _state=State.IDLE;
95 _initial=true;
96 }
97
98 public State getState()
99 {
100 synchronized(this)
101 {
102 return _state;
103 }
104 }
105
106 @Override
107 public void addListener(AsyncListener listener)
108 {
109 synchronized(this)
110 {
111 if (_asyncListeners==null)
112 _asyncListeners=new ArrayList<>();
113 _asyncListeners.add(listener);
114 }
115 }
116
117 @Override
118 public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response)
119 {
120 synchronized(this)
121 {
122 if (_asyncListeners==null)
123 _asyncListeners=new ArrayList<>();
124 _asyncListeners.add(listener);
125 }
126 }
127
128
129 @Override
130 public void setTimeout(long ms)
131 {
132 synchronized(this)
133 {
134 _timeoutMs=ms;
135 }
136 }
137
138 @Override
139 public long getTimeout()
140 {
141 synchronized(this)
142 {
143 return _timeoutMs;
144 }
145 }
146
147 public AsyncEventState getAsyncEventState()
148 {
149 synchronized(this)
150 {
151 return _event;
152 }
153 }
154
155 @Override
156 public String toString()
157 {
158 synchronized (this)
159 {
160 return super.toString()+"@"+getStatusString();
161 }
162 }
163
164 public String getStatusString()
165 {
166 synchronized (this)
167 {
168 return _state+
169 (_initial?",initial":"")+
170 (_dispatched?",resumed":"")+
171 (_expired?",expired":"");
172 }
173 }
174
175
176
177
178 protected boolean handling()
179 {
180 synchronized (this)
181 {
182 switch(_state)
183 {
184 case IDLE:
185 _initial=true;
186 _state=State.DISPATCHED;
187 if (_lastAsyncListeners!=null)
188 _lastAsyncListeners.clear();
189 if (_asyncListeners!=null)
190 _asyncListeners.clear();
191 else
192 {
193 _asyncListeners=_lastAsyncListeners;
194 _lastAsyncListeners=null;
195 }
196 break;
197
198 case COMPLETECALLED:
199 _state=State.COMPLETING;
200 return false;
201
202 case ASYNCWAIT:
203 case COMPLETING:
204 case COMPLETED:
205 return false;
206
207 case REDISPATCH:
208 _state=State.REDISPATCHED;
209 break;
210
211 default:
212 throw new IllegalStateException(this.getStatusString());
213 }
214
215 _responseWrapped=false;
216 return true;
217
218 }
219 }
220
221 public void startAsync()
222 {
223 synchronized (this)
224 {
225 switch(_state)
226 {
227 case DISPATCHED:
228 case REDISPATCHED:
229 _dispatched=false;
230 _expired=false;
231 _responseWrapped=false;
232 _event=new AsyncEventState(_channel.getRequest().getServletContext(),_channel.getRequest(),_channel.getResponse());
233 _state=State.ASYNCSTARTED;
234 List<AsyncListener> listeners=_lastAsyncListeners;
235 _lastAsyncListeners=_asyncListeners;
236 _asyncListeners=listeners;
237 if (_asyncListeners!=null)
238 _asyncListeners.clear();
239 break;
240
241 default:
242 throw new IllegalStateException(this.getStatusString());
243 }
244 }
245
246 if (_lastAsyncListeners!=null)
247 {
248 for (AsyncListener listener : _lastAsyncListeners)
249 {
250 try
251 {
252 listener.onStartAsync(_event);
253 }
254 catch(Exception e)
255 {
256 LOG.warn(e);
257 }
258 }
259 }
260 }
261
262 public void startAsync(final ServletContext context,final ServletRequest request,final ServletResponse response)
263 {
264 synchronized (this)
265 {
266 switch(_state)
267 {
268 case DISPATCHED:
269 case REDISPATCHED:
270 _dispatched=false;
271 _expired=false;
272 _responseWrapped=response!=_channel.getResponse();
273 _event=new AsyncEventState(context,request,response);
274 _event._pathInContext = (request instanceof HttpServletRequest)?URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo()):null;
275 _state=State.ASYNCSTARTED;
276 List<AsyncListener> listeners=_lastAsyncListeners;
277 _lastAsyncListeners=_asyncListeners;
278 _asyncListeners=listeners;
279 if (_asyncListeners!=null)
280 _asyncListeners.clear();
281 break;
282
283 default:
284 throw new IllegalStateException(this.getStatusString());
285 }
286 }
287
288 if (_lastAsyncListeners!=null)
289 {
290 for (AsyncListener listener : _lastAsyncListeners)
291 {
292 try
293 {
294 listener.onStartAsync(_event);
295 }
296 catch(Exception e)
297 {
298 LOG.warn(e);
299 }
300 }
301 }
302 }
303
304 protected void error(Throwable th)
305 {
306 synchronized (this)
307 {
308 if (_event!=null)
309 _event._cause=th;
310 }
311 }
312
313
314
315
316
317
318
319
320 protected boolean unhandle()
321 {
322 synchronized (this)
323 {
324 switch(_state)
325 {
326 case REDISPATCHED:
327 case DISPATCHED:
328 _state=State.COMPLETING;
329 return true;
330
331 case IDLE:
332 throw new IllegalStateException(this.getStatusString());
333
334 case ASYNCSTARTED:
335 _initial=false;
336 _state=State.ASYNCWAIT;
337 scheduleTimeout();
338 if (_state==State.ASYNCWAIT)
339 return true;
340 else if (_state==State.COMPLETECALLED)
341 {
342 _state=State.COMPLETING;
343 return true;
344 }
345 _initial=false;
346 _state=State.REDISPATCHED;
347 return false;
348
349 case REDISPATCHING:
350 _initial=false;
351 _state=State.REDISPATCHED;
352 return false;
353
354 case COMPLETECALLED:
355 _initial=false;
356 _state=State.COMPLETING;
357 return true;
358
359 default:
360 throw new IllegalStateException(this.getStatusString());
361 }
362 }
363 }
364
365 @Override
366 public void dispatch()
367 {
368 boolean dispatch;
369 synchronized (this)
370 {
371 switch(_state)
372 {
373 case ASYNCSTARTED:
374 _state=State.REDISPATCHING;
375 _dispatched=true;
376 return;
377
378 case ASYNCWAIT:
379 dispatch=!_expired;
380 _state=State.REDISPATCH;
381 _dispatched=true;
382 break;
383
384 case REDISPATCH:
385 return;
386
387 default:
388 throw new IllegalStateException(this.getStatusString());
389 }
390 }
391
392 if (dispatch)
393 {
394 cancelTimeout();
395 scheduleDispatch();
396 }
397 }
398
399 public boolean isDispatched()
400 {
401 synchronized (this)
402 {
403 return _dispatched;
404 }
405 }
406
407 protected void expired()
408 {
409 final List<AsyncListener> aListeners;
410 synchronized (this)
411 {
412 switch(_state)
413 {
414 case ASYNCSTARTED:
415 case ASYNCWAIT:
416 aListeners=_asyncListeners;
417 break;
418 default:
419 return;
420 }
421 _expired=true;
422 }
423
424 if (aListeners!=null)
425 {
426 for (AsyncListener listener : aListeners)
427 {
428 try
429 {
430 listener.onTimeout(_event);
431 }
432 catch(Exception e)
433 {
434 LOG.warn(e);
435 }
436 }
437 }
438
439 boolean complete;
440 synchronized (this)
441 {
442 switch(_state)
443 {
444 case ASYNCSTARTED:
445 case ASYNCWAIT:
446 complete = true;
447 break;
448 default:
449 complete = false;
450 break;
451 }
452 }
453 if (complete)
454 complete();
455
456 scheduleDispatch();
457 }
458
459 @Override
460 public void complete()
461 {
462
463 boolean dispatch;
464 synchronized (this)
465 {
466 switch(_state)
467 {
468 case DISPATCHED:
469 case REDISPATCHED:
470 throw new IllegalStateException(this.getStatusString());
471
472 case IDLE:
473 case ASYNCSTARTED:
474 _state=State.COMPLETECALLED;
475 return;
476
477 case ASYNCWAIT:
478 _state=State.COMPLETECALLED;
479 dispatch=!_expired;
480 break;
481
482 default:
483 throw new IllegalStateException(this.getStatusString());
484 }
485 }
486
487 if (dispatch)
488 {
489 cancelTimeout();
490 scheduleDispatch();
491 }
492 }
493
494 @Override
495 public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
496 {
497 try
498 {
499 return clazz.newInstance();
500 }
501 catch(Exception e)
502 {
503 throw new ServletException(e);
504 }
505 }
506
507 protected void completed()
508 {
509 final List<AsyncListener> aListeners;
510 synchronized (this)
511 {
512 switch(_state)
513 {
514 case COMPLETING:
515 _state=State.COMPLETED;
516 aListeners=_asyncListeners;
517 break;
518
519 default:
520 throw new IllegalStateException(this.getStatusString());
521 }
522 }
523
524 if (aListeners!=null)
525 {
526 for (AsyncListener listener : aListeners)
527 {
528 try
529 {
530 if (_event!=null && _event._cause!=null)
531 {
532 _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,_event._cause);
533 _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,_event._cause.getMessage());
534 listener.onError(_event);
535 }
536 else
537 listener.onComplete(_event);
538 }
539 catch(Exception e)
540 {
541 LOG.warn(e);
542 }
543 }
544 }
545 }
546
547 protected void recycle()
548 {
549 synchronized (this)
550 {
551 switch(_state)
552 {
553 case DISPATCHED:
554 case REDISPATCHED:
555 throw new IllegalStateException(getStatusString());
556 default:
557 _state=State.IDLE;
558 }
559 _initial = true;
560 _dispatched=false;
561 _expired=false;
562 _responseWrapped=false;
563 cancelTimeout();
564 _timeoutMs=DEFAULT_TIMEOUT;
565 _event=null;
566 }
567 }
568
569 public void cancel()
570 {
571 synchronized (this)
572 {
573 cancelTimeout();
574 }
575 }
576
577 protected void scheduleDispatch()
578 {
579 _channel.execute(_channel);
580 }
581
582 protected void scheduleTimeout()
583 {
584 Scheduler scheduler = _channel.getScheduler();
585 if (scheduler!=null && _timeoutMs>0)
586 _event._timeout=scheduler.schedule(new AsyncTimeout(),_timeoutMs,TimeUnit.MILLISECONDS);
587 }
588
589 protected void cancelTimeout()
590 {
591 AsyncEventState event=_event;
592 if (event!=null)
593 {
594 Scheduler.Task task=event._timeout;
595 if (task!=null)
596 task.cancel();
597 }
598 }
599
600 public boolean isInitial()
601 {
602 synchronized(this)
603 {
604 return _initial;
605 }
606 }
607
608 public boolean isSuspended()
609 {
610 synchronized(this)
611 {
612 switch(_state)
613 {
614 case ASYNCSTARTED:
615 case REDISPATCHING:
616 case COMPLETECALLED:
617 case ASYNCWAIT:
618 return true;
619
620 default:
621 return false;
622 }
623 }
624 }
625
626 boolean isCompleting()
627 {
628 synchronized (this)
629 {
630 return _state==State.COMPLETING;
631 }
632 }
633
634 public boolean isAsync()
635 {
636 synchronized (this)
637 {
638 switch(_state)
639 {
640 case ASYNCSTARTED:
641 case REDISPATCHING:
642 case ASYNCWAIT:
643 case REDISPATCHED:
644 case REDISPATCH:
645 case COMPLETECALLED:
646 return true;
647
648 default:
649 return false;
650 }
651 }
652 }
653
654 @Override
655 public void dispatch(ServletContext context, String path)
656 {
657 _event._dispatchContext=context;
658 _event._pathInContext=path;
659 dispatch();
660 }
661
662 @Override
663 public void dispatch(String path)
664 {
665 _event._pathInContext=path;
666 dispatch();
667 }
668
669 public Request getBaseRequest()
670 {
671 return _channel.getRequest();
672 }
673
674 @Override
675 public ServletRequest getRequest()
676 {
677 if (_event!=null)
678 return _event.getSuppliedRequest();
679 return _channel.getRequest();
680 }
681
682 @Override
683 public ServletResponse getResponse()
684 {
685 if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
686 return _event.getSuppliedResponse();
687 return _channel.getResponse();
688 }
689
690 @Override
691 public void start(final Runnable run)
692 {
693 final AsyncEventState event=_event;
694 if (event!=null)
695 {
696 _channel.execute(new Runnable()
697 {
698 @Override
699 public void run()
700 {
701 ((Context)event.getServletContext()).getContextHandler().handle(run);
702 }
703 });
704 }
705 }
706
707 @Override
708 public boolean hasOriginalRequestAndResponse()
709 {
710 synchronized (this)
711 {
712 return (_event!=null && _event.getSuppliedRequest()==_channel.getRequest() && _event.getSuppliedResponse()==_channel.getResponse());
713 }
714 }
715
716 public ContextHandler getContextHandler()
717 {
718 final AsyncEventState event=_event;
719 if (event!=null)
720 return ((Context)event.getServletContext()).getContextHandler();
721 return null;
722 }
723
724 public ServletResponse getServletResponse()
725 {
726 if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
727 return _event.getSuppliedResponse();
728 return _channel.getResponse();
729 }
730
731 public Object getAttribute(String name)
732 {
733 return _channel.getRequest().getAttribute(name);
734 }
735
736 public void removeAttribute(String name)
737 {
738 _channel.getRequest().removeAttribute(name);
739 }
740
741 public void setAttribute(String name, Object attribute)
742 {
743 _channel.getRequest().setAttribute(name,attribute);
744 }
745
746 public class AsyncTimeout implements Runnable
747 {
748 @Override
749 public void run()
750 {
751 HttpChannelState.this.expired();
752 }
753 }
754
755 public class AsyncEventState extends AsyncEvent
756 {
757 final private ServletContext _suspendedContext;
758 private String _pathInContext;
759 private Scheduler.Task _timeout;
760 private ServletContext _dispatchContext;
761 private Throwable _cause;
762
763 public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
764 {
765 super(HttpChannelState.this, request,response);
766 _suspendedContext=context;
767
768
769 Request r=_channel.getRequest();
770
771
772 if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
773 {
774
775
776
777
778 String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
779 if (uri!=null)
780 {
781 r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
782 r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
783 r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
784 r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
785 r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
786 }
787 else
788 {
789 r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI());
790 r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath());
791 r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath());
792 r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo());
793 r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString());
794 }
795 }
796 }
797
798 public ServletContext getSuspendedContext()
799 {
800 return _suspendedContext;
801 }
802
803 public ServletContext getDispatchContext()
804 {
805 return _dispatchContext;
806 }
807
808 public ServletContext getServletContext()
809 {
810 return _dispatchContext==null?_suspendedContext:_dispatchContext;
811 }
812
813
814
815
816 public String getPath()
817 {
818 return _pathInContext;
819 }
820 }
821 }