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                         byte digit=_tok1.peek(_tok1.getIndex());
417                         if (digit>='1'&&digit<='5')
418                         {
419 			    _responseStatus = BufferUtil.toInt(_tok1);
420                             _handler.startResponse(HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark());
421                         } 
422                         else
423                             _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1,HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
424                         _eol=ch;
425                         _state=STATE_HEADER;
426                         _tok0.setPutIndex(_tok0.getIndex());
427                         _tok1.setPutIndex(_tok1.getIndex());
428                         _multiLineValue=null;
429                         continue;
430                         // return total_filled;
431                     }
432                     break;
433 
434                 case STATE_HEADER:
435                     switch(ch)
436                     {
437                         case HttpTokens.COLON:
438                         case HttpTokens.SPACE:
439                         case HttpTokens.TAB:
440                         {
441                             // header value without name - continuation?
442                             _length=-1;
443                             _state=STATE_HEADER_VALUE;
444                             break;
445                         }
446                         
447                         default:
448                         {
449                             // handler last header if any
450                             if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
451                             {
452                                 
453                                 Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
454                                 _cached=null;
455                                 Buffer value=_multiLineValue == null ? (Buffer) _tok1 : (Buffer) new ByteArrayBuffer(_multiLineValue);
456                                 
457                                 int ho=HttpHeaders.CACHE.getOrdinal(header);
458                                 if (ho >= 0)
459                                 {
460                                     int vo=-1; 
461                                     
462                                     switch (ho)
463                                     {
464                                         case HttpHeaders.CONTENT_LENGTH_ORDINAL:
465                                             if (_contentLength != HttpTokens.CHUNKED_CONTENT)
466                                             {
467                                                 _contentLength=BufferUtil.toLong(value);
468                                                 if (_contentLength <= 0)
469                                                     _contentLength=HttpTokens.NO_CONTENT;
470                                             }
471                                             break;
472                                             
473                                         case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
474                                             value=HttpHeaderValues.CACHE.lookup(value);
475                                             vo=HttpHeaderValues.CACHE.getOrdinal(value);
476                                             if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
477                                                 _contentLength=HttpTokens.CHUNKED_CONTENT;
478                                             else
479                                             {
480                                                 String c=value.toString();
481                                                 if (c.endsWith(HttpHeaderValues.CHUNKED))
482                                                     _contentLength=HttpTokens.CHUNKED_CONTENT;
483                                                 
484                                                 else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
485                                                     throw new HttpException(400,null);
486                                             }
487                                             break;
488                                     }
489                                 }
490                                 
491                                 _handler.parsedHeader(header, value);
492                                 _tok0.setPutIndex(_tok0.getIndex());
493                                 _tok1.setPutIndex(_tok1.getIndex());
494                                 _multiLineValue=null;
495                             }
496                             
497                             
498                             // now handle ch
499                             if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
500                             {
501                                 // End of header
502 
503                                 // work out the _content demarcation
504                                 if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
505                                 {
506                                     if (_responseStatus == 0  // request
507                                     || _responseStatus == 304 // not-modified response
508                                     || _responseStatus == 204 // no-content response
509                                     || _responseStatus < 200) // 1xx response
510                                         _contentLength=HttpTokens.NO_CONTENT;
511                                     else
512                                         _contentLength=HttpTokens.EOF_CONTENT;
513                                 }
514 
515                                 _contentPosition=0;
516                                 _eol=ch;
517                                 // We convert _contentLength to an int for this switch statement because
518                                 // we don't care about the amount of data available just whether there is some.
519                                 switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
520                                 {
521                                     case HttpTokens.EOF_CONTENT:
522                                         _state=STATE_EOF_CONTENT;
523                                         if(_body==null && _buffers!=null)
524                                             _body=_buffers.getBuffer(_contentBufferSize);
525                                         
526                                         _handler.headerComplete(); // May recurse here !
527                                         break;
528                                         
529                                     case HttpTokens.CHUNKED_CONTENT:
530                                         _state=STATE_CHUNKED_CONTENT;
531                                         if (_body==null && _buffers!=null)
532                                             _body=_buffers.getBuffer(_contentBufferSize);
533                                         _handler.headerComplete(); // May recurse here !
534                                         break;
535                                         
536                                     case HttpTokens.NO_CONTENT:
537                                         _state=STATE_END;
538                                         _handler.headerComplete(); 
539                                         _handler.messageComplete(_contentPosition);
540                                         break;
541                                         
542                                     default:
543                                         _state=STATE_CONTENT;
544                                         if(_forceContentBuffer || 
545                                           (_buffers!=null && _body==null && _buffer==_header && _contentLength>=(_header.capacity()-_header.getIndex())))
546                                             _body=_buffers.getBuffer(_contentBufferSize);
547                                         _handler.headerComplete(); // May recurse here !
548                                         break;
549                                 }
550                                 return total_filled;
551                             }
552                             else
553                             {
554                                 // New header
555                                 _length=1;
556                                 _buffer.mark();
557                                 _state=STATE_HEADER_NAME;
558                                 
559                                 // try cached name!
560                                 if (array!=null)
561                                 {
562                                     _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1);
563 
564                                     if (_cached!=null)
565                                     {
566                                         _length=_cached.length();
567                                         _buffer.setGetIndex(_buffer.markIndex()+_length);
568                                         length=_buffer.length();
569                                     }
570                                 }
571                             } 
572                         }
573                     }
574                     
575                     break;
576 
577                 case STATE_HEADER_NAME:
578                     switch(ch)
579                     {
580                         case HttpTokens.CARRIAGE_RETURN:
581                         case HttpTokens.LINE_FEED:
582                             if (_length > 0)
583                                 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
584                             _eol=ch;
585                             _state=STATE_HEADER;
586                             break;
587                         case HttpTokens.COLON:
588                             if (_length > 0 && _cached==null)
589                                 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
590                             _length=-1;
591                             _state=STATE_HEADER_VALUE;
592                             break;
593                         case HttpTokens.SPACE:
594                         case HttpTokens.TAB:
595                             break;
596                         default: 
597                         {
598                             _cached=null;
599                             if (_length == -1) 
600                                 _buffer.mark();
601                             _length=_buffer.getIndex() - _buffer.markIndex();
602                             _state=STATE_HEADER_IN_NAME;  
603                         }
604                     }
605      
606                     break;
607 
608                 case STATE_HEADER_IN_NAME:
609                     switch(ch)
610                     {
611                         case HttpTokens.CARRIAGE_RETURN:
612                         case HttpTokens.LINE_FEED:
613                             if (_length > 0)
614                                 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
615                             _eol=ch;
616                             _state=STATE_HEADER;
617                             break;
618                         case HttpTokens.COLON:
619                             if (_length > 0 && _cached==null)
620                                 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
621                             _length=-1;
622                             _state=STATE_HEADER_VALUE;
623                             break;
624                         case HttpTokens.SPACE:
625                         case HttpTokens.TAB:
626                             _state=STATE_HEADER_NAME;
627                             break;
628                         default:
629                         {
630                             _cached=null;
631                             _length++;
632                         }
633                     }
634                     break;
635 
636                 case STATE_HEADER_VALUE:
637                     switch(ch)
638                     {
639                         case HttpTokens.CARRIAGE_RETURN:
640                         case HttpTokens.LINE_FEED:
641                             if (_length > 0)
642                             {
643                                 if (_tok1.length() == 0)
644                                     _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
645                                 else
646                                 {
647                                     // Continuation line!
648                                     if (_multiLineValue == null) _multiLineValue=_tok1.toString();
649                                     _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
650                                     _multiLineValue += " " + _tok1.toString();
651                                 }
652                             }
653                             _eol=ch;
654                             _state=STATE_HEADER;
655                             break;
656                         case HttpTokens.SPACE:
657                         case HttpTokens.TAB:
658                             break;
659                         default:
660                         {
661                             if (_length == -1) 
662                                 _buffer.mark();
663                             _length=_buffer.getIndex() - _buffer.markIndex();
664                             _state=STATE_HEADER_IN_VALUE;
665                         }       
666                     }
667                     break;
668 
669                 case STATE_HEADER_IN_VALUE:
670                     switch(ch)
671                     {
672                         case HttpTokens.CARRIAGE_RETURN:
673                         case HttpTokens.LINE_FEED:
674                             if (_length > 0)
675                             {
676                                 if (_tok1.length() == 0)
677                                     _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
678                                 else
679                                 {
680                                     // Continuation line!
681                                     if (_multiLineValue == null) _multiLineValue=_tok1.toString();
682                                     _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
683                                     _multiLineValue += " " + _tok1.toString();
684                                 }
685                             }
686                             _eol=ch;
687                             _state=STATE_HEADER;
688                             break;
689                         case HttpTokens.SPACE:
690                         case HttpTokens.TAB:
691                             _state=STATE_HEADER_VALUE;
692                             break;
693                         default:
694                             _length++;
695                     }
696                     break;
697             }
698         } // end of HEADER states loop
699         
700         // ==========================
701         
702         // Handle _content
703         length=_buffer.length();
704         if (_input!=null)
705             _input._contentView=_contentView;
706         Buffer chunk; 
707         while (_state > STATE_END && length > 0)
708         {
709             if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
710             {
711                 _eol=_buffer.get();
712                 length=_buffer.length();
713                 continue;
714             }
715             _eol=0;
716             switch (_state)
717             {
718                 case STATE_EOF_CONTENT:
719                     chunk=_buffer.get(_buffer.length());
720                     _contentPosition += chunk.length();
721                     _contentView.update(chunk);
722                     _handler.content(chunk); // May recurse here 
723                     // TODO adjust the _buffer to keep unconsumed content
724                     return total_filled;
725 
726                 case STATE_CONTENT: 
727                 {
728                     long remaining=_contentLength - _contentPosition;
729                     if (remaining == 0)
730                     {
731                         _state=STATE_END;
732                         _handler.messageComplete(_contentPosition);
733                         return total_filled;
734                     }
735                     
736                     if (length > remaining) 
737                     {
738                         // We can cast reamining to an int as we know that it is smaller than
739                         // or equal to length which is already an int. 
740                         length=(int)remaining;
741                     }
742                     
743                     chunk=_buffer.get(length);
744                     _contentPosition += chunk.length();
745                     _contentView.update(chunk);
746                     _handler.content(chunk); // May recurse here 
747                     
748                     if(_contentPosition == _contentLength)
749                     {
750                         _state=STATE_END;
751                         _handler.messageComplete(_contentPosition);
752                     }
753                     // TODO adjust the _buffer to keep unconsumed content
754                     return total_filled;
755                 }
756 
757                 case STATE_CHUNKED_CONTENT:
758                 {
759                     ch=_buffer.peek();
760                     if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
761                         _eol=_buffer.get();
762                     else if (ch <= HttpTokens.SPACE)
763                         _buffer.get();
764                     else
765                     {
766                         _chunkLength=0;
767                         _chunkPosition=0;
768                         _state=STATE_CHUNK_SIZE;
769                     }
770                     break;
771                 }
772 
773                 case STATE_CHUNK_SIZE:
774                 {
775                     ch=_buffer.get();
776                     if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
777                     {
778                         _eol=ch;
779                         if (_chunkLength == 0)
780                         {
781                             _state=STATE_END;
782                             _handler.messageComplete(_contentPosition);
783                             return total_filled;
784                         }
785                         else
786                             _state=STATE_CHUNK;
787                     }
788                     else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
789                         _state=STATE_CHUNK_PARAMS;
790                     else if (ch >= '0' && ch <= '9')
791                         _chunkLength=_chunkLength * 16 + (ch - '0');
792                     else if (ch >= 'a' && ch <= 'f')
793                         _chunkLength=_chunkLength * 16 + (10 + ch - 'a');
794                     else if (ch >= 'A' && ch <= 'F')
795                         _chunkLength=_chunkLength * 16 + (10 + ch - 'A');
796                     else
797                         throw new IOException("bad chunk char: " + ch);
798                     break;
799                 }
800 
801                 case STATE_CHUNK_PARAMS:
802                 {
803                     ch=_buffer.get();
804                     if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
805                     {
806                         _eol=ch;
807                         if (_chunkLength == 0)
808                         {
809                             _state=STATE_END;
810                             _handler.messageComplete(_contentPosition);
811                             return total_filled;
812                         }
813                         else
814                             _state=STATE_CHUNK;
815                     }
816                     break;
817                 }
818                 
819                 case STATE_CHUNK: 
820                 {
821                     int remaining=_chunkLength - _chunkPosition;
822                     if (remaining == 0)
823                     {
824                         _state=STATE_CHUNKED_CONTENT;
825                         break;
826                     }
827                     else if (length > remaining) 
828                         length=remaining;
829                     chunk=_buffer.get(length);
830                     _contentPosition += chunk.length();
831                     _chunkPosition += chunk.length();
832                     _contentView.update(chunk);
833                     _handler.content(chunk); // May recurse here 
834                     // TODO adjust the _buffer to keep unconsumed content
835                     return total_filled;
836                 }
837             }
838 
839             length=_buffer.length();
840         }
841         return total_filled;
842     }
843 
844     /* ------------------------------------------------------------------------------- */
845     /** fill the buffers from the endpoint
846      * 
847      */
848     public long fill() throws IOException
849     {
850         if (_buffer==null)
851         {
852             _buffer=_header=getHeaderBuffer();
853             _tok0=new View.CaseInsensitive(_buffer);
854             _tok1=new View.CaseInsensitive(_buffer);
855         }
856         if (_body!=null && _buffer!=_body)
857             _buffer=_body;
858         if (_buffer == _body) 
859             _buffer.compact();
860         
861         int space=_buffer.space();
862         
863         // Fill buffer if we can
864         if (space == 0) 
865             throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL "+(_buffer==_body?"body":"head"));
866         else
867         {
868             int filled=-1;
869             
870             if (_endp != null )
871             {
872                 try
873                 {
874                     filled=_endp.fill(_buffer);
875                 }
876                 catch(IOException e)
877                 {
878                     Log.debug(e);
879                     reset(true);
880                     throw (e instanceof EofException) ? e:new EofException(e);
881                 }
882             }
883             
884             return filled;
885         }
886     }
887 
888     /* ------------------------------------------------------------------------------- */
889     /** Skip any CRLFs in buffers
890      * 
891      */
892     public void skipCRLF()
893     {
894 
895         while (_header!=null && _header.length()>0)
896         {
897             byte ch = _header.peek();
898             if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED)
899             {
900                 _eol=ch;
901                 _header.skip(1);
902             }
903             else
904                 break;
905         }
906 
907         while (_body!=null && _body.length()>0)
908         {
909             byte ch = _body.peek();
910             if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED)
911             {
912                 _eol=ch;
913                 _body.skip(1);
914             }
915             else
916                 break;
917         }
918         
919     }
920     /* ------------------------------------------------------------------------------- */
921     public void reset(boolean returnBuffers)
922     {   
923         synchronized (this) 
924         {
925             if (_input!=null && _contentView.length()>0)
926                 _input._contentView=_contentView.duplicate(Buffer.READWRITE);
927             
928             _state=STATE_START;
929             _contentLength=HttpTokens.UNKNOWN_CONTENT;
930             _contentPosition=0;
931             _length=0;
932             _responseStatus=0;
933 
934             if (_buffer!=null && _buffer.length()>0 && _eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
935             {
936                 _buffer.skip(1);
937                 _eol=HttpTokens.LINE_FEED;
938             }
939 
940             if (_body!=null)
941             {   
942                 if (_body.hasContent())
943                 {
944                     _header.setMarkIndex(-1);
945                     _header.compact();
946                     // TODO if pipelined requests received after big input - maybe this is not good?.
947                     _body.skip(_header.put(_body));
948 
949                 }
950 
951                 if (_body.length()==0)
952                 {
953                     if (_buffers!=null && returnBuffers)
954                         _buffers.returnBuffer(_body);
955                     _body=null; 
956                 }
957                 else
958                 {
959                     _body.setMarkIndex(-1);
960                     _body.compact();
961                 }
962             }
963 
964 
965             if (_header!=null)
966             {
967                 _header.setMarkIndex(-1);
968                 if (!_header.hasContent() && _buffers!=null && returnBuffers)
969                 {
970                     _buffers.returnBuffer(_header);
971                     _header=null;
972                     _buffer=null;
973                 }   
974                 else
975                 {
976                     _header.compact();
977                     _tok0.update(_header);
978                     _tok0.update(0,0);
979                     _tok1.update(_header);
980                     _tok1.update(0,0);
981                 }
982             }
983 
984             _buffer=_header;
985         }
986     }
987 
988     /* ------------------------------------------------------------------------------- */
989     public void setState(int state)
990     {
991         this._state=state;
992         _contentLength=HttpTokens.UNKNOWN_CONTENT;
993     }
994 
995     /* ------------------------------------------------------------------------------- */
996     public String toString(Buffer buf)
997     {
998         return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
999     }
1000 
1001     /* ------------------------------------------------------------------------------- */
1002     public String toString()
1003     {
1004         return "state=" + _state + " length=" + _length + " len=" + _contentLength;
1005     }    
1006     
1007     /* ------------------------------------------------------------ */
1008     public Buffer getHeaderBuffer()
1009     {
1010         if (_header == null)
1011         {
1012             _header=_buffers.getBuffer(_headerBufferSize);
1013         }
1014         return _header;
1015     }
1016     
1017     /* ------------------------------------------------------------ */
1018     public Buffer getBodyBuffer()
1019     {
1020         return _body;
1021     }
1022 
1023     /* ------------------------------------------------------------ */
1024     /**
1025      * @param force True if a new buffer will be forced to be used for content and the header buffer will not be used.
1026      */
1027     public void setForceContentBuffer(boolean force)
1028     {
1029         _forceContentBuffer=force;
1030     } 
1031     
1032     /* ------------------------------------------------------------ */
1033     /* ------------------------------------------------------------ */
1034     /* ------------------------------------------------------------ */
1035     public static abstract class EventHandler
1036     {
1037         public abstract void content(Buffer ref) throws IOException;
1038 
1039         public void headerComplete() throws IOException
1040         {
1041         }
1042 
1043         public void messageComplete(long contentLength) throws IOException
1044         {
1045         }
1046 
1047         /**
1048          * This is the method called by parser when a HTTP Header name and value is found
1049          */
1050         public void parsedHeader(Buffer name, Buffer value) throws IOException
1051         {
1052         }
1053 
1054         /**
1055          * This is the method called by parser when the HTTP request line is parsed
1056          */
1057         public abstract void startRequest(Buffer method, Buffer url, Buffer version)
1058                 throws IOException;
1059         
1060         /**
1061          * This is the method called by parser when the HTTP request line is parsed
1062          */
1063         public abstract void startResponse(Buffer version, int status, Buffer reason)
1064                 throws IOException;
1065     }
1066     
1067     
1068 
1069     /* ------------------------------------------------------------ */
1070     /* ------------------------------------------------------------ */
1071     /* ------------------------------------------------------------ */
1072     public static class Input extends ServletInputStream
1073     {
1074         protected HttpParser _parser;
1075         protected EndPoint _endp;
1076         protected long _maxIdleTime;
1077         protected Buffer _contentView;
1078         
1079         /* ------------------------------------------------------------ */
1080         public Input(HttpParser parser, long maxIdleTime)
1081         {
1082             _parser=parser;
1083             _endp=parser._endp;
1084             _maxIdleTime=maxIdleTime;
1085             _contentView=_parser._contentView;
1086             _parser._input=this;
1087         }
1088         
1089         /* ------------------------------------------------------------ */
1090         /*
1091          * @see java.io.InputStream#read()
1092          */
1093         public int read() throws IOException
1094         {
1095             int c=-1;
1096             if (blockForContent())
1097                 c= 0xff & _contentView.get();
1098             return c;
1099         }
1100         
1101         /* ------------------------------------------------------------ */
1102         /* 
1103          * @see java.io.InputStream#read(byte[], int, int)
1104          */
1105         public int read(byte[] b, int off, int len) throws IOException
1106         {
1107             int l=-1;
1108             if (blockForContent())
1109                 l= _contentView.get(b, off, len);
1110             return l;
1111         }
1112         
1113         /* ------------------------------------------------------------ */
1114         private boolean blockForContent() throws IOException
1115         {
1116             if (_contentView.length()>0)
1117                 return true;
1118             if (_parser.getState() <= HttpParser.STATE_END) 
1119                 return false;
1120             
1121             // Handle simple end points.
1122             if (_endp==null)
1123                 _parser.parseNext();
1124             
1125             // Handle blocking end points
1126             else if (_endp.isBlocking())
1127             {
1128                 try
1129                 {
1130                     _parser.parseNext();
1131 
1132                     // parse until some progress is made (or IOException thrown for timeout)
1133                     while(_contentView.length() == 0 && !_parser.isState(HttpParser.STATE_END))
1134                     {
1135                         // Try to get more _parser._content
1136                         _parser.parseNext();
1137                     }
1138                 }
1139                 catch(IOException e)
1140                 {
1141                     _endp.close();
1142                     throw e;
1143                 }
1144             }
1145             else // Handle non-blocking end point
1146             {
1147                 _parser.parseNext();
1148                 
1149                 // parse until some progress is made (or IOException thrown for timeout)
1150                 while(_contentView.length() == 0 && !_parser.isState(HttpParser.STATE_END))
1151                 {
1152                     if (!_endp.blockReadable(_maxIdleTime))
1153                     {
1154                         _endp.close();
1155                         throw new EofException("timeout");
1156                     }
1157 
1158                     // Try to get more _parser._content
1159                     _parser.parseNext();
1160                 }
1161             }
1162             
1163             return _contentView.length()>0; 
1164         }   
1165 
1166         /* ------------------------------------------------------------ */
1167         /* (non-Javadoc)
1168          * @see java.io.InputStream#available()
1169          */
1170         public int available() throws IOException
1171         {
1172             if (_contentView!=null && _contentView.length()>0)
1173                 return _contentView.length();
1174             if (!_endp.isBlocking())
1175                 _parser.parseNext();
1176             
1177             return _contentView==null?0:_contentView.length();
1178         }
1179     }
1180 
1181 
1182 
1183     
1184 }