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