1// 2// ======================================================================== 3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4// ------------------------------------------------------------------------ 5// All rights reserved. This program and the accompanying materials 6// are made available under the terms of the Eclipse Public License v1.0 7// and Apache License v2.0 which accompanies this distribution. 8// 9// The Eclipse Public License is available at 10// http://www.eclipse.org/legal/epl-v10.html 11// 12// The Apache License v2.0 is available at 13// http://www.opensource.org/licenses/apache2.0.php 14// 15// You may elect to redistribute this code under either of these licenses. 16// ======================================================================== 17// 18 19package org.eclipse.jetty.server; 20 21import java.io.IOException; 22import java.io.InputStream; 23import java.io.PrintWriter; 24 25import javax.servlet.DispatcherType; 26import javax.servlet.RequestDispatcher; 27import javax.servlet.ServletInputStream; 28import javax.servlet.ServletOutputStream; 29import javax.servlet.http.HttpServletRequest; 30import javax.servlet.http.HttpServletResponse; 31 32import org.eclipse.jetty.continuation.ContinuationThrowable; 33import org.eclipse.jetty.http.EncodedHttpURI; 34import org.eclipse.jetty.http.Generator; 35import org.eclipse.jetty.http.HttpBuffers; 36import org.eclipse.jetty.http.HttpContent; 37import org.eclipse.jetty.http.HttpException; 38import org.eclipse.jetty.http.HttpFields; 39import org.eclipse.jetty.http.HttpGenerator; 40import org.eclipse.jetty.http.HttpHeaderValues; 41import org.eclipse.jetty.http.HttpHeaders; 42import org.eclipse.jetty.http.HttpMethods; 43import org.eclipse.jetty.http.HttpParser; 44import org.eclipse.jetty.http.HttpStatus; 45import org.eclipse.jetty.http.HttpURI; 46import org.eclipse.jetty.http.HttpVersions; 47import org.eclipse.jetty.http.MimeTypes; 48import org.eclipse.jetty.http.Parser; 49import org.eclipse.jetty.http.PathMap; 50import org.eclipse.jetty.io.AbstractConnection; 51import org.eclipse.jetty.io.Buffer; 52import org.eclipse.jetty.io.BufferCache.CachedBuffer; 53import org.eclipse.jetty.io.Buffers; 54import org.eclipse.jetty.io.Connection; 55import org.eclipse.jetty.io.EndPoint; 56import org.eclipse.jetty.io.EofException; 57import org.eclipse.jetty.io.RuntimeIOException; 58import org.eclipse.jetty.io.UncheckedPrintWriter; 59import org.eclipse.jetty.server.handler.ErrorHandler; 60import org.eclipse.jetty.server.nio.NIOConnector; 61import org.eclipse.jetty.server.ssl.SslConnector; 62import org.eclipse.jetty.util.QuotedStringTokenizer; 63import org.eclipse.jetty.util.StringUtil; 64import org.eclipse.jetty.util.URIUtil; 65import org.eclipse.jetty.util.log.Log; 66import org.eclipse.jetty.util.log.Logger; 67import org.eclipse.jetty.util.resource.Resource; 68 69/** 70 * <p>A HttpConnection represents the connection of a HTTP client to the server 71 * and is created by an instance of a {@link Connector}. It's prime function is 72 * to associate {@link Request} and {@link Response} instances with a {@link EndPoint}. 73 * </p> 74 * <p> 75 * A connection is also the prime mechanism used by jetty to recycle objects without 76 * pooling. The {@link Request}, {@link Response}, {@link HttpParser}, {@link HttpGenerator} 77 * and {@link HttpFields} instances are all recycled for the duraction of 78 * a connection. Where appropriate, allocated buffers are also kept associated 79 * with the connection via the parser and/or generator. 80 * </p> 81 * <p> 82 * The connection state is held by 3 separate state machines: The request state, the 83 * response state and the continuation state. All three state machines must be driven 84 * to completion for every request, and all three can complete in any order. 85 * </p> 86 * <p> 87 * The HttpConnection support protocol upgrade. If on completion of a request, the 88 * response code is 101 (switch protocols), then the org.eclipse.jetty.io.Connection 89 * request attribute is checked to see if there is a new Connection instance. If so, 90 * the new connection is returned from {@link #handle()} and is used for future 91 * handling of the underlying connection. Note that for switching protocols that 92 * don't use 101 responses (eg CONNECT), the response should be sent and then the 93 * status code changed to 101 before returning from the handler. Implementors 94 * of new Connection types should be careful to extract any buffered data from 95 * (HttpParser)http.getParser()).getHeaderBuffer() and 96 * (HttpParser)http.getParser()).getBodyBuffer() to initialise their new connection. 97 * </p> 98 * 99 */ 100public abstract class AbstractHttpConnection extends AbstractConnection 101{ 102 private static final Logger LOG = Log.getLogger(AbstractHttpConnection.class); 103 104 private static final int UNKNOWN = -2; 105 private static final ThreadLocal<AbstractHttpConnection> __currentConnection = new ThreadLocal<AbstractHttpConnection>(); 106 107 private int _requests; 108 109 protected final Connector _connector; 110 protected final Server _server; 111 protected final HttpURI _uri; 112 113 protected final Parser _parser; 114 protected final HttpFields _requestFields; 115 protected final Request _request; 116 protected volatile ServletInputStream _in; 117 118 protected final Generator _generator; 119 protected final HttpFields _responseFields; 120 protected final Response _response; 121 protected volatile Output _out; 122 protected volatile OutputWriter _writer; 123 protected volatile PrintWriter _printWriter; 124 125 int _include; 126 127 private Object _associatedObject; // associated object 128 129 private int _version = UNKNOWN; 130 131 private String _charset; 132 private boolean _expect = false; 133 private boolean _expect100Continue = false; 134 private boolean _expect102Processing = false; 135 private boolean _head = false; 136 private boolean _host = false; 137 private boolean _delayedHandling=false; 138 private boolean _earlyEOF = false; 139 140 /* ------------------------------------------------------------ */ 141 public static AbstractHttpConnection getCurrentConnection() 142 { 143 return __currentConnection.get(); 144 } 145 146 /* ------------------------------------------------------------ */ 147 protected static void setCurrentConnection(AbstractHttpConnection connection) 148 { 149 __currentConnection.set(connection); 150 } 151 152 /* ------------------------------------------------------------ */ 153 public AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server) 154 { 155 super(endpoint); 156 _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET); 157 _connector = connector; 158 HttpBuffers ab = (HttpBuffers)_connector; 159 _parser = newHttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler()); 160 _requestFields = new HttpFields(); 161 _responseFields = new HttpFields(); 162 _request = new Request(this); 163 _response = new Response(this); 164 _generator = newHttpGenerator(ab.getResponseBuffers(), endpoint); 165 _generator.setSendServerVersion(server.getSendServerVersion()); 166 _server = server; 167 } 168 169 /* ------------------------------------------------------------ */ 170 protected AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server, 171 Parser parser, Generator generator, Request request) 172 { 173 super(endpoint); 174 175 _uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET); 176 _connector = connector; 177 _parser = parser; 178 _requestFields = new HttpFields(); 179 _responseFields = new HttpFields(); 180 _request = request; 181 _response = new Response(this); 182 _generator = generator; 183 _generator.setSendServerVersion(server.getSendServerVersion()); 184 _server = server; 185 } 186 187 protected HttpParser newHttpParser(Buffers requestBuffers, EndPoint endpoint, HttpParser.EventHandler requestHandler) 188 { 189 return new HttpParser(requestBuffers, endpoint, requestHandler); 190 } 191 192 protected HttpGenerator newHttpGenerator(Buffers responseBuffers, EndPoint endPoint) 193 { 194 return new HttpGenerator(responseBuffers, endPoint); 195 } 196 197 /* ------------------------------------------------------------ */ 198 /** 199 * @return the parser used by this connection 200 */ 201 public Parser getParser() 202 { 203 return _parser; 204 } 205 206 /* ------------------------------------------------------------ */ 207 /** 208 * @return the number of requests handled by this connection 209 */ 210 public int getRequests() 211 { 212 return _requests; 213 } 214 215 /* ------------------------------------------------------------ */ 216 public Server getServer() 217 { 218 return _server; 219 } 220 221 /* ------------------------------------------------------------ */ 222 /** 223 * @return Returns the associatedObject. 224 */ 225 public Object getAssociatedObject() 226 { 227 return _associatedObject; 228 } 229 230 /* ------------------------------------------------------------ */ 231 /** 232 * @param associatedObject The associatedObject to set. 233 */ 234 public void setAssociatedObject(Object associatedObject) 235 { 236 _associatedObject = associatedObject; 237 } 238 239 /* ------------------------------------------------------------ */ 240 /** 241 * @return Returns the connector. 242 */ 243 public Connector getConnector() 244 { 245 return _connector; 246 } 247 248 /* ------------------------------------------------------------ */ 249 /** 250 * @return Returns the requestFields. 251 */ 252 public HttpFields getRequestFields() 253 { 254 return _requestFields; 255 } 256 257 /* ------------------------------------------------------------ */ 258 /** 259 * @return Returns the responseFields. 260 */ 261 public HttpFields getResponseFields() 262 { 263 return _responseFields; 264 } 265 266 /* ------------------------------------------------------------ */ 267 /** 268 * Find out if the request supports CONFIDENTIAL security. 269 * @param request the incoming HTTP request 270 * @return the result of calling {@link Connector#isConfidential(Request)}, or false 271 * if there is no connector 272 */ 273 public boolean isConfidential(Request request) 274 { 275 return _connector != null && _connector.isConfidential(request); 276 } 277 278 /* ------------------------------------------------------------ */ 279 /** 280 * Find out if the request supports INTEGRAL security. 281 * @param request the incoming HTTP request 282 * @return the result of calling {@link Connector#isIntegral(Request)}, or false 283 * if there is no connector 284 */ 285 public boolean isIntegral(Request request) 286 { 287 return _connector != null && _connector.isIntegral(request); 288 } 289 290 /* ------------------------------------------------------------ */ 291 /** 292 * @return <code>false</code> (this method is not yet implemented) 293 */ 294 public boolean getResolveNames() 295 { 296 return _connector.getResolveNames(); 297 } 298 299 /* ------------------------------------------------------------ */ 300 /** 301 * @return Returns the request. 302 */ 303 public Request getRequest() 304 { 305 return _request; 306 } 307 308 /* ------------------------------------------------------------ */ 309 /** 310 * @return Returns the response. 311 */ 312 public Response getResponse() 313 { 314 return _response; 315 } 316 317 /* ------------------------------------------------------------ */ 318 /** 319 * Get the inputStream from the connection. 320 * <p> 321 * If the associated response has the Expect header set to 100 Continue, 322 * then accessing the input stream indicates that the handler/servlet 323 * is ready for the request body and thus a 100 Continue response is sent. 324 * 325 * @return The input stream for this connection. 326 * The stream will be created if it does not already exist. 327 * @throws IOException if the input stream cannot be retrieved 328 */ 329 public ServletInputStream getInputStream() throws IOException 330 { 331 // If the client is expecting 100 CONTINUE, then send it now. 332 if (_expect100Continue) 333 { 334 // is content missing? 335 if (((HttpParser)_parser).getHeaderBuffer()==null || ((HttpParser)_parser).getHeaderBuffer().length()<2) 336 { 337 if (_generator.isCommitted()) 338 throw new IllegalStateException("Committed before 100 Continues"); 339 340 ((HttpGenerator)_generator).send1xx(HttpStatus.CONTINUE_100); 341 } 342 _expect100Continue=false; 343 } 344 345 if (_in == null) 346 _in = new HttpInput(AbstractHttpConnection.this); 347 return _in; 348 } 349 350 /* ------------------------------------------------------------ */ 351 /** 352 * @return The output stream for this connection. The stream will be created if it does not already exist. 353 */ 354 public ServletOutputStream getOutputStream() 355 { 356 if (_out == null) 357 _out = new Output(); 358 return _out; 359 } 360 361 /* ------------------------------------------------------------ */ 362 /** 363 * @param encoding the PrintWriter encoding 364 * @return A {@link PrintWriter} wrapping the {@link #getOutputStream output stream}. The writer is created if it 365 * does not already exist. 366 */ 367 public PrintWriter getPrintWriter(String encoding) 368 { 369 getOutputStream(); 370 if (_writer==null) 371 { 372 _writer=new OutputWriter(); 373 if (_server.isUncheckedPrintWriter()) 374 _printWriter=new UncheckedPrintWriter(_writer); 375 else 376 _printWriter = new PrintWriter(_writer) 377 { 378 public void close() 379 { 380 synchronized (lock) 381 { 382 try 383 { 384 out.close(); 385 } 386 catch (IOException e) 387 { 388 setError(); 389 } 390 } 391 } 392 }; 393 } 394 _writer.setCharacterEncoding(encoding); 395 return _printWriter; 396 } 397 398 /* ------------------------------------------------------------ */ 399 public boolean isResponseCommitted() 400 { 401 return _generator.isCommitted(); 402 } 403 404 /* ------------------------------------------------------------ */ 405 public boolean isEarlyEOF() 406 { 407 return _earlyEOF; 408 } 409 410 /* ------------------------------------------------------------ */ 411 public void reset() 412 { 413 _parser.reset(); 414 _parser.returnBuffers(); // TODO maybe only on unhandle 415 _requestFields.clear(); 416 _request.recycle(); 417 _generator.reset(); 418 _generator.returnBuffers();// TODO maybe only on unhandle 419 _responseFields.clear(); 420 _response.recycle(); 421 _uri.clear(); 422 _writer=null; 423 _earlyEOF = false; 424 } 425 426 /* ------------------------------------------------------------ */ 427 protected void handleRequest() throws IOException 428 { 429 boolean error = false; 430 431 String threadName=null; 432 Throwable async_exception=null; 433 try 434 { 435 if (LOG.isDebugEnabled()) 436 { 437 threadName=Thread.currentThread().getName(); 438 Thread.currentThread().setName(threadName+" - "+_uri); 439 } 440 441 442 // Loop here to handle async request redispatches. 443 // The loop is controlled by the call to async.unhandle in the 444 // finally block below. If call is from a non-blocking connector, 445 // then the unhandle will return false only if an async dispatch has 446 // already happened when unhandle is called. For a blocking connector, 447 // the wait for the asynchronous dispatch or timeout actually happens 448 // within the call to unhandle(). 449 450 final Server server=_server; 451 boolean was_continuation=_request._async.isContinuation(); 452 boolean handling=_request._async.handling() && server!=null && server.isRunning(); 453 while (handling) 454 { 455 _request.setHandled(false); 456 457 String info=null; 458 try 459 { 460 _uri.getPort(); 461 String path = null; 462 463 try 464 { 465 path = _uri.getDecodedPath(); 466 } 467 catch (Exception e) 468 { 469 LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1"); 470 LOG.ignore(e); 471 path = _uri.getDecodedPath(StringUtil.__ISO_8859_1); 472 } 473 474 info=URIUtil.canonicalPath(path); 475 if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT)) 476 { 477 if (path==null && _uri.getScheme()!=null && _uri.getHost()!=null) 478 { 479 info="/"; 480 _request.setRequestURI(""); 481 } 482 else 483 throw new HttpException(400); 484 } 485 _request.setPathInfo(info); 486 487 if (_out!=null) 488 _out.reopen(); 489 490 if (_request._async.isInitial()) 491 { 492 _request.setDispatcherType(DispatcherType.REQUEST); 493 _connector.customize(_endp, _request); 494 server.handle(this); 495 } 496 else 497 { 498 if (_request._async.isExpired()&&!was_continuation) 499 { 500 async_exception = (Throwable)_request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); 501 _response.setStatus(500,async_exception==null?"Async Timeout":"Async Exception"); 502 _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500)); 503 _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, _response.getReason()); 504 _request.setDispatcherType(DispatcherType.ERROR); 505 506 ErrorHandler eh = _request._async.getContextHandler().getErrorHandler(); 507 if (eh instanceof ErrorHandler.ErrorPageMapper) 508 { 509 String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_request._async.getRequest()); 510 if (error_page!=null) 511 { 512 AsyncContinuation.AsyncEventState state = _request._async.getAsyncEventState(); 513 state.setPath(error_page); 514 } 515 } 516 } 517 else 518 _request.setDispatcherType(DispatcherType.ASYNC); 519 server.handleAsync(this); 520 } 521 } 522 catch (ContinuationThrowable e) 523 { 524 LOG.ignore(e); 525 } 526 catch (EofException e) 527 { 528 async_exception=e; 529 LOG.debug(e); 530 error=true; 531 _request.setHandled(true); 532 if (!_response.isCommitted()) 533 _generator.sendError(500, null, null, true); 534 } 535 catch (RuntimeIOException e) 536 { 537 async_exception=e; 538 LOG.debug(e); 539 error=true; 540 _request.setHandled(true); 541 } 542 catch (HttpException e) 543 { 544 LOG.debug(e); 545 error=true; 546 _request.setHandled(true); 547 _response.sendError(e.getStatus(), e.getReason()); 548 } 549 catch (Throwable e) 550 { 551 async_exception=e; 552 LOG.warn(String.valueOf(_uri),e); 553 error=true; 554 _request.setHandled(true); 555 _generator.sendError(info==null?400:500, null, null, true); 556 557 } 558 finally 559 { 560 // Complete async requests 561 if (error && _request.isAsyncStarted()) 562 _request.getAsyncContinuation().errorComplete(); 563 564 was_continuation=_request._async.isContinuation(); 565 handling = !_request._async.unhandle() && server.isRunning() && _server!=null; 566 } 567 } 568 } 569 finally 570 { 571 if (threadName!=null) 572 Thread.currentThread().setName(threadName); 573 574 if (_request._async.isUncompleted()) 575 { 576 577 _request._async.doComplete(async_exception); 578 579 if (_expect100Continue) 580 { 581 LOG.debug("100 continues not sent"); 582 // We didn't send 100 continues, but the latest interpretation 583 // of the spec (see httpbis) is that the client will either 584 // send the body anyway, or close. So we no longer need to 585 // do anything special here other than make the connection not persistent 586 _expect100Continue = false; 587 if (!_response.isCommitted()) 588 _generator.setPersistent(false); 589 } 590 591 if(_endp.isOpen()) 592 { 593 if (error) 594 { 595 _endp.shutdownOutput(); 596 _generator.setPersistent(false); 597 if (!_generator.isComplete()) 598 _response.complete(); 599 } 600 else 601 { 602 if (!_response.isCommitted() && !_request.isHandled()) 603 _response.sendError(HttpServletResponse.SC_NOT_FOUND); 604 _response.complete(); 605 if (_generator.isPersistent()) 606 _connector.persist(_endp); 607 } 608 } 609 else 610 { 611 _response.complete(); 612 } 613 614 _request.setHandled(true); 615 } 616 } 617 } 618 619 /* ------------------------------------------------------------ */ 620 public abstract Connection handle() throws IOException; 621 622 /* ------------------------------------------------------------ */ 623 public void commitResponse(boolean last) throws IOException 624 { 625 if (!_generator.isCommitted()) 626 { 627 _generator.setResponse(_response.getStatus(), _response.getReason()); 628 try 629 { 630 // If the client was expecting 100 continues, but we sent something 631 // else, then we need to close the connection 632 if (_expect100Continue && _response.getStatus()!=100) 633 _generator.setPersistent(false); 634 _generator.completeHeader(_responseFields, last); 635 } 636 catch(RuntimeException e) 637 { 638 LOG.warn("header full: " + e); 639 640 _response.reset(); 641 _generator.reset(); 642 _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null); 643 _generator.completeHeader(_responseFields,Generator.LAST); 644 _generator.complete(); 645 throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500); 646 } 647 648 } 649 if (last) 650 _generator.complete(); 651 } 652 653 /* ------------------------------------------------------------ */ 654 public void completeResponse() throws IOException 655 { 656 if (!_generator.isCommitted()) 657 { 658 _generator.setResponse(_response.getStatus(), _response.getReason()); 659 try 660 { 661 _generator.completeHeader(_responseFields, Generator.LAST); 662 } 663 catch(RuntimeException e) 664 { 665 LOG.warn("header full: "+e); 666 LOG.debug(e); 667 668 _response.reset(); 669 _generator.reset(); 670 _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null); 671 _generator.completeHeader(_responseFields,Generator.LAST); 672 _generator.complete(); 673 throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500); 674 } 675 } 676 677 _generator.complete(); 678 } 679 680 /* ------------------------------------------------------------ */ 681 public void flushResponse() throws IOException 682 { 683 try 684 { 685 commitResponse(Generator.MORE); 686 _generator.flushBuffer(); 687 } 688 catch(IOException e) 689 { 690 throw (e instanceof EofException) ? e:new EofException(e); 691 } 692 } 693 694 /* ------------------------------------------------------------ */ 695 public Generator getGenerator() 696 { 697 return _generator; 698 } 699 700 /* ------------------------------------------------------------ */ 701 public boolean isIncluding() 702 { 703 return _include>0; 704 } 705 706 /* ------------------------------------------------------------ */ 707 public void include() 708 { 709 _include++; 710 } 711 712 /* ------------------------------------------------------------ */ 713 public void included() 714 { 715 _include--; 716 if (_out!=null) 717 _out.reopen(); 718 } 719 720 /* ------------------------------------------------------------ */ 721 public boolean isIdle() 722 { 723 return _generator.isIdle() && (_parser.isIdle() || _delayedHandling); 724 } 725 726 /* ------------------------------------------------------------ */ 727 /** 728 * @see org.eclipse.jetty.io.Connection#isSuspended() 729 */ 730 public boolean isSuspended() 731 { 732 return _request.getAsyncContinuation().isSuspended(); 733 } 734 735 /* ------------------------------------------------------------ */ 736 public void onClose() 737 { 738 LOG.debug("closed {}",this); 739 } 740 741 /* ------------------------------------------------------------ */ 742 public boolean isExpecting100Continues() 743 { 744 return _expect100Continue; 745 } 746 747 /* ------------------------------------------------------------ */ 748 public boolean isExpecting102Processing() 749 { 750 return _expect102Processing; 751 } 752 753 /* ------------------------------------------------------------ */ 754 public int getMaxIdleTime() 755 { 756 if (_connector.isLowResources() && _endp.getMaxIdleTime()==_connector.getMaxIdleTime()) 757 return _connector.getLowResourceMaxIdleTime(); 758 if (_endp.getMaxIdleTime()>0) 759 return _endp.getMaxIdleTime(); 760 return _connector.getMaxIdleTime(); 761 } 762 763 /* ------------------------------------------------------------ */ 764 public String toString() 765 { 766 return String.format("%s,g=%s,p=%s,r=%d", 767 super.toString(), 768 _generator, 769 _parser, 770 _requests); 771 } 772 773 /* ------------------------------------------------------------ */ 774 protected void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException 775 { 776 uri=uri.asImmutableBuffer(); 777 778 _host = false; 779 _expect = false; 780 _expect100Continue=false; 781 _expect102Processing=false; 782 _delayedHandling=false; 783 _charset=null; 784 785 if(_request.getTimeStamp()==0) 786 _request.setTimeStamp(System.currentTimeMillis()); 787 _request.setMethod(method.toString()); 788 789 try 790 { 791 _head=false; 792 switch (HttpMethods.CACHE.getOrdinal(method)) 793 { 794 case HttpMethods.CONNECT_ORDINAL: 795 _uri.parseConnect(uri.array(), uri.getIndex(), uri.length()); 796 break; 797 798 case HttpMethods.HEAD_ORDINAL: 799 _head=true; 800 _uri.parse(uri.array(), uri.getIndex(), uri.length()); 801 break; 802 803 default: 804 _uri.parse(uri.array(), uri.getIndex(), uri.length()); 805 } 806 807 _request.setUri(_uri); 808 809 if (version==null) 810 { 811 _request.setProtocol(HttpVersions.HTTP_0_9); 812 _version=HttpVersions.HTTP_0_9_ORDINAL; 813 } 814 else 815 { 816 version= HttpVersions.CACHE.get(version); 817 if (version==null) 818 throw new HttpException(HttpStatus.BAD_REQUEST_400,null); 819 _version = HttpVersions.CACHE.getOrdinal(version); 820 if (_version <= 0) _version = HttpVersions.HTTP_1_0_ORDINAL; 821 _request.setProtocol(version.toString()); 822 } 823 } 824 catch (Exception e) 825 { 826 LOG.debug(e); 827 if (e instanceof HttpException) 828 throw (HttpException)e; 829 throw new HttpException(HttpStatus.BAD_REQUEST_400,null,e); 830 } 831 } 832 833 /* ------------------------------------------------------------ */ 834 protected void parsedHeader(Buffer name, Buffer value) throws IOException 835 { 836 int ho = HttpHeaders.CACHE.getOrdinal(name); 837 switch (ho) 838 { 839 case HttpHeaders.HOST_ORDINAL: 840 // TODO check if host matched a host in the URI. 841 _host = true; 842 break; 843 844 case HttpHeaders.EXPECT_ORDINAL: 845 if (_version>=HttpVersions.HTTP_1_1_ORDINAL) 846 { 847 value = HttpHeaderValues.CACHE.lookup(value); 848 switch(HttpHeaderValues.CACHE.getOrdinal(value)) 849 { 850 case HttpHeaderValues.CONTINUE_ORDINAL: 851 _expect100Continue=_generator instanceof HttpGenerator; 852 break; 853 854 case HttpHeaderValues.PROCESSING_ORDINAL: 855 _expect102Processing=_generator instanceof HttpGenerator; 856 break; 857 858 default: 859 String[] values = value.toString().split(","); 860 for (int i=0;values!=null && i<values.length;i++) 861 { 862 CachedBuffer cb=HttpHeaderValues.CACHE.get(values[i].trim()); 863 if (cb==null) 864 _expect=true; 865 else 866 { 867 switch(cb.getOrdinal()) 868 { 869 case HttpHeaderValues.CONTINUE_ORDINAL: 870 _expect100Continue=_generator instanceof HttpGenerator; 871 break; 872 case HttpHeaderValues.PROCESSING_ORDINAL: 873 _expect102Processing=_generator instanceof HttpGenerator; 874 break; 875 default: 876 _expect=true; 877 } 878 } 879 } 880 } 881 } 882 break; 883 884 case HttpHeaders.ACCEPT_ENCODING_ORDINAL: 885 case HttpHeaders.USER_AGENT_ORDINAL: 886 value = HttpHeaderValues.CACHE.lookup(value); 887 break; 888 889 case HttpHeaders.CONTENT_TYPE_ORDINAL: 890 value = MimeTypes.CACHE.lookup(value); 891 _charset=MimeTypes.getCharsetFromContentType(value); 892 break; 893 } 894 895 _requestFields.add(name, value); 896 } 897 898 /* ------------------------------------------------------------ */ 899 protected void headerComplete() throws IOException 900 { 901 // Handle idle race 902 if (_endp.isOutputShutdown()) 903 { 904 _endp.close(); 905 return; 906 } 907 908 _requests++; 909 _generator.setVersion(_version); 910 switch (_version) 911 { 912 case HttpVersions.HTTP_0_9_ORDINAL: 913 break; 914 case HttpVersions.HTTP_1_0_ORDINAL: 915 _generator.setHead(_head); 916 if (_parser.isPersistent()) 917 { 918 _responseFields.add(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.KEEP_ALIVE_BUFFER); 919 _generator.setPersistent(true); 920 } 921 else if (HttpMethods.CONNECT.equals(_request.getMethod())) 922 { 923 _generator.setPersistent(true); 924 _parser.setPersistent(true); 925 } 926 927 if (_server.getSendDateHeader()) 928 _generator.setDate(_request.getTimeStampBuffer()); 929 break; 930 931 case HttpVersions.HTTP_1_1_ORDINAL: 932 _generator.setHead(_head); 933 934 if (!_parser.isPersistent()) 935 { 936 _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER); 937 _generator.setPersistent(false); 938 } 939 if (_server.getSendDateHeader()) 940 _generator.setDate(_request.getTimeStampBuffer()); 941 942 if (!_host) 943 { 944 LOG.debug("!host {}",this); 945 _generator.setResponse(HttpStatus.BAD_REQUEST_400, null); 946 _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER); 947 _generator.completeHeader(_responseFields, true); 948 _generator.complete(); 949 return; 950 } 951 952 if (_expect) 953 { 954 LOG.debug("!expectation {}",this); 955 _generator.setResponse(HttpStatus.EXPECTATION_FAILED_417, null); 956 _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER); 957 _generator.completeHeader(_responseFields, true); 958 _generator.complete(); 959 return; 960 } 961 962 break; 963 default: 964 } 965 966 if(_charset!=null) 967 _request.setCharacterEncodingUnchecked(_charset); 968 969 // Either handle now or wait for first content 970 if ((((HttpParser)_parser).getContentLength()<=0 && !((HttpParser)_parser).isChunking())||_expect100Continue) 971 handleRequest(); 972 else 973 _delayedHandling=true; 974 } 975 976 /* ------------------------------------------------------------ */ 977 protected void content(Buffer buffer) throws IOException 978 { 979 if (_delayedHandling) 980 { 981 _delayedHandling=false; 982 handleRequest(); 983 } 984 } 985 986 /* ------------------------------------------------------------ */ 987 public void messageComplete(long contentLength) throws IOException 988 { 989 if (_delayedHandling) 990 { 991 _delayedHandling=false; 992 handleRequest(); 993 } 994 } 995 996 /* ------------------------------------------------------------ */ 997 public void earlyEOF() 998 { 999 _earlyEOF = true; 1000 } 1001 1002 /* ------------------------------------------------------------ */ 1003 /* ------------------------------------------------------------ */ 1004 /* ------------------------------------------------------------ */ 1005 private class RequestHandler extends HttpParser.EventHandler 1006 { 1007 /* 1008 * 1009 * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startRequest(org.eclipse.io.Buffer, 1010 * org.eclipse.io.Buffer, org.eclipse.io.Buffer) 1011 */ 1012 @Override 1013 public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException 1014 { 1015 AbstractHttpConnection.this.startRequest(method, uri, version); 1016 } 1017 1018 /* 1019 * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#parsedHeaderValue(org.eclipse.io.Buffer) 1020 */ 1021 @Override 1022 public void parsedHeader(Buffer name, Buffer value) throws IOException 1023 { 1024 AbstractHttpConnection.this.parsedHeader(name, value); 1025 } 1026 1027 /* 1028 * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#headerComplete() 1029 */ 1030 @Override 1031 public void headerComplete() throws IOException 1032 { 1033 AbstractHttpConnection.this.headerComplete(); 1034 } 1035 1036 /* ------------------------------------------------------------ */ 1037 /* 1038 * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#content(int, org.eclipse.io.Buffer) 1039 */ 1040 @Override 1041 public void content(Buffer ref) throws IOException 1042 { 1043 AbstractHttpConnection.this.content(ref); 1044 } 1045 1046 /* ------------------------------------------------------------ */ 1047 /* 1048 * (non-Javadoc) 1049 * 1050 * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#messageComplete(int) 1051 */ 1052 @Override 1053 public void messageComplete(long contentLength) throws IOException 1054 { 1055 AbstractHttpConnection.this.messageComplete(contentLength); 1056 } 1057 1058 /* ------------------------------------------------------------ */ 1059 /* 1060 * (non-Javadoc) 1061 * 1062 * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startResponse(org.eclipse.io.Buffer, int, 1063 * org.eclipse.io.Buffer) 1064 */ 1065 @Override 1066 public void startResponse(Buffer version, int status, Buffer reason) 1067 { 1068 if (LOG.isDebugEnabled()) 1069 LOG.debug("Bad request!: "+version+" "+status+" "+reason); 1070 } 1071 1072 /* ------------------------------------------------------------ */ 1073 /* 1074 * (non-Javadoc) 1075 * 1076 * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#earlyEOF() 1077 */ 1078 @Override 1079 public void earlyEOF() 1080 { 1081 AbstractHttpConnection.this.earlyEOF(); 1082 } 1083 } 1084 1085 /* ------------------------------------------------------------ */ 1086 /* ------------------------------------------------------------ */ 1087 /* ------------------------------------------------------------ */ 1088 public class Output extends HttpOutput 1089 { 1090 Output() 1091 { 1092 super(AbstractHttpConnection.this); 1093 } 1094 1095 /* ------------------------------------------------------------ */ 1096 /* 1097 * @see java.io.OutputStream#close() 1098 */ 1099 @Override 1100 public void close() throws IOException 1101 { 1102 if (isClosed()) 1103 return; 1104 1105 if (!isIncluding() && !super._generator.isCommitted()) 1106 commitResponse(Generator.LAST); 1107 else 1108 flushResponse(); 1109 1110 super.close(); 1111 } 1112 1113 1114 /* ------------------------------------------------------------ */ 1115 /* 1116 * @see java.io.OutputStream#flush() 1117 */ 1118 @Override 1119 public void flush() throws IOException 1120 { 1121 if (!super._generator.isCommitted()) 1122 commitResponse(Generator.MORE); 1123 super.flush(); 1124 } 1125 1126 /* ------------------------------------------------------------ */ 1127 /* 1128 * @see javax.servlet.ServletOutputStream#print(java.lang.String) 1129 */ 1130 @Override 1131 public void print(String s) throws IOException 1132 { 1133 if (isClosed()) 1134 throw new IOException("Closed"); 1135 PrintWriter writer=getPrintWriter(null); 1136 writer.print(s); 1137 } 1138 1139 /* ------------------------------------------------------------ */ 1140 public void sendResponse(Buffer response) throws IOException 1141 { 1142 ((HttpGenerator)super._generator).sendResponse(response); 1143 } 1144 1145 /* ------------------------------------------------------------ */ 1146 public void sendContent(Object content) throws IOException 1147 { 1148 Resource resource=null; 1149 1150 if (isClosed()) 1151 throw new IOException("Closed"); 1152 1153 if (super._generator.isWritten()) 1154 throw new IllegalStateException("!empty"); 1155 1156 // Convert HTTP content to content 1157 if (content instanceof HttpContent) 1158 { 1159 HttpContent httpContent = (HttpContent) content; 1160 Buffer contentType = httpContent.getContentType(); 1161 if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER)) 1162 { 1163 String enc = _response.getSetCharacterEncoding(); 1164 if(enc==null) 1165 _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType); 1166 else 1167 { 1168 if(contentType instanceof CachedBuffer) 1169 { 1170 CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc); 1171 if(content_type!=null) 1172 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type); 1173 else 1174 { 1175 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 1176 contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= ")); 1177 } 1178 } 1179 else 1180 { 1181 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 1182 contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= ")); 1183 } 1184 } 1185 } 1186 if (httpContent.getContentLength() > 0) 1187 _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, httpContent.getContentLength()); 1188 Buffer lm = httpContent.getLastModified(); 1189 long lml=httpContent.getResource().lastModified(); 1190 if (lm != null) 1191 { 1192 _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm); 1193 } 1194 else if (httpContent.getResource()!=null) 1195 { 1196 if (lml!=-1) 1197 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml); 1198 } 1199 1200 Buffer etag=httpContent.getETag(); 1201 if (etag!=null) 1202 _responseFields.put(HttpHeaders.ETAG_BUFFER,etag); 1203 1204 1205 boolean direct=_connector instanceof NIOConnector && ((NIOConnector)_connector).getUseDirectBuffers() && !(_connector instanceof SslConnector); 1206 content = direct?httpContent.getDirectBuffer():httpContent.getIndirectBuffer(); 1207 if (content==null) 1208 content=httpContent.getInputStream(); 1209 } 1210 else if (content instanceof Resource) 1211 { 1212 resource=(Resource)content; 1213 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, resource.lastModified()); 1214 content=resource.getInputStream(); 1215 } 1216 1217 // Process content. 1218 if (content instanceof Buffer) 1219 { 1220 super._generator.addContent((Buffer) content, Generator.LAST); 1221 commitResponse(Generator.LAST); 1222 } 1223 else if (content instanceof InputStream) 1224 { 1225 InputStream in = (InputStream)content; 1226 1227 try 1228 { 1229 int max = super._generator.prepareUncheckedAddContent(); 1230 Buffer buffer = super._generator.getUncheckedBuffer(); 1231 1232 int len=buffer.readFrom(in,max); 1233 1234 while (len>=0) 1235 { 1236 super._generator.completeUncheckedAddContent(); 1237 _out.flush(); 1238 1239 max = super._generator.prepareUncheckedAddContent(); 1240 buffer = super._generator.getUncheckedBuffer(); 1241 len=buffer.readFrom(in,max); 1242 } 1243 super._generator.completeUncheckedAddContent(); 1244 _out.flush(); 1245 } 1246 finally 1247 { 1248 if (resource!=null) 1249 resource.release(); 1250 else 1251 in.close(); 1252 } 1253 } 1254 else 1255 throw new IllegalArgumentException("unknown content type?"); 1256 1257 1258 } 1259 } 1260 1261 /* ------------------------------------------------------------ */ 1262 /* ------------------------------------------------------------ */ 1263 /* ------------------------------------------------------------ */ 1264 public class OutputWriter extends HttpWriter 1265 { 1266 OutputWriter() 1267 { 1268 super(AbstractHttpConnection.this._out); 1269 } 1270 } 1271 1272 1273} 1274