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 }