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