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