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