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