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