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.http; 20 21import java.io.IOException; 22 23import org.eclipse.jetty.io.Buffer; 24import org.eclipse.jetty.io.BufferCache.CachedBuffer; 25import org.eclipse.jetty.io.BufferUtil; 26import org.eclipse.jetty.io.Buffers; 27import org.eclipse.jetty.io.ByteArrayBuffer; 28import org.eclipse.jetty.io.EndPoint; 29import org.eclipse.jetty.io.EofException; 30import org.eclipse.jetty.io.View; 31import org.eclipse.jetty.io.bio.StreamEndPoint; 32import org.eclipse.jetty.util.StringUtil; 33import org.eclipse.jetty.util.log.Log; 34import org.eclipse.jetty.util.log.Logger; 35 36public class HttpParser implements Parser 37{ 38 private static final Logger LOG = Log.getLogger(HttpParser.class); 39 40 // States 41 public static final int STATE_START=-14; 42 public static final int STATE_FIELD0=-13; 43 public static final int STATE_SPACE1=-12; 44 public static final int STATE_STATUS=-11; 45 public static final int STATE_URI=-10; 46 public static final int STATE_SPACE2=-9; 47 public static final int STATE_END0=-8; 48 public static final int STATE_END1=-7; 49 public static final int STATE_FIELD2=-6; 50 public static final int STATE_HEADER=-5; 51 public static final int STATE_HEADER_NAME=-4; 52 public static final int STATE_HEADER_IN_NAME=-3; 53 public static final int STATE_HEADER_VALUE=-2; 54 public static final int STATE_HEADER_IN_VALUE=-1; 55 public static final int STATE_END=0; 56 public static final int STATE_EOF_CONTENT=1; 57 public static final int STATE_CONTENT=2; 58 public static final int STATE_CHUNKED_CONTENT=3; 59 public static final int STATE_CHUNK_SIZE=4; 60 public static final int STATE_CHUNK_PARAMS=5; 61 public static final int STATE_CHUNK=6; 62 public static final int STATE_SEEKING_EOF=7; 63 64 private final EventHandler _handler; 65 private final Buffers _buffers; // source of buffers 66 private final EndPoint _endp; 67 private Buffer _header; // Buffer for header data (and small _content) 68 private Buffer _body; // Buffer for large content 69 private Buffer _buffer; // The current buffer in use (either _header or _content) 70 private CachedBuffer _cached; 71 private final View.CaseInsensitive _tok0; // Saved token: header name, request method or response version 72 private final View.CaseInsensitive _tok1; // Saved token: header value, request URI or response code 73 private String _multiLineValue; 74 private int _responseStatus; // If >0 then we are parsing a response 75 private boolean _forceContentBuffer; 76 private boolean _persistent; 77 78 /* ------------------------------------------------------------------------------- */ 79 protected final View _contentView=new View(); // View of the content in the buffer for {@link Input} 80 protected int _state=STATE_START; 81 protected byte _eol; 82 protected int _length; 83 protected long _contentLength; 84 protected long _contentPosition; 85 protected int _chunkLength; 86 protected int _chunkPosition; 87 private boolean _headResponse; 88 89 /* ------------------------------------------------------------------------------- */ 90 /** 91 * Constructor. 92 */ 93 public HttpParser(Buffer buffer, EventHandler handler) 94 { 95 _endp=null; 96 _buffers=null; 97 _header=buffer; 98 _buffer=buffer; 99 _handler=handler; 100 101 _tok0=new View.CaseInsensitive(_header); 102 _tok1=new View.CaseInsensitive(_header); 103 } 104 105 /* ------------------------------------------------------------------------------- */ 106 /** 107 * Constructor. 108 * @param buffers the buffers to use 109 * @param endp the endpoint 110 * @param handler the even handler 111 */ 112 public HttpParser(Buffers buffers, EndPoint endp, EventHandler handler) 113 { 114 _buffers=buffers; 115 _endp=endp; 116 _handler=handler; 117 _tok0=new View.CaseInsensitive(); 118 _tok1=new View.CaseInsensitive(); 119 } 120 121 /* ------------------------------------------------------------------------------- */ 122 public long getContentLength() 123 { 124 return _contentLength; 125 } 126 127 /* ------------------------------------------------------------ */ 128 public long getContentRead() 129 { 130 return _contentPosition; 131 } 132 133 /* ------------------------------------------------------------ */ 134 /** Set if a HEAD response is expected 135 * @param head 136 */ 137 public void setHeadResponse(boolean head) 138 { 139 _headResponse=head; 140 } 141 142 /* ------------------------------------------------------------------------------- */ 143 public int getState() 144 { 145 return _state; 146 } 147 148 /* ------------------------------------------------------------------------------- */ 149 public boolean inContentState() 150 { 151 return _state > 0; 152 } 153 154 /* ------------------------------------------------------------------------------- */ 155 public boolean inHeaderState() 156 { 157 return _state < 0; 158 } 159 160 /* ------------------------------------------------------------------------------- */ 161 public boolean isChunking() 162 { 163 return _contentLength==HttpTokens.CHUNKED_CONTENT; 164 } 165 166 /* ------------------------------------------------------------ */ 167 public boolean isIdle() 168 { 169 return isState(STATE_START); 170 } 171 172 /* ------------------------------------------------------------ */ 173 public boolean isComplete() 174 { 175 return isState(STATE_END); 176 } 177 178 /* ------------------------------------------------------------ */ 179 public boolean isMoreInBuffer() 180 throws IOException 181 { 182 return ( _header!=null && _header.hasContent() || 183 _body!=null && _body.hasContent()); 184 } 185 186 /* ------------------------------------------------------------------------------- */ 187 public boolean isState(int state) 188 { 189 return _state == state; 190 } 191 192 /* ------------------------------------------------------------------------------- */ 193 public boolean isPersistent() 194 { 195 return _persistent; 196 } 197 198 /* ------------------------------------------------------------------------------- */ 199 public void setPersistent(boolean persistent) 200 { 201 _persistent = persistent; 202 if (!_persistent &&(_state==STATE_END || _state==STATE_START)) 203 _state=STATE_SEEKING_EOF; 204 } 205 206 /* ------------------------------------------------------------------------------- */ 207 /** 208 * Parse until {@link #STATE_END END} state. 209 * If the parser is already in the END state, then it is {@link #reset reset} and re-parsed. 210 * @throws IllegalStateException If the buffers have already been partially parsed. 211 */ 212 public void parse() throws IOException 213 { 214 if (_state==STATE_END) 215 reset(); 216 if (_state!=STATE_START) 217 throw new IllegalStateException("!START"); 218 219 // continue parsing 220 while (_state != STATE_END) 221 if (parseNext()<0) 222 return; 223 } 224 225 /* ------------------------------------------------------------------------------- */ 226 /** 227 * Parse until END state. 228 * This method will parse any remaining content in the current buffer as long as there is 229 * no unconsumed content. It does not care about the {@link #getState current state} of the parser. 230 * @see #parse 231 * @see #parseNext 232 */ 233 public boolean parseAvailable() throws IOException 234 { 235 boolean progress=parseNext()>0; 236 237 // continue parsing 238 while (!isComplete() && _buffer!=null && _buffer.length()>0 && !_contentView.hasContent()) 239 { 240 progress |= parseNext()>0; 241 } 242 return progress; 243 } 244 245 246 /* ------------------------------------------------------------------------------- */ 247 /** 248 * Parse until next Event. 249 * @return an indication of progress <0 EOF, 0 no progress, >0 progress. 250 */ 251 public int parseNext() throws IOException 252 { 253 try 254 { 255 int progress=0; 256 257 if (_state == STATE_END) 258 return 0; 259 260 if (_buffer==null) 261 _buffer=getHeaderBuffer(); 262 263 264 if (_state == STATE_CONTENT && _contentPosition == _contentLength) 265 { 266 _state=STATE_END; 267 _handler.messageComplete(_contentPosition); 268 return 1; 269 } 270 271 int length=_buffer.length(); 272 273 // Fill buffer if we can 274 if (length == 0) 275 { 276 int filled=-1; 277 IOException ex=null; 278 try 279 { 280 filled=fill(); 281 LOG.debug("filled {}/{}",filled,_buffer.length()); 282 } 283 catch(IOException e) 284 { 285 LOG.debug(this.toString(),e); 286 ex=e; 287 } 288 289 if (filled > 0 ) 290 progress++; 291 else if (filled < 0 ) 292 { 293 _persistent=false; 294 295 // do we have content to deliver? 296 if (_state>STATE_END) 297 { 298 if (_buffer.length()>0 && !_headResponse) 299 { 300 Buffer chunk=_buffer.get(_buffer.length()); 301 _contentPosition += chunk.length(); 302 _contentView.update(chunk); 303 _handler.content(chunk); // May recurse here 304 } 305 } 306 307 // was this unexpected? 308 switch(_state) 309 { 310 case STATE_END: 311 case STATE_SEEKING_EOF: 312 _state=STATE_END; 313 break; 314 315 case STATE_EOF_CONTENT: 316 _state=STATE_END; 317 _handler.messageComplete(_contentPosition); 318 break; 319 320 default: 321 _state=STATE_END; 322 if (!_headResponse) 323 _handler.earlyEOF(); 324 _handler.messageComplete(_contentPosition); 325 } 326 327 if (ex!=null) 328 throw ex; 329 330 if (!isComplete() && !isIdle()) 331 throw new EofException(); 332 333 return -1; 334 } 335 length=_buffer.length(); 336 } 337 338 339 // Handle header states 340 byte ch; 341 byte[] array=_buffer.array(); 342 int last=_state; 343 while (_state<STATE_END && length-->0) 344 { 345 if (last!=_state) 346 { 347 progress++; 348 last=_state; 349 } 350 351 ch=_buffer.get(); 352 353 if (_eol == HttpTokens.CARRIAGE_RETURN) 354 { 355 if (ch == HttpTokens.LINE_FEED) 356 { 357 _eol=HttpTokens.LINE_FEED; 358 continue; 359 } 360 throw new HttpException(HttpStatus.BAD_REQUEST_400); 361 } 362 _eol=0; 363 364 switch (_state) 365 { 366 case STATE_START: 367 _contentLength=HttpTokens.UNKNOWN_CONTENT; 368 _cached=null; 369 if (ch > HttpTokens.SPACE || ch<0) 370 { 371 _buffer.mark(); 372 _state=STATE_FIELD0; 373 } 374 break; 375 376 case STATE_FIELD0: 377 if (ch == HttpTokens.SPACE) 378 { 379 _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1); 380 _responseStatus=HttpVersions.CACHE.get(_tok0)==null?-1:0; 381 _state=STATE_SPACE1; 382 continue; 383 } 384 else if (ch < HttpTokens.SPACE && ch>=0) 385 { 386 throw new HttpException(HttpStatus.BAD_REQUEST_400); 387 } 388 break; 389 390 case STATE_SPACE1: 391 if (ch > HttpTokens.SPACE || ch<0) 392 { 393 _buffer.mark(); 394 if (_responseStatus>=0) 395 { 396 _state=STATE_STATUS; 397 _responseStatus=ch-'0'; 398 } 399 else 400 _state=STATE_URI; 401 } 402 else if (ch < HttpTokens.SPACE) 403 { 404 throw new HttpException(HttpStatus.BAD_REQUEST_400); 405 } 406 break; 407 408 case STATE_STATUS: 409 if (ch == HttpTokens.SPACE) 410 { 411 _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); 412 _state=STATE_SPACE2; 413 continue; 414 } 415 else if (ch>='0' && ch<='9') 416 { 417 _responseStatus=_responseStatus*10+(ch-'0'); 418 continue; 419 } 420 else if (ch < HttpTokens.SPACE && ch>=0) 421 { 422 _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null); 423 _eol=ch; 424 _state=STATE_HEADER; 425 _tok0.setPutIndex(_tok0.getIndex()); 426 _tok1.setPutIndex(_tok1.getIndex()); 427 _multiLineValue=null; 428 continue; 429 } 430 // not a digit, so must be a URI 431 _state=STATE_URI; 432 _responseStatus=-1; 433 break; 434 435 case STATE_URI: 436 if (ch == HttpTokens.SPACE) 437 { 438 _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); 439 _state=STATE_SPACE2; 440 continue; 441 } 442 else if (ch < HttpTokens.SPACE && ch>=0) 443 { 444 // HTTP/0.9 445 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer.sliceFromMark(), null); 446 _persistent=false; 447 _state=STATE_SEEKING_EOF; 448 _handler.headerComplete(); 449 _handler.messageComplete(_contentPosition); 450 return 1; 451 } 452 break; 453 454 case STATE_SPACE2: 455 if (ch > HttpTokens.SPACE || ch<0) 456 { 457 _buffer.mark(); 458 _state=STATE_FIELD2; 459 } 460 else if (ch < HttpTokens.SPACE) 461 { 462 if (_responseStatus>0) 463 { 464 _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null); 465 _eol=ch; 466 _state=STATE_HEADER; 467 _tok0.setPutIndex(_tok0.getIndex()); 468 _tok1.setPutIndex(_tok1.getIndex()); 469 _multiLineValue=null; 470 } 471 else 472 { 473 // HTTP/0.9 474 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null); 475 _persistent=false; 476 _state=STATE_SEEKING_EOF; 477 _handler.headerComplete(); 478 _handler.messageComplete(_contentPosition); 479 return 1; 480 } 481 } 482 break; 483 484 case STATE_FIELD2: 485 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) 486 { 487 Buffer version; 488 if (_responseStatus>0) 489 _handler.startResponse(version=HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark()); 490 else 491 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, version=HttpVersions.CACHE.lookup(_buffer.sliceFromMark())); 492 _eol=ch; 493 _persistent=HttpVersions.CACHE.getOrdinal(version)>=HttpVersions.HTTP_1_1_ORDINAL; 494 _state=STATE_HEADER; 495 _tok0.setPutIndex(_tok0.getIndex()); 496 _tok1.setPutIndex(_tok1.getIndex()); 497 _multiLineValue=null; 498 continue; 499 } 500 break; 501 502 case STATE_HEADER: 503 switch(ch) 504 { 505 case HttpTokens.COLON: 506 case HttpTokens.SPACE: 507 case HttpTokens.TAB: 508 { 509 // header value without name - continuation? 510 _length=-1; 511 _state=STATE_HEADER_VALUE; 512 break; 513 } 514 515 default: 516 { 517 // handler last header if any 518 if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null) 519 { 520 Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0); 521 _cached=null; 522 Buffer value=_multiLineValue == null ? _tok1 : new ByteArrayBuffer(_multiLineValue); 523 524 int ho=HttpHeaders.CACHE.getOrdinal(header); 525 if (ho >= 0) 526 { 527 int vo; 528 529 switch (ho) 530 { 531 case HttpHeaders.CONTENT_LENGTH_ORDINAL: 532 if (_contentLength != HttpTokens.CHUNKED_CONTENT ) 533 { 534 try 535 { 536 _contentLength=BufferUtil.toLong(value); 537 } 538 catch(NumberFormatException e) 539 { 540 LOG.ignore(e); 541 throw new HttpException(HttpStatus.BAD_REQUEST_400); 542 } 543 if (_contentLength <= 0) 544 _contentLength=HttpTokens.NO_CONTENT; 545 } 546 break; 547 548 case HttpHeaders.TRANSFER_ENCODING_ORDINAL: 549 value=HttpHeaderValues.CACHE.lookup(value); 550 vo=HttpHeaderValues.CACHE.getOrdinal(value); 551 if (HttpHeaderValues.CHUNKED_ORDINAL == vo) 552 _contentLength=HttpTokens.CHUNKED_CONTENT; 553 else 554 { 555 String c=value.toString(StringUtil.__ISO_8859_1); 556 if (c.endsWith(HttpHeaderValues.CHUNKED)) 557 _contentLength=HttpTokens.CHUNKED_CONTENT; 558 559 else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0) 560 throw new HttpException(400,null); 561 } 562 break; 563 564 case HttpHeaders.CONNECTION_ORDINAL: 565 switch(HttpHeaderValues.CACHE.getOrdinal(value)) 566 { 567 case HttpHeaderValues.CLOSE_ORDINAL: 568 _persistent=false; 569 break; 570 571 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: 572 _persistent=true; 573 break; 574 575 case -1: // No match, may be multi valued 576 { 577 for (String v : value.toString().split(",")) 578 { 579 switch(HttpHeaderValues.CACHE.getOrdinal(v.trim())) 580 { 581 case HttpHeaderValues.CLOSE_ORDINAL: 582 _persistent=false; 583 break; 584 585 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: 586 _persistent=true; 587 break; 588 } 589 } 590 break; 591 } 592 } 593 } 594 } 595 596 _handler.parsedHeader(header, value); 597 _tok0.setPutIndex(_tok0.getIndex()); 598 _tok1.setPutIndex(_tok1.getIndex()); 599 _multiLineValue=null; 600 } 601 _buffer.setMarkIndex(-1); 602 603 // now handle ch 604 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) 605 { 606 // is it a response that cannot have a body? 607 if (_responseStatus > 0 && // response 608 (_responseStatus == 304 || // not-modified response 609 _responseStatus == 204 || // no-content response 610 _responseStatus < 200)) // 1xx response 611 _contentLength=HttpTokens.NO_CONTENT; // ignore any other headers set 612 // else if we don't know framing 613 else if (_contentLength == HttpTokens.UNKNOWN_CONTENT) 614 { 615 if (_responseStatus == 0 // request 616 || _responseStatus == 304 // not-modified response 617 || _responseStatus == 204 // no-content response 618 || _responseStatus < 200) // 1xx response 619 _contentLength=HttpTokens.NO_CONTENT; 620 else 621 _contentLength=HttpTokens.EOF_CONTENT; 622 } 623 624 _contentPosition=0; 625 _eol=ch; 626 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) 627 _eol=_buffer.get(); 628 629 // We convert _contentLength to an int for this switch statement because 630 // we don't care about the amount of data available just whether there is some. 631 switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength) 632 { 633 case HttpTokens.EOF_CONTENT: 634 _state=STATE_EOF_CONTENT; 635 _handler.headerComplete(); // May recurse here ! 636 break; 637 638 case HttpTokens.CHUNKED_CONTENT: 639 _state=STATE_CHUNKED_CONTENT; 640 _handler.headerComplete(); // May recurse here ! 641 break; 642 643 case HttpTokens.NO_CONTENT: 644 _handler.headerComplete(); 645 _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; 646 _handler.messageComplete(_contentPosition); 647 return 1; 648 649 default: 650 _state=STATE_CONTENT; 651 _handler.headerComplete(); // May recurse here ! 652 break; 653 } 654 return 1; 655 } 656 else 657 { 658 // New header 659 _length=1; 660 _buffer.mark(); 661 _state=STATE_HEADER_NAME; 662 663 // try cached name! 664 if (array!=null) 665 { 666 _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1); 667 668 if (_cached!=null) 669 { 670 _length=_cached.length(); 671 _buffer.setGetIndex(_buffer.markIndex()+_length); 672 length=_buffer.length(); 673 } 674 } 675 } 676 } 677 } 678 679 break; 680 681 case STATE_HEADER_NAME: 682 switch(ch) 683 { 684 case HttpTokens.CARRIAGE_RETURN: 685 case HttpTokens.LINE_FEED: 686 if (_length > 0) 687 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); 688 _eol=ch; 689 _state=STATE_HEADER; 690 break; 691 case HttpTokens.COLON: 692 if (_length > 0 && _cached==null) 693 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); 694 _length=-1; 695 _state=STATE_HEADER_VALUE; 696 break; 697 case HttpTokens.SPACE: 698 case HttpTokens.TAB: 699 break; 700 default: 701 { 702 _cached=null; 703 if (_length == -1) 704 _buffer.mark(); 705 _length=_buffer.getIndex() - _buffer.markIndex(); 706 _state=STATE_HEADER_IN_NAME; 707 } 708 } 709 710 break; 711 712 case STATE_HEADER_IN_NAME: 713 switch(ch) 714 { 715 case HttpTokens.CARRIAGE_RETURN: 716 case HttpTokens.LINE_FEED: 717 if (_length > 0) 718 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); 719 _eol=ch; 720 _state=STATE_HEADER; 721 break; 722 case HttpTokens.COLON: 723 if (_length > 0 && _cached==null) 724 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); 725 _length=-1; 726 _state=STATE_HEADER_VALUE; 727 break; 728 case HttpTokens.SPACE: 729 case HttpTokens.TAB: 730 _state=STATE_HEADER_NAME; 731 break; 732 default: 733 { 734 _cached=null; 735 _length++; 736 } 737 } 738 break; 739 740 case STATE_HEADER_VALUE: 741 switch(ch) 742 { 743 case HttpTokens.CARRIAGE_RETURN: 744 case HttpTokens.LINE_FEED: 745 if (_length > 0) 746 { 747 if (_tok1.length() == 0) 748 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); 749 else 750 { 751 // Continuation line! 752 if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1); 753 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); 754 _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1); 755 } 756 } 757 _eol=ch; 758 _state=STATE_HEADER; 759 break; 760 case HttpTokens.SPACE: 761 case HttpTokens.TAB: 762 break; 763 default: 764 { 765 if (_length == -1) 766 _buffer.mark(); 767 _length=_buffer.getIndex() - _buffer.markIndex(); 768 _state=STATE_HEADER_IN_VALUE; 769 } 770 } 771 break; 772 773 case STATE_HEADER_IN_VALUE: 774 switch(ch) 775 { 776 case HttpTokens.CARRIAGE_RETURN: 777 case HttpTokens.LINE_FEED: 778 if (_length > 0) 779 { 780 if (_tok1.length() == 0) 781 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); 782 else 783 { 784 // Continuation line! 785 if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1); 786 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); 787 _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1); 788 } 789 } 790 _eol=ch; 791 _state=STATE_HEADER; 792 break; 793 case HttpTokens.SPACE: 794 case HttpTokens.TAB: 795 _state=STATE_HEADER_VALUE; 796 break; 797 default: 798 _length++; 799 } 800 break; 801 } 802 } // end of HEADER states loop 803 804 // ========================== 805 806 // Handle HEAD response 807 if (_responseStatus>0 && _headResponse) 808 { 809 _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; 810 _handler.messageComplete(_contentLength); 811 } 812 813 814 // ========================== 815 816 // Handle _content 817 length=_buffer.length(); 818 Buffer chunk; 819 last=_state; 820 while (_state > STATE_END && length > 0) 821 { 822 if (last!=_state) 823 { 824 progress++; 825 last=_state; 826 } 827 828 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED) 829 { 830 _eol=_buffer.get(); 831 length=_buffer.length(); 832 continue; 833 } 834 _eol=0; 835 switch (_state) 836 { 837 case STATE_EOF_CONTENT: 838 chunk=_buffer.get(_buffer.length()); 839 _contentPosition += chunk.length(); 840 _contentView.update(chunk); 841 _handler.content(chunk); // May recurse here 842 // TODO adjust the _buffer to keep unconsumed content 843 return 1; 844 845 case STATE_CONTENT: 846 { 847 long remaining=_contentLength - _contentPosition; 848 if (remaining == 0) 849 { 850 _state=_persistent?STATE_END:STATE_SEEKING_EOF; 851 _handler.messageComplete(_contentPosition); 852 return 1; 853 } 854 855 if (length > remaining) 856 { 857 // We can cast reamining to an int as we know that it is smaller than 858 // or equal to length which is already an int. 859 length=(int)remaining; 860 } 861 862 chunk=_buffer.get(length); 863 _contentPosition += chunk.length(); 864 _contentView.update(chunk); 865 _handler.content(chunk); // May recurse here 866 867 if(_contentPosition == _contentLength) 868 { 869 _state=_persistent?STATE_END:STATE_SEEKING_EOF; 870 _handler.messageComplete(_contentPosition); 871 } 872 // TODO adjust the _buffer to keep unconsumed content 873 return 1; 874 } 875 876 case STATE_CHUNKED_CONTENT: 877 { 878 ch=_buffer.peek(); 879 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) 880 _eol=_buffer.get(); 881 else if (ch <= HttpTokens.SPACE) 882 _buffer.get(); 883 else 884 { 885 _chunkLength=0; 886 _chunkPosition=0; 887 _state=STATE_CHUNK_SIZE; 888 } 889 break; 890 } 891 892 case STATE_CHUNK_SIZE: 893 { 894 ch=_buffer.get(); 895 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) 896 { 897 _eol=ch; 898 899 if (_chunkLength == 0) 900 { 901 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) 902 _eol=_buffer.get(); 903 _state=_persistent?STATE_END:STATE_SEEKING_EOF; 904 _handler.messageComplete(_contentPosition); 905 return 1; 906 } 907 else 908 _state=STATE_CHUNK; 909 } 910 else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON) 911 _state=STATE_CHUNK_PARAMS; 912 else if (ch >= '0' && ch <= '9') 913 _chunkLength=_chunkLength * 16 + (ch - '0'); 914 else if (ch >= 'a' && ch <= 'f') 915 _chunkLength=_chunkLength * 16 + (10 + ch - 'a'); 916 else if (ch >= 'A' && ch <= 'F') 917 _chunkLength=_chunkLength * 16 + (10 + ch - 'A'); 918 else 919 throw new IOException("bad chunk char: " + ch); 920 break; 921 } 922 923 case STATE_CHUNK_PARAMS: 924 { 925 ch=_buffer.get(); 926 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) 927 { 928 _eol=ch; 929 if (_chunkLength == 0) 930 { 931 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) 932 _eol=_buffer.get(); 933 _state=_persistent?STATE_END:STATE_SEEKING_EOF; 934 _handler.messageComplete(_contentPosition); 935 return 1; 936 } 937 else 938 _state=STATE_CHUNK; 939 } 940 break; 941 } 942 943 case STATE_CHUNK: 944 { 945 int remaining=_chunkLength - _chunkPosition; 946 if (remaining == 0) 947 { 948 _state=STATE_CHUNKED_CONTENT; 949 break; 950 } 951 else if (length > remaining) 952 length=remaining; 953 chunk=_buffer.get(length); 954 _contentPosition += chunk.length(); 955 _chunkPosition += chunk.length(); 956 _contentView.update(chunk); 957 _handler.content(chunk); // May recurse here 958 // TODO adjust the _buffer to keep unconsumed content 959 return 1; 960 } 961 962 case STATE_SEEKING_EOF: 963 { 964 // Close if there is more data than CRLF 965 if (_buffer.length()>2) 966 { 967 _state=STATE_END; 968 _endp.close(); 969 } 970 else 971 { 972 // or if the data is not white space 973 while (_buffer.length()>0) 974 if (!Character.isWhitespace(_buffer.get())) 975 { 976 _state=STATE_END; 977 _endp.close(); 978 _buffer.clear(); 979 } 980 } 981 982 _buffer.clear(); 983 break; 984 } 985 } 986 987 length=_buffer.length(); 988 } 989 990 return progress; 991 } 992 catch(HttpException e) 993 { 994 _persistent=false; 995 _state=STATE_SEEKING_EOF; 996 throw e; 997 } 998 } 999 1000 /* ------------------------------------------------------------------------------- */ 1001 /** fill the buffers from the endpoint 1002 * 1003 */ 1004 protected int fill() throws IOException 1005 { 1006 // Do we have a buffer? 1007 if (_buffer==null) 1008 _buffer=getHeaderBuffer(); 1009 1010 // Is there unconsumed content in body buffer 1011 if (_state>STATE_END && _buffer==_header && _header!=null && !_header.hasContent() && _body!=null && _body.hasContent()) 1012 { 1013 _buffer=_body; 1014 return _buffer.length(); 1015 } 1016 1017 // Shall we switch to a body buffer? 1018 if (_buffer==_header && _state>STATE_END && _header.length()==0 && (_forceContentBuffer || (_contentLength-_contentPosition)>_header.capacity()) && (_body!=null||_buffers!=null)) 1019 { 1020 if (_body==null) 1021 _body=_buffers.getBuffer(); 1022 _buffer=_body; 1023 } 1024 1025 // Do we have somewhere to fill from? 1026 if (_endp != null ) 1027 { 1028 // Shall we compact the body? 1029 if (_buffer==_body || _state>STATE_END) 1030 { 1031 _buffer.compact(); 1032 } 1033 1034 // Are we full? 1035 if (_buffer.space() == 0) 1036 { 1037 LOG.warn("HttpParser Full for {} ",_endp); 1038 _buffer.clear(); 1039 throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "Request Entity Too Large: "+(_buffer==_body?"body":"head")); 1040 } 1041 1042 try 1043 { 1044 int filled = _endp.fill(_buffer); 1045 return filled; 1046 } 1047 catch(IOException e) 1048 { 1049 LOG.debug(e); 1050 throw (e instanceof EofException) ? e:new EofException(e); 1051 } 1052 } 1053 1054 return -1; 1055 } 1056 1057 /* ------------------------------------------------------------------------------- */ 1058 public void reset() 1059 { 1060 // reset state 1061 _contentView.setGetIndex(_contentView.putIndex()); 1062 _state=_persistent?STATE_START:(_endp.isInputShutdown()?STATE_END:STATE_SEEKING_EOF); 1063 _contentLength=HttpTokens.UNKNOWN_CONTENT; 1064 _contentPosition=0; 1065 _length=0; 1066 _responseStatus=0; 1067 1068 // Consume LF if CRLF 1069 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer!=null && _buffer.hasContent() && _buffer.peek() == HttpTokens.LINE_FEED) 1070 _eol=_buffer.get(); 1071 1072 if (_body!=null && _body.hasContent()) 1073 { 1074 // There is content in the body after the end of the request. 1075 // This is probably a pipelined header of the next request, so we need to 1076 // copy it to the header buffer. 1077 if (_header==null) 1078 getHeaderBuffer(); 1079 else 1080 { 1081 _header.setMarkIndex(-1); 1082 _header.compact(); 1083 } 1084 int take=_header.space(); 1085 if (take>_body.length()) 1086 take=_body.length(); 1087 _body.peek(_body.getIndex(),take); 1088 _body.skip(_header.put(_body.peek(_body.getIndex(),take))); 1089 } 1090 1091 if (_header!=null) 1092 { 1093 _header.setMarkIndex(-1); 1094 _header.compact(); 1095 } 1096 if (_body!=null) 1097 _body.setMarkIndex(-1); 1098 1099 _buffer=_header; 1100 returnBuffers(); 1101 } 1102 1103 1104 /* ------------------------------------------------------------------------------- */ 1105 public void returnBuffers() 1106 { 1107 if (_body!=null && !_body.hasContent() && _body.markIndex()==-1 && _buffers!=null) 1108 { 1109 if (_buffer==_body) 1110 _buffer=_header; 1111 if (_buffers!=null) 1112 _buffers.returnBuffer(_body); 1113 _body=null; 1114 } 1115 1116 if (_header!=null && !_header.hasContent() && _header.markIndex()==-1 && _buffers!=null) 1117 { 1118 if (_buffer==_header) 1119 _buffer=null; 1120 _buffers.returnBuffer(_header); 1121 _header=null; 1122 } 1123 } 1124 1125 /* ------------------------------------------------------------------------------- */ 1126 public void setState(int state) 1127 { 1128 this._state=state; 1129 _contentLength=HttpTokens.UNKNOWN_CONTENT; 1130 } 1131 1132 /* ------------------------------------------------------------------------------- */ 1133 public String toString(Buffer buf) 1134 { 1135 return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode(); 1136 } 1137 1138 /* ------------------------------------------------------------------------------- */ 1139 @Override 1140 public String toString() 1141 { 1142 return String.format("%s{s=%d,l=%d,c=%d}", 1143 getClass().getSimpleName(), 1144 _state, 1145 _length, 1146 _contentLength); 1147 } 1148 1149 /* ------------------------------------------------------------ */ 1150 public Buffer getHeaderBuffer() 1151 { 1152 if (_header == null) 1153 { 1154 _header=_buffers.getHeader(); 1155 _tok0.update(_header); 1156 _tok1.update(_header); 1157 } 1158 return _header; 1159 } 1160 1161 /* ------------------------------------------------------------ */ 1162 public Buffer getBodyBuffer() 1163 { 1164 return _body; 1165 } 1166 1167 /* ------------------------------------------------------------ */ 1168 /** 1169 * @param force True if a new buffer will be forced to be used for content and the header buffer will not be used. 1170 */ 1171 public void setForceContentBuffer(boolean force) 1172 { 1173 _forceContentBuffer=force; 1174 } 1175 1176 /* ------------------------------------------------------------ */ 1177 public Buffer blockForContent(long maxIdleTime) throws IOException 1178 { 1179 if (_contentView.length()>0) 1180 return _contentView; 1181 1182 if (getState() <= STATE_END || isState(STATE_SEEKING_EOF)) 1183 return null; 1184 1185 try 1186 { 1187 parseNext(); 1188 1189 // parse until some progress is made (or IOException thrown for timeout) 1190 while(_contentView.length() == 0 && !(isState(HttpParser.STATE_END)||isState(HttpParser.STATE_SEEKING_EOF)) && _endp!=null && _endp.isOpen()) 1191 { 1192 if (!_endp.isBlocking()) 1193 { 1194 if (parseNext()>0) 1195 continue; 1196 1197 if (!_endp.blockReadable(maxIdleTime)) 1198 { 1199 _endp.close(); 1200 throw new EofException("timeout"); 1201 } 1202 } 1203 1204 parseNext(); 1205 } 1206 } 1207 catch(IOException e) 1208 { 1209 // TODO is this needed? 1210 _endp.close(); 1211 throw e; 1212 } 1213 1214 return _contentView.length()>0?_contentView:null; 1215 } 1216 1217 /* ------------------------------------------------------------ */ 1218 /* (non-Javadoc) 1219 * @see java.io.InputStream#available() 1220 */ 1221 public int available() throws IOException 1222 { 1223 if (_contentView!=null && _contentView.length()>0) 1224 return _contentView.length(); 1225 1226 if (_endp.isBlocking()) 1227 { 1228 if (_state>0 && _endp instanceof StreamEndPoint) 1229 return ((StreamEndPoint)_endp).getInputStream().available()>0?1:0; 1230 1231 return 0; 1232 } 1233 1234 parseNext(); 1235 return _contentView==null?0:_contentView.length(); 1236 } 1237 1238 /* ------------------------------------------------------------ */ 1239 /* ------------------------------------------------------------ */ 1240 /* ------------------------------------------------------------ */ 1241 public static abstract class EventHandler 1242 { 1243 public abstract void content(Buffer ref) throws IOException; 1244 1245 public void headerComplete() throws IOException 1246 { 1247 } 1248 1249 public void messageComplete(long contentLength) throws IOException 1250 { 1251 } 1252 1253 /** 1254 * This is the method called by parser when a HTTP Header name and value is found 1255 */ 1256 public void parsedHeader(Buffer name, Buffer value) throws IOException 1257 { 1258 } 1259 1260 /** 1261 * This is the method called by parser when the HTTP request line is parsed 1262 */ 1263 public abstract void startRequest(Buffer method, Buffer url, Buffer version) 1264 throws IOException; 1265 1266 /** 1267 * This is the method called by parser when the HTTP request line is parsed 1268 */ 1269 public abstract void startResponse(Buffer version, int status, Buffer reason) 1270 throws IOException; 1271 1272 public void earlyEOF() 1273 {} 1274 } 1275 1276 1277 1278 1279} 1280