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