View Javadoc

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