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