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