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