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