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