View Javadoc

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