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