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