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