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.io.IOException;
22 import java.net.InetSocketAddress;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.ClosedChannelException;
25 import java.util.List;
26 import java.util.concurrent.TimeoutException;
27 import java.util.concurrent.atomic.AtomicBoolean;
28 import java.util.concurrent.atomic.AtomicInteger;
29
30 import javax.servlet.DispatcherType;
31 import javax.servlet.RequestDispatcher;
32 import javax.servlet.UnavailableException;
33 import javax.servlet.http.HttpServletRequest;
34
35 import org.eclipse.jetty.http.BadMessageException;
36 import org.eclipse.jetty.http.HttpFields;
37 import org.eclipse.jetty.http.HttpGenerator;
38 import org.eclipse.jetty.http.HttpHeader;
39 import org.eclipse.jetty.http.HttpHeaderValue;
40 import org.eclipse.jetty.http.HttpStatus;
41 import org.eclipse.jetty.http.HttpVersion;
42 import org.eclipse.jetty.http.MetaData;
43 import org.eclipse.jetty.io.ByteBufferPool;
44 import org.eclipse.jetty.io.ChannelEndPoint;
45 import org.eclipse.jetty.io.EndPoint;
46 import org.eclipse.jetty.io.EofException;
47 import org.eclipse.jetty.server.HttpChannelState.Action;
48 import org.eclipse.jetty.server.handler.ContextHandler;
49 import org.eclipse.jetty.server.handler.ErrorHandler;
50 import org.eclipse.jetty.util.BufferUtil;
51 import org.eclipse.jetty.util.Callback;
52 import org.eclipse.jetty.util.SharedBlockingCallback.Blocker;
53 import org.eclipse.jetty.util.log.Log;
54 import org.eclipse.jetty.util.log.Logger;
55 import org.eclipse.jetty.util.thread.Scheduler;
56
57
58
59
60
61
62
63
64
65
66
67
68 public class HttpChannel implements Runnable, HttpOutput.Interceptor
69 {
70 private static final Logger LOG = Log.getLogger(HttpChannel.class);
71 private final AtomicBoolean _committed = new AtomicBoolean();
72 private final AtomicInteger _requests = new AtomicInteger();
73 private final Connector _connector;
74 private final HttpConfiguration _configuration;
75 private final EndPoint _endPoint;
76 private final HttpTransport _transport;
77 private final HttpChannelState _state;
78 private final Request _request;
79 private final Response _response;
80 private MetaData.Response _committedMetaData;
81 private RequestLog _requestLog;
82
83
84 private long _written;
85
86 public HttpChannel(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport)
87 {
88 _connector = connector;
89 _configuration = configuration;
90 _endPoint = endPoint;
91 _transport = transport;
92
93 _state = new HttpChannelState(this);
94 _request = new Request(this, newHttpInput(_state));
95 _response = new Response(this, newHttpOutput());
96 _requestLog=_connector==null?null:_connector.getServer().getRequestLog();
97 if (LOG.isDebugEnabled())
98 LOG.debug("new {} -> {},{},{}",this,_endPoint,_endPoint.getConnection(),_state);
99 }
100
101 protected HttpInput newHttpInput(HttpChannelState state)
102 {
103 return new HttpInput(state);
104 }
105
106 protected HttpOutput newHttpOutput()
107 {
108 return new HttpOutput(this);
109 }
110
111 public HttpChannelState getState()
112 {
113 return _state;
114 }
115
116 public long getBytesWritten()
117 {
118 return _written;
119 }
120
121
122
123
124 public int getRequests()
125 {
126 return _requests.get();
127 }
128
129 public Connector getConnector()
130 {
131 return _connector;
132 }
133
134 public HttpTransport getHttpTransport()
135 {
136 return _transport;
137 }
138
139 public RequestLog getRequestLog()
140 {
141 return _requestLog;
142 }
143
144 public void setRequestLog(RequestLog requestLog)
145 {
146 _requestLog = requestLog;
147 }
148
149 public void addRequestLog(RequestLog requestLog)
150 {
151 if (_requestLog==null)
152 _requestLog = requestLog;
153 else if (_requestLog instanceof RequestLogCollection)
154 ((RequestLogCollection) _requestLog).add(requestLog);
155 else
156 _requestLog = new RequestLogCollection(_requestLog, requestLog);
157 }
158
159 public MetaData.Response getCommittedMetaData()
160 {
161 return _committedMetaData;
162 }
163
164
165
166
167
168
169
170 public long getIdleTimeout()
171 {
172 return _endPoint.getIdleTimeout();
173 }
174
175
176
177
178
179
180
181 public void setIdleTimeout(long timeoutMs)
182 {
183 _endPoint.setIdleTimeout(timeoutMs);
184 }
185
186 public ByteBufferPool getByteBufferPool()
187 {
188 return _connector.getByteBufferPool();
189 }
190
191 public HttpConfiguration getHttpConfiguration()
192 {
193 return _configuration;
194 }
195
196 @Override
197 public boolean isOptimizedForDirectBuffers()
198 {
199 return getHttpTransport().isOptimizedForDirectBuffers();
200 }
201
202 public Server getServer()
203 {
204 return _connector.getServer();
205 }
206
207 public Request getRequest()
208 {
209 return _request;
210 }
211
212 public Response getResponse()
213 {
214 return _response;
215 }
216
217 public EndPoint getEndPoint()
218 {
219 return _endPoint;
220 }
221
222 public InetSocketAddress getLocalAddress()
223 {
224 return _endPoint.getLocalAddress();
225 }
226
227 public InetSocketAddress getRemoteAddress()
228 {
229 return _endPoint.getRemoteAddress();
230 }
231
232
233
234
235
236
237
238
239
240 public void continue100(int available) throws IOException
241 {
242 throw new UnsupportedOperationException();
243 }
244
245 public void recycle()
246 {
247 _committed.set(false);
248 _request.recycle();
249 _response.recycle();
250 _committedMetaData=null;
251 _requestLog=_connector==null?null:_connector.getServer().getRequestLog();
252 _written=0;
253 }
254
255 public void asyncReadFillInterested()
256 {
257 }
258
259 @Override
260 public void run()
261 {
262 handle();
263 }
264
265
266
267
268 public boolean handle()
269 {
270 if (LOG.isDebugEnabled())
271 LOG.debug("{} handle {} ", this,_request.getHttpURI());
272
273 HttpChannelState.Action action = _state.handling();
274
275
276
277
278
279 loop: while (!getServer().isStopped())
280 {
281 try
282 {
283 if (LOG.isDebugEnabled())
284 LOG.debug("{} action {}",this,action);
285
286 switch(action)
287 {
288 case TERMINATED:
289 case WAIT:
290 break loop;
291
292 case DISPATCH:
293 {
294 if (!_request.hasMetaData())
295 throw new IllegalStateException("state=" + _state);
296 _request.setHandled(false);
297 _response.getHttpOutput().reopen();
298
299 List<HttpConfiguration.Customizer> customizers = _configuration.getCustomizers();
300 if (!customizers.isEmpty())
301 {
302 for (HttpConfiguration.Customizer customizer : customizers)
303 customizer.customize(getConnector(), _configuration, _request);
304 }
305 try
306 {
307 _request.setDispatcherType(DispatcherType.REQUEST);
308 getServer().handle(this);
309 }
310 finally
311 {
312 _request.setDispatcherType(null);
313 }
314 break;
315 }
316
317 case ASYNC_DISPATCH:
318 {
319 _request.setHandled(false);
320 _response.getHttpOutput().reopen();
321
322 try
323 {
324 _request.setDispatcherType(DispatcherType.ASYNC);
325 getServer().handleAsync(this);
326 }
327 finally
328 {
329 _request.setDispatcherType(null);
330 }
331 break;
332 }
333
334 case ERROR_DISPATCH:
335 {
336 Throwable ex = _state.getAsyncContextEvent().getThrowable();
337
338
339 Integer loop_detect = (Integer)_request.getAttribute("org.eclipse.jetty.server.ERROR_DISPATCH");
340 if (loop_detect==null)
341 loop_detect=1;
342 else
343 loop_detect=loop_detect+1;
344 _request.setAttribute("org.eclipse.jetty.server.ERROR_DISPATCH",loop_detect);
345 if (loop_detect > getHttpConfiguration().getMaxErrorDispatches())
346 {
347 LOG.warn("ERROR_DISPATCH loop detected on {} {}",_request,ex);
348 try
349 {
350 _response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
351 }
352 finally
353 {
354 _state.errorComplete();
355 }
356 break loop;
357 }
358
359 _request.setHandled(false);
360 _response.resetBuffer();
361 _response.getHttpOutput().reopen();
362
363
364 String reason;
365 if (ex == null || ex instanceof TimeoutException)
366 {
367 reason = "Async Timeout";
368 }
369 else
370 {
371 reason = HttpStatus.Code.INTERNAL_SERVER_ERROR.getMessage();
372 _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ex);
373 }
374
375 _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 500);
376 _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, reason);
377 _request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, _request.getRequestURI());
378
379 _response.setStatusWithReason(HttpStatus.INTERNAL_SERVER_ERROR_500, reason);
380
381 ErrorHandler eh = ErrorHandler.getErrorHandler(getServer(), _state.getContextHandler());
382 if (eh instanceof ErrorHandler.ErrorPageMapper)
383 {
384 String error_page = ((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
385 if (error_page != null)
386 _state.getAsyncContextEvent().setDispatchPath(error_page);
387 }
388
389
390 try
391 {
392 _request.setDispatcherType(DispatcherType.ERROR);
393 getServer().handleAsync(this);
394 }
395 finally
396 {
397 _request.setDispatcherType(null);
398 }
399 break;
400 }
401
402 case READ_CALLBACK:
403 {
404 ContextHandler handler=_state.getContextHandler();
405 if (handler!=null)
406 handler.handle(_request,_request.getHttpInput());
407 else
408 _request.getHttpInput().run();
409 break;
410 }
411
412 case WRITE_CALLBACK:
413 {
414 ContextHandler handler=_state.getContextHandler();
415 if (handler!=null)
416 handler.handle(_request,_response.getHttpOutput());
417 else
418 _response.getHttpOutput().run();
419 break;
420 }
421
422 case ASYNC_ERROR:
423 {
424 _state.onError();
425 break;
426 }
427
428 case COMPLETE:
429 {
430
431
432
433 if (!_response.isCommitted() && !_request.isHandled())
434 _response.sendError(404);
435 else
436 _response.closeOutput();
437 _request.setHandled(true);
438
439
440 _state.onComplete();
441
442 onCompleted();
443
444 break loop;
445 }
446
447 default:
448 {
449 throw new IllegalStateException("state="+_state);
450 }
451 }
452 }
453 catch (EofException|QuietServletException|BadMessageException e)
454 {
455 if (LOG.isDebugEnabled())
456 LOG.debug(e);
457 handleException(e);
458 }
459 catch (Throwable e)
460 {
461 if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
462 {
463 LOG.ignore(e);
464 }
465 else
466 {
467 if (_connector.isStarted())
468 LOG.warn(String.valueOf(_request.getHttpURI()), e);
469 else
470 LOG.debug(String.valueOf(_request.getHttpURI()), e);
471 handleException(e);
472 }
473 }
474
475 action = _state.unhandle();
476 }
477
478 if (LOG.isDebugEnabled())
479 LOG.debug("{} handle exit, result {}", this, action);
480
481 boolean suspended=action==Action.WAIT;
482 return !suspended;
483 }
484
485
486
487
488
489
490
491
492
493
494 protected void handleException(Throwable x)
495 {
496 if (_state.isAsyncStarted())
497 {
498
499 Throwable root = _state.getAsyncContextEvent().getThrowable();
500 if (root==null)
501 {
502 _state.error(x);
503 }
504 else
505 {
506
507
508 root.addSuppressed(x);
509 LOG.warn("Error while handling async error: ", root);
510 abort(x);
511 _state.errorComplete();
512 }
513 }
514 else
515 {
516 try
517 {
518
519 _request.setHandled(true);
520 _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x);
521 _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, x.getClass());
522
523 if (isCommitted())
524 {
525 abort(x);
526 if (LOG.isDebugEnabled())
527 LOG.debug("Could not send response error 500, already committed", x);
528 }
529 else
530 {
531 _response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
532
533 if (x instanceof BadMessageException)
534 {
535 BadMessageException bme = (BadMessageException)x;
536 _response.sendError(bme.getCode(), bme.getReason());
537 }
538 else if (x instanceof UnavailableException)
539 {
540 if (((UnavailableException)x).isPermanent())
541 _response.sendError(HttpStatus.NOT_FOUND_404);
542 else
543 _response.sendError(HttpStatus.SERVICE_UNAVAILABLE_503);
544 }
545 else
546 _response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
547 }
548 }
549 catch (Throwable e)
550 {
551 abort(e);
552 if (LOG.isDebugEnabled())
553 LOG.debug("Could not commit response error 500", e);
554 }
555 }
556 }
557
558 public boolean isExpecting100Continue()
559 {
560 return false;
561 }
562
563 public boolean isExpecting102Processing()
564 {
565 return false;
566 }
567
568 @Override
569 public String toString()
570 {
571 return String.format("%s@%x{r=%s,c=%b,a=%s,uri=%s}",
572 getClass().getSimpleName(),
573 hashCode(),
574 _requests,
575 _committed.get(),
576 _state.getState(),
577 _request.getHttpURI());
578 }
579
580 public void onRequest(MetaData.Request request)
581 {
582 _requests.incrementAndGet();
583 _request.setTimeStamp(System.currentTimeMillis());
584 HttpFields fields = _response.getHttpFields();
585 if (_configuration.getSendDateHeader() && !fields.contains(HttpHeader.DATE))
586 fields.put(_connector.getServer().getDateField());
587
588 _request.setMetaData(request);
589 }
590
591 public boolean onContent(HttpInput.Content content)
592 {
593 if (LOG.isDebugEnabled())
594 LOG.debug("{} content {}", this, content);
595
596 return _request.getHttpInput().addContent(content);
597 }
598
599 public boolean onRequestComplete()
600 {
601 if (LOG.isDebugEnabled())
602 LOG.debug("{} onRequestComplete", this);
603 return _request.getHttpInput().eof();
604 }
605
606 public void onCompleted()
607 {
608 if (_requestLog!=null )
609 _requestLog.log(_request, _response);
610
611 _transport.onCompleted();
612 }
613
614 public boolean onEarlyEOF()
615 {
616 return _request.getHttpInput().earlyEOF();
617 }
618
619 public void onBadMessage(int status, String reason)
620 {
621 if (status < 400 || status > 599)
622 status = HttpStatus.BAD_REQUEST_400;
623
624 try
625 {
626 if (_state.handling()==Action.DISPATCH)
627 {
628 ByteBuffer content=null;
629 HttpFields fields=new HttpFields();
630
631 ErrorHandler handler=getServer().getBean(ErrorHandler.class);
632 if (handler!=null)
633 content=handler.badMessageError(status,reason,fields);
634
635 sendResponse(new MetaData.Response(HttpVersion.HTTP_1_1,status,reason,fields,BufferUtil.length(content)),content ,true);
636 }
637 }
638 catch (IOException e)
639 {
640 LOG.debug(e);
641 }
642 finally
643 {
644
645 if (_state.unhandle()==Action.COMPLETE)
646 _state.onComplete();
647 else
648 throw new IllegalStateException();
649 onCompleted();
650 }
651 }
652
653 protected boolean sendResponse(MetaData.Response info, ByteBuffer content, boolean complete, final Callback callback)
654 {
655 boolean committing = _committed.compareAndSet(false, true);
656 if (committing)
657 {
658
659 if (info==null)
660 info = _response.newResponseMetaData();
661 commit(info);
662
663
664 final int status=info.getStatus();
665 final Callback committed = (status<200&&status>=100)?new Commit100Callback(callback):new CommitCallback(callback);
666
667
668 _transport.send(info, _request.isHead(), content, complete, committed);
669 }
670 else if (info==null)
671 {
672
673 _transport.send(null,_request.isHead(), content, complete, callback);
674 }
675 else
676 {
677 callback.failed(new IllegalStateException("committed"));
678 }
679 return committing;
680 }
681
682 protected boolean sendResponse(MetaData.Response info, ByteBuffer content, boolean complete) throws IOException
683 {
684 try(Blocker blocker = _response.getHttpOutput().acquireWriteBlockingCallback())
685 {
686 boolean committing = sendResponse(info,content,complete,blocker);
687 blocker.block();
688 return committing;
689 }
690 catch (Throwable failure)
691 {
692 if (LOG.isDebugEnabled())
693 LOG.debug(failure);
694 abort(failure);
695 throw failure;
696 }
697 }
698
699 protected void commit (MetaData.Response info)
700 {
701 _committedMetaData=info;
702 if (LOG.isDebugEnabled())
703 LOG.debug("Commit {} to {}",info,this);
704 }
705
706 public boolean isCommitted()
707 {
708 return _committed.get();
709 }
710
711
712
713
714
715
716
717
718 @Override
719 public void write(ByteBuffer content, boolean complete, Callback callback)
720 {
721 _written+=BufferUtil.length(content);
722 sendResponse(null,content,complete,callback);
723 }
724
725 public HttpOutput.Interceptor getNextInterceptor()
726 {
727 return null;
728 }
729
730 protected void execute(Runnable task)
731 {
732 _connector.getExecutor().execute(task);
733 }
734
735 public Scheduler getScheduler()
736 {
737 return _connector.getScheduler();
738 }
739
740
741
742
743 public boolean useDirectBuffers()
744 {
745 return getEndPoint() instanceof ChannelEndPoint;
746 }
747
748
749
750
751
752
753
754
755
756 public void abort(Throwable failure)
757 {
758 _transport.abort(failure);
759 }
760
761 private class CommitCallback implements Callback
762 {
763 private final Callback _callback;
764
765 private CommitCallback(Callback callback)
766 {
767 _callback = callback;
768 }
769
770 @Override
771 public boolean isNonBlocking()
772 {
773 return _callback.isNonBlocking();
774 }
775
776 @Override
777 public void succeeded()
778 {
779 _callback.succeeded();
780 }
781
782 @Override
783 public void failed(final Throwable x)
784 {
785 if (LOG.isDebugEnabled())
786 LOG.debug("Commit failed", x);
787
788 if (x instanceof EofException || x instanceof ClosedChannelException)
789 {
790 _callback.failed(x);
791 _response.getHttpOutput().closed();
792 }
793 else
794 {
795 _transport.send(HttpGenerator.RESPONSE_500_INFO, false, null, true, new Callback()
796 {
797 @Override
798 public void succeeded()
799 {
800 _callback.failed(x);
801 _response.getHttpOutput().closed();
802 }
803
804 @Override
805 public void failed(Throwable th)
806 {
807 _callback.failed(x);
808 _response.getHttpOutput().closed();
809 }
810 });
811 }
812 }
813 }
814
815 private class Commit100Callback extends CommitCallback
816 {
817 private Commit100Callback(Callback callback)
818 {
819 super(callback);
820 }
821
822 @Override
823 public void succeeded()
824 {
825 if (_committed.compareAndSet(true, false))
826 super.succeeded();
827 else
828 super.failed(new IllegalStateException());
829 }
830
831 }
832
833
834 }