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