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