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                         // Skip all data
973                         _buffer.clear();
974                         break;
975                     }
976                 }
977 
978                 length=_buffer.length();
979             }
980 
981             return progress;
982         }
983         catch(HttpException e)
984         {
985             _persistent=false;
986             _state=STATE_SEEKING_EOF;
987             throw e;
988         }
989     }
990 
991     /* ------------------------------------------------------------------------------- */
992     /** fill the buffers from the endpoint
993      *
994      */
995     protected int fill() throws IOException
996     {
997         // Do we have a buffer?
998         if (_buffer==null)
999         {
1000             _buffer=_header=getHeaderBuffer();
1001             _tok0=new View.CaseInsensitive(_buffer);
1002             _tok1=new View.CaseInsensitive(_buffer);
1003         }
1004 
1005         // Is there unconsumed content in body buffer
1006         if (_state>STATE_END && _buffer==_header && _header!=null && !_header.hasContent() && _body!=null && _body.hasContent())
1007         {
1008             _buffer=_body;
1009             return _buffer.length();
1010         }
1011 
1012         // Shall we switch to a body buffer?
1013         if (_buffer==_header && _state>STATE_END && _header.length()==0 && (_forceContentBuffer || (_contentLength-_contentPosition)>_header.capacity()) && (_body!=null||_buffers!=null))
1014         {
1015             if (_body==null)
1016                 _body=_buffers.getBuffer();
1017             _buffer=_body;
1018         }
1019 
1020         // Do we have somewhere to fill from?
1021         if (_endp != null )
1022         {
1023             // Shall we compact the body?
1024             if (_buffer==_body || _state>STATE_END)
1025             {
1026                 _buffer.compact();
1027             }
1028 
1029             // Are we full?
1030             if (_buffer.space() == 0)
1031             {
1032                 LOG.warn("Full {}",_buffer.toDetailString());
1033                 throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
1034             }
1035 
1036             try
1037             {
1038                 int filled = _endp.fill(_buffer);
1039                 return filled;
1040             }
1041             catch(IOException e)
1042             {
1043                 LOG.debug(e);
1044                 throw (e instanceof EofException) ? e:new EofException(e);
1045             }
1046         }
1047 
1048         return -1;
1049     }
1050 
1051     /* ------------------------------------------------------------------------------- */
1052     public void reset()
1053     {
1054         // reset state
1055         _contentView.setGetIndex(_contentView.putIndex());
1056         _state=_persistent?STATE_START:(_endp.isInputShutdown()?STATE_END:STATE_SEEKING_EOF);
1057         _contentLength=HttpTokens.UNKNOWN_CONTENT;
1058         _contentPosition=0;
1059         _length=0;
1060         _responseStatus=0;
1061 
1062         // Consume LF if CRLF
1063         if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer!=null && _buffer.hasContent() && _buffer.peek() == HttpTokens.LINE_FEED)
1064             _eol=_buffer.get();
1065 
1066         if (_body!=null && _body.hasContent())
1067         {
1068             // There is content in the body after the end of the request.
1069             // This is probably a pipelined header of the next request, so we need to
1070             // copy it to the header buffer.
1071             if (_header==null)
1072             {
1073                 _header=_buffers.getHeader();
1074             }
1075             else
1076             {
1077                 _header.setMarkIndex(-1);
1078                 _header.compact();
1079             }
1080             int take=_header.space();
1081             if (take>_body.length())
1082                 take=_body.length();
1083             _body.peek(_body.getIndex(),take);
1084             _body.skip(_header.put(_body.peek(_body.getIndex(),take)));
1085         }
1086 
1087         if (_header!=null)
1088         {
1089             _header.setMarkIndex(-1);
1090             _header.compact();
1091         }
1092         if (_body!=null)
1093             _body.setMarkIndex(-1);
1094 
1095         _buffer=_header;
1096         returnBuffers();
1097     }
1098 
1099 
1100     /* ------------------------------------------------------------------------------- */
1101     public void returnBuffers()
1102     {
1103         if (_body!=null && !_body.hasContent() && _body.markIndex()==-1 && _buffers!=null)
1104         {
1105             if (_buffer==_body)
1106                 _buffer=_header;
1107             if (_buffers!=null)
1108                 _buffers.returnBuffer(_body);
1109             _body=null;
1110         }
1111 
1112         if (_header!=null && !_header.hasContent() && _header.markIndex()==-1 && _buffers!=null)
1113         {
1114             if (_buffer==_header)
1115                 _buffer=null;
1116             _buffers.returnBuffer(_header);
1117             _header=null;
1118         }
1119     }
1120 
1121     /* ------------------------------------------------------------------------------- */
1122     public void setState(int state)
1123     {
1124         this._state=state;
1125         _contentLength=HttpTokens.UNKNOWN_CONTENT;
1126     }
1127 
1128     /* ------------------------------------------------------------------------------- */
1129     public String toString(Buffer buf)
1130     {
1131         return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
1132     }
1133 
1134     /* ------------------------------------------------------------------------------- */
1135     @Override
1136     public String toString()
1137     {
1138         return String.format("%s{s=%d,l=%d,c=%d}",
1139                 getClass().getSimpleName(),
1140                 _state,
1141                 _length,
1142                 _contentLength);
1143     }
1144 
1145     /* ------------------------------------------------------------ */
1146     public Buffer getHeaderBuffer()
1147     {
1148         if (_header == null)
1149         {
1150             _header=_buffers.getHeader();
1151         }
1152         return _header;
1153     }
1154 
1155     /* ------------------------------------------------------------ */
1156     public Buffer getBodyBuffer()
1157     {
1158         return _body;
1159     }
1160 
1161     /* ------------------------------------------------------------ */
1162     /**
1163      * @param force True if a new buffer will be forced to be used for content and the header buffer will not be used.
1164      */
1165     public void setForceContentBuffer(boolean force)
1166     {
1167         _forceContentBuffer=force;
1168     }
1169 
1170     /* ------------------------------------------------------------ */
1171     public Buffer blockForContent(long maxIdleTime) throws IOException
1172     {
1173         if (_contentView.length()>0)
1174             return _contentView;
1175 
1176         if (getState() <= STATE_END || isState(STATE_SEEKING_EOF))
1177             return null;
1178 
1179         try
1180         {
1181             parseNext();
1182 
1183             // parse until some progress is made (or IOException thrown for timeout)
1184             while(_contentView.length() == 0 && !(isState(HttpParser.STATE_END)||isState(HttpParser.STATE_SEEKING_EOF)) && _endp!=null && _endp.isOpen())
1185             {
1186                 if (!_endp.isBlocking())
1187                 {
1188                     if (parseNext()>0)
1189                         continue;
1190 
1191                     if (!_endp.blockReadable(maxIdleTime))
1192                     {
1193                         _endp.close();
1194                         throw new EofException("timeout");
1195                     }
1196                 }
1197 
1198                 parseNext();
1199             }
1200         }
1201         catch(IOException e)
1202         {
1203             // TODO is this needed?
1204             _endp.close();
1205             throw e;
1206         }
1207 
1208         return _contentView.length()>0?_contentView:null;
1209     }
1210 
1211     /* ------------------------------------------------------------ */
1212     /* (non-Javadoc)
1213      * @see java.io.InputStream#available()
1214      */
1215     public int available() throws IOException
1216     {
1217         if (_contentView!=null && _contentView.length()>0)
1218             return _contentView.length();
1219 
1220         if (_endp.isBlocking())
1221         {
1222             if (_state>0 && _endp instanceof StreamEndPoint)
1223                 return ((StreamEndPoint)_endp).getInputStream().available()>0?1:0;
1224 
1225             return 0;
1226         }
1227 
1228         parseNext();
1229         return _contentView==null?0:_contentView.length();
1230     }
1231 
1232     /* ------------------------------------------------------------ */
1233     /* ------------------------------------------------------------ */
1234     /* ------------------------------------------------------------ */
1235     public static abstract class EventHandler
1236     {
1237         public abstract void content(Buffer ref) throws IOException;
1238 
1239         public void headerComplete() throws IOException
1240         {
1241         }
1242 
1243         public void messageComplete(long contentLength) throws IOException
1244         {
1245         }
1246 
1247         /**
1248          * This is the method called by parser when a HTTP Header name and value is found
1249          */
1250         public void parsedHeader(Buffer name, Buffer value) throws IOException
1251         {
1252         }
1253 
1254         /**
1255          * This is the method called by parser when the HTTP request line is parsed
1256          */
1257         public abstract void startRequest(Buffer method, Buffer url, Buffer version)
1258                 throws IOException;
1259 
1260         /**
1261          * This is the method called by parser when the HTTP request line is parsed
1262          */
1263         public abstract void startResponse(Buffer version, int status, Buffer reason)
1264                 throws IOException;
1265 
1266         public void earlyEOF()
1267         {}
1268     }
1269 
1270 
1271 
1272 
1273 }