View Javadoc

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