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 final View.CaseInsensitive _tok0; // Saved token: header name, request method or response version
67      private final 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          _tok0=new View.CaseInsensitive(_header);
97          _tok1=new View.CaseInsensitive(_header);
98      }
99  
100     /* ------------------------------------------------------------------------------- */
101     /**
102      * Constructor.
103      * @param buffers the buffers to use
104      * @param endp the endpoint
105      * @param handler the even handler
106      */
107     public HttpParser(Buffers buffers, EndPoint endp, EventHandler handler)
108     {
109         _buffers=buffers;
110         _endp=endp;
111         _handler=handler;
112         _tok0=new View.CaseInsensitive();
113         _tok1=new View.CaseInsensitive();
114     }
115 
116     /* ------------------------------------------------------------------------------- */
117     public long getContentLength()
118     {
119         return _contentLength;
120     }
121 
122     /* ------------------------------------------------------------ */
123     public long getContentRead()
124     {
125         return _contentPosition;
126     }
127 
128     /* ------------------------------------------------------------ */
129     /** Set if a HEAD response is expected
130      * @param head
131      */
132     public void setHeadResponse(boolean head)
133     {
134         _headResponse=head;
135     }
136 
137     /* ------------------------------------------------------------------------------- */
138     public int getState()
139     {
140         return _state;
141     }
142 
143     /* ------------------------------------------------------------------------------- */
144     public boolean inContentState()
145     {
146         return _state > 0;
147     }
148 
149     /* ------------------------------------------------------------------------------- */
150     public boolean inHeaderState()
151     {
152         return _state < 0;
153     }
154 
155     /* ------------------------------------------------------------------------------- */
156     public boolean isChunking()
157     {
158         return _contentLength==HttpTokens.CHUNKED_CONTENT;
159     }
160 
161     /* ------------------------------------------------------------ */
162     public boolean isIdle()
163     {
164         return isState(STATE_START);
165     }
166 
167     /* ------------------------------------------------------------ */
168     public boolean isComplete()
169     {
170         return isState(STATE_END);
171     }
172 
173     /* ------------------------------------------------------------ */
174     public boolean isMoreInBuffer()
175     throws IOException
176     {
177         return ( _header!=null && _header.hasContent() ||
178              _body!=null && _body.hasContent());
179     }
180 
181     /* ------------------------------------------------------------------------------- */
182     public boolean isState(int state)
183     {
184         return _state == state;
185     }
186 
187     /* ------------------------------------------------------------------------------- */
188     public boolean isPersistent()
189     {
190         return _persistent;
191     }
192 
193     /* ------------------------------------------------------------------------------- */
194     public void setPersistent(boolean persistent)
195     {
196         _persistent = persistent;
197         if (!_persistent &&(_state==STATE_END || _state==STATE_START))
198             _state=STATE_SEEKING_EOF;
199     }
200 
201     /* ------------------------------------------------------------------------------- */
202     /**
203      * Parse until {@link #STATE_END END} state.
204      * If the parser is already in the END state, then it is {@link #reset reset} and re-parsed.
205      * @throws IllegalStateException If the buffers have already been partially parsed.
206      */
207     public void parse() throws IOException
208     {
209         if (_state==STATE_END)
210             reset();
211         if (_state!=STATE_START)
212             throw new IllegalStateException("!START");
213 
214         // continue parsing
215         while (_state != STATE_END)
216             if (parseNext()<0)
217                 return;
218     }
219 
220     /* ------------------------------------------------------------------------------- */
221     /**
222      * Parse until END state.
223      * This method will parse any remaining content in the current buffer. It does not care about the
224      * {@link #getState current state} of the parser.
225      * @see #parse
226      * @see #parseNext
227      */
228     public boolean parseAvailable() throws IOException
229     {
230         boolean progress=parseNext()>0;
231 
232         // continue parsing
233         while (!isComplete() && _buffer!=null && _buffer.length()>0)
234         {
235             progress |= parseNext()>0;
236         }
237         return progress;
238     }
239 
240 
241     /* ------------------------------------------------------------------------------- */
242     /**
243      * Parse until next Event.
244      * @return an indication of progress <0 EOF, 0 no progress, >0 progress.
245      */
246     public int parseNext() throws IOException
247     {
248         try
249         {
250             int progress=0;
251 
252             if (_state == STATE_END)
253                 return 0;
254 
255             if (_buffer==null)
256                 _buffer=getHeaderBuffer();
257 
258 
259             if (_state == STATE_CONTENT && _contentPosition == _contentLength)
260             {
261                 _state=STATE_END;
262                 _handler.messageComplete(_contentPosition);
263                 returnBuffers();
264                 return 1;
265             }
266 
267             int length=_buffer.length();
268 
269             // Fill buffer if we can
270             if (length == 0)
271             {
272                 int filled=-1;
273                 IOException ex=null;
274                 try
275                 {
276                     filled=fill();
277                     LOG.debug("filled {}/{}",filled,_buffer.length());
278                 }
279                 catch(IOException e)
280                 {
281                     LOG.debug(this.toString(),e);
282                     ex=e;
283                 }
284 
285                 if (filled > 0 )
286                     progress++;
287                 else if (filled < 0 )
288                 {
289                     _persistent=false;
290 
291                     // do we have content to deliver?
292                     if (_state>STATE_END)
293                     {
294                         if (_buffer.length()>0 && !_headResponse)
295                         {
296                             Buffer chunk=_buffer.get(_buffer.length());
297                             _contentPosition += chunk.length();
298                             _contentView.update(chunk);
299                             _handler.content(chunk); // May recurse here
300                         }
301                     }
302 
303                     // was this unexpected?
304                     switch(_state)
305                     {
306                         case STATE_END:
307                         case STATE_SEEKING_EOF:
308                             _state=STATE_END;
309                             break;
310 
311                         case STATE_EOF_CONTENT:
312                             _state=STATE_END;
313                             _handler.messageComplete(_contentPosition);
314                             break;
315 
316                         default:
317                             _state=STATE_END;
318                             if (!_headResponse)
319                                 _handler.earlyEOF();
320                             _handler.messageComplete(_contentPosition);
321                     }
322 
323                     if (ex!=null)
324                         throw ex;
325 
326                     if (!isComplete() && !isIdle())
327                         throw new EofException();
328 
329                     returnBuffers();
330                     return -1;
331                 }
332                 length=_buffer.length();
333             }
334 
335 
336             // Handle header states
337             byte ch;
338             byte[] array=_buffer.array();
339             int last=_state;
340             while (_state<STATE_END && length-->0)
341             {
342                 if (last!=_state)
343                 {
344                     progress++;
345                     last=_state;
346                 }
347 
348                 ch=_buffer.get();
349 
350                 if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
351                 {
352                     _eol=HttpTokens.LINE_FEED;
353                     continue;
354                 }
355                 _eol=0;
356 
357                 switch (_state)
358                 {
359                     case STATE_START:
360                         _contentLength=HttpTokens.UNKNOWN_CONTENT;
361                         _cached=null;
362                         if (ch > HttpTokens.SPACE || ch<0)
363                         {
364                             _buffer.mark();
365                             _state=STATE_FIELD0;
366                         }
367                         break;
368 
369                     case STATE_FIELD0:
370                         if (ch == HttpTokens.SPACE)
371                         {
372                             _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1);
373                             _responseStatus=HttpVersions.CACHE.get(_tok0)==null?-1:0;
374                             _state=STATE_SPACE1;
375                             continue;
376                         }
377                         else if (ch < HttpTokens.SPACE && ch>=0)
378                         {
379                             throw new HttpException(HttpStatus.BAD_REQUEST_400);
380                         }
381                         break;
382 
383                     case STATE_SPACE1:
384                         if (ch > HttpTokens.SPACE || ch<0)
385                         {
386                             _buffer.mark();
387                             if (_responseStatus>=0)
388                             {
389                                 _state=STATE_STATUS;
390                                 _responseStatus=ch-'0';
391                             }
392                             else
393                                 _state=STATE_URI;
394                         }
395                         else if (ch < HttpTokens.SPACE)
396                         {
397                             throw new HttpException(HttpStatus.BAD_REQUEST_400);
398                         }
399                         break;
400 
401                     case STATE_STATUS:
402                         if (ch == HttpTokens.SPACE)
403                         {
404                             _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
405                             _state=STATE_SPACE2;
406                             continue;
407                         }
408                         else if (ch>='0' && ch<='9')
409                         {
410                             _responseStatus=_responseStatus*10+(ch-'0');
411                             continue;
412                         }
413                         else if (ch < HttpTokens.SPACE && ch>=0)
414                         {
415                             _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null);
416                             _eol=ch;
417                             _state=STATE_HEADER;
418                             _tok0.setPutIndex(_tok0.getIndex());
419                             _tok1.setPutIndex(_tok1.getIndex());
420                             _multiLineValue=null;
421                             continue;
422                         }
423                         // not a digit, so must be a URI
424                         _state=STATE_URI;
425                         _responseStatus=-1;
426                         break;
427 
428                     case STATE_URI:
429                         if (ch == HttpTokens.SPACE)
430                         {
431                             _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
432                             _state=STATE_SPACE2;
433                             continue;
434                         }
435                         else if (ch < HttpTokens.SPACE && ch>=0)
436                         {
437                             // HTTP/0.9
438                             _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer.sliceFromMark(), null);
439                             _persistent=false;
440                             _state=STATE_SEEKING_EOF;
441                             _handler.headerComplete();
442                             _handler.messageComplete(_contentPosition);
443                             returnBuffers();
444                             return 1;
445                         }
446                         break;
447 
448                     case STATE_SPACE2:
449                         if (ch > HttpTokens.SPACE || ch<0)
450                         {
451                             _buffer.mark();
452                             _state=STATE_FIELD2;
453                         }
454                         else if (ch < HttpTokens.SPACE)
455                         {
456                             if (_responseStatus>0)
457                             {
458                                 _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null);
459                                 _eol=ch;
460                                 _state=STATE_HEADER;
461                                 _tok0.setPutIndex(_tok0.getIndex());
462                                 _tok1.setPutIndex(_tok1.getIndex());
463                                 _multiLineValue=null;
464                             }
465                             else
466                             {
467                                 // HTTP/0.9
468                                 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null);
469                                 _persistent=false;
470                                 _state=STATE_SEEKING_EOF;
471                                 _handler.headerComplete();
472                                 _handler.messageComplete(_contentPosition);
473                                 returnBuffers();
474                                 return 1;
475                             }
476                         }
477                         break;
478 
479                     case STATE_FIELD2:
480                         if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
481                         {
482                             Buffer version;
483                             if (_responseStatus>0)
484                                 _handler.startResponse(version=HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark());
485                             else
486                                 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, version=HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
487                             _eol=ch;
488                             _persistent=HttpVersions.CACHE.getOrdinal(version)>=HttpVersions.HTTP_1_1_ORDINAL;
489                             _state=STATE_HEADER;
490                             _tok0.setPutIndex(_tok0.getIndex());
491                             _tok1.setPutIndex(_tok1.getIndex());
492                             _multiLineValue=null;
493                             continue;
494                         }
495                         break;
496 
497                     case STATE_HEADER:
498                         switch(ch)
499                         {
500                             case HttpTokens.COLON:
501                             case HttpTokens.SPACE:
502                             case HttpTokens.TAB:
503                             {
504                                 // header value without name - continuation?
505                                 _length=-1;
506                                 _state=STATE_HEADER_VALUE;
507                                 break;
508                             }
509 
510                             default:
511                             {
512                                 // handler last header if any
513                                 if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
514                                 {
515                                     Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
516                                     _cached=null;
517                                     Buffer value=_multiLineValue == null ? _tok1 : new ByteArrayBuffer(_multiLineValue);
518 
519                                     int ho=HttpHeaders.CACHE.getOrdinal(header);
520                                     if (ho >= 0)
521                                     {
522                                         int vo;
523 
524                                         switch (ho)
525                                         {
526                                             case HttpHeaders.CONTENT_LENGTH_ORDINAL:
527                                                 if (_contentLength != HttpTokens.CHUNKED_CONTENT && _responseStatus!=304 && _responseStatus!=204 && (_responseStatus<100 || _responseStatus>=200))
528                                                 {
529                                                     try
530                                                     {
531                                                         _contentLength=BufferUtil.toLong(value);
532                                                     }
533                                                     catch(NumberFormatException e)
534                                                     {
535                                                         LOG.ignore(e);
536                                                         throw new HttpException(HttpStatus.BAD_REQUEST_400);
537                                                     }
538                                                     if (_contentLength <= 0)
539                                                         _contentLength=HttpTokens.NO_CONTENT;
540                                                 }
541                                                 break;
542 
543                                             case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
544                                                 value=HttpHeaderValues.CACHE.lookup(value);
545                                                 vo=HttpHeaderValues.CACHE.getOrdinal(value);
546                                                 if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
547                                                     _contentLength=HttpTokens.CHUNKED_CONTENT;
548                                                 else
549                                                 {
550                                                     String c=value.toString(StringUtil.__ISO_8859_1);
551                                                     if (c.endsWith(HttpHeaderValues.CHUNKED))
552                                                         _contentLength=HttpTokens.CHUNKED_CONTENT;
553 
554                                                     else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
555                                                         throw new HttpException(400,null);
556                                                 }
557                                                 break;
558 
559                                             case HttpHeaders.CONNECTION_ORDINAL:
560                                                 switch(HttpHeaderValues.CACHE.getOrdinal(value))
561                                                 {
562                                                     case HttpHeaderValues.CLOSE_ORDINAL:
563                                                         _persistent=false;
564                                                         break;
565 
566                                                     case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
567                                                         _persistent=true;
568                                                         break;
569 
570                                                     case -1: // No match, may be multi valued
571                                                     {
572                                                         for (String v : value.toString().split(","))
573                                                         {
574                                                             switch(HttpHeaderValues.CACHE.getOrdinal(v.trim()))
575                                                             {
576                                                                 case HttpHeaderValues.CLOSE_ORDINAL:
577                                                                     _persistent=false;
578                                                                     break;
579 
580                                                                 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
581                                                                     _persistent=true;
582                                                                     break;
583                                                             }
584                                                         }
585                                                         break;
586                                                     }
587                                                 }
588                                         }
589                                     }
590 
591                                     _handler.parsedHeader(header, value);
592                                     _tok0.setPutIndex(_tok0.getIndex());
593                                     _tok1.setPutIndex(_tok1.getIndex());
594                                     _multiLineValue=null;
595                                 }
596                                 _buffer.setMarkIndex(-1);
597 
598 
599                                 // now handle ch
600                                 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
601                                 {
602                                     // work out the _content demarcation
603                                     if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
604                                     {
605                                         if (_responseStatus == 0  // request
606                                                 || _responseStatus == 304 // not-modified response
607                                                 || _responseStatus == 204 // no-content response
608                                                 || _responseStatus < 200) // 1xx response
609                                             _contentLength=HttpTokens.NO_CONTENT;
610                                         else
611                                             _contentLength=HttpTokens.EOF_CONTENT;
612                                     }
613 
614                                     _contentPosition=0;
615                                     _eol=ch;
616                                     if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
617                                         _eol=_buffer.get();
618 
619                                     // We convert _contentLength to an int for this switch statement because
620                                     // we don't care about the amount of data available just whether there is some.
621                                     switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
622                                     {
623                                         case HttpTokens.EOF_CONTENT:
624                                             _state=STATE_EOF_CONTENT;
625                                             _handler.headerComplete(); // May recurse here !
626                                             break;
627 
628                                         case HttpTokens.CHUNKED_CONTENT:
629                                             _state=STATE_CHUNKED_CONTENT;
630                                             _handler.headerComplete(); // May recurse here !
631                                             break;
632 
633                                         case HttpTokens.NO_CONTENT:
634                                             _handler.headerComplete();
635                                             _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF;
636                                             _handler.messageComplete(_contentPosition);
637                                             returnBuffers();
638                                             return 1;
639 
640                                         default:
641                                             _state=STATE_CONTENT;
642                                             _handler.headerComplete(); // May recurse here !
643                                             break;
644                                     }
645                                     return 1;
646                                 }
647                                 else
648                                 {
649                                     // New header
650                                     _length=1;
651                                     _buffer.mark();
652                                     _state=STATE_HEADER_NAME;
653 
654                                     // try cached name!
655                                     if (array!=null)
656                                     {
657                                         _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1);
658 
659                                         if (_cached!=null)
660                                         {
661                                             _length=_cached.length();
662                                             _buffer.setGetIndex(_buffer.markIndex()+_length);
663                                             length=_buffer.length();
664                                         }
665                                     }
666                                 }
667                             }
668                         }
669 
670                         break;
671 
672                     case STATE_HEADER_NAME:
673                         switch(ch)
674                         {
675                             case HttpTokens.CARRIAGE_RETURN:
676                             case HttpTokens.LINE_FEED:
677                                 if (_length > 0)
678                                     _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
679                                 _eol=ch;
680                                 _state=STATE_HEADER;
681                                 break;
682                             case HttpTokens.COLON:
683                                 if (_length > 0 && _cached==null)
684                                     _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
685                                 _length=-1;
686                                 _state=STATE_HEADER_VALUE;
687                                 break;
688                             case HttpTokens.SPACE:
689                             case HttpTokens.TAB:
690                                 break;
691                             default:
692                             {
693                                 _cached=null;
694                                 if (_length == -1)
695                                     _buffer.mark();
696                                 _length=_buffer.getIndex() - _buffer.markIndex();
697                                 _state=STATE_HEADER_IN_NAME;
698                             }
699                         }
700 
701                         break;
702 
703                     case STATE_HEADER_IN_NAME:
704                         switch(ch)
705                         {
706                             case HttpTokens.CARRIAGE_RETURN:
707                             case HttpTokens.LINE_FEED:
708                                 if (_length > 0)
709                                     _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
710                                 _eol=ch;
711                                 _state=STATE_HEADER;
712                                 break;
713                             case HttpTokens.COLON:
714                                 if (_length > 0 && _cached==null)
715                                     _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
716                                 _length=-1;
717                                 _state=STATE_HEADER_VALUE;
718                                 break;
719                             case HttpTokens.SPACE:
720                             case HttpTokens.TAB:
721                                 _state=STATE_HEADER_NAME;
722                                 break;
723                             default:
724                             {
725                                 _cached=null;
726                                 _length++;
727                             }
728                         }
729                         break;
730 
731                     case STATE_HEADER_VALUE:
732                         switch(ch)
733                         {
734                             case HttpTokens.CARRIAGE_RETURN:
735                             case HttpTokens.LINE_FEED:
736                                 if (_length > 0)
737                                 {
738                                     if (_tok1.length() == 0)
739                                         _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
740                                     else
741                                     {
742                                         // Continuation line!
743                                         if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1);
744                                         _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
745                                         _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1);
746                                     }
747                                 }
748                                 _eol=ch;
749                                 _state=STATE_HEADER;
750                                 break;
751                             case HttpTokens.SPACE:
752                             case HttpTokens.TAB:
753                                 break;
754                             default:
755                             {
756                                 if (_length == -1)
757                                     _buffer.mark();
758                                 _length=_buffer.getIndex() - _buffer.markIndex();
759                                 _state=STATE_HEADER_IN_VALUE;
760                             }
761                         }
762                         break;
763 
764                     case STATE_HEADER_IN_VALUE:
765                         switch(ch)
766                         {
767                             case HttpTokens.CARRIAGE_RETURN:
768                             case HttpTokens.LINE_FEED:
769                                 if (_length > 0)
770                                 {
771                                     if (_tok1.length() == 0)
772                                         _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
773                                     else
774                                     {
775                                         // Continuation line!
776                                         if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1);
777                                         _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
778                                         _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1);
779                                     }
780                                 }
781                                 _eol=ch;
782                                 _state=STATE_HEADER;
783                                 break;
784                             case HttpTokens.SPACE:
785                             case HttpTokens.TAB:
786                                 _state=STATE_HEADER_VALUE;
787                                 break;
788                             default:
789                                 _length++;
790                         }
791                         break;
792                 }
793             } // end of HEADER states loop
794 
795             // ==========================
796 
797             // Handle HEAD response
798             if (_responseStatus>0 && _headResponse)
799             {
800                 _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF;
801                 _handler.messageComplete(_contentLength);
802             }
803 
804 
805             // ==========================
806 
807             // Handle _content
808             length=_buffer.length();
809             Buffer chunk;
810             last=_state;
811             while (_state > STATE_END && length > 0)
812             {
813                 if (last!=_state)
814                 {
815                     progress++;
816                     last=_state;
817                 }
818 
819                 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
820                 {
821                     _eol=_buffer.get();
822                     length=_buffer.length();
823                     continue;
824                 }
825                 _eol=0;
826                 switch (_state)
827                 {
828                     case STATE_EOF_CONTENT:
829                         chunk=_buffer.get(_buffer.length());
830                         _contentPosition += chunk.length();
831                         _contentView.update(chunk);
832                         _handler.content(chunk); // May recurse here
833                         // TODO adjust the _buffer to keep unconsumed content
834                         return 1;
835 
836                     case STATE_CONTENT:
837                     {
838                         long remaining=_contentLength - _contentPosition;
839                         if (remaining == 0)
840                         {
841                             _state=_persistent?STATE_END:STATE_SEEKING_EOF;
842                             _handler.messageComplete(_contentPosition);
843                             returnBuffers();
844                             return 1;
845                         }
846 
847                         if (length > remaining)
848                         {
849                             // We can cast reamining to an int as we know that it is smaller than
850                             // or equal to length which is already an int.
851                             length=(int)remaining;
852                         }
853 
854                         chunk=_buffer.get(length);
855                         _contentPosition += chunk.length();
856                         _contentView.update(chunk);
857                         _handler.content(chunk); // May recurse here
858 
859                         if(_contentPosition == _contentLength)
860                         {
861                             _state=_persistent?STATE_END:STATE_SEEKING_EOF;
862                             _handler.messageComplete(_contentPosition);
863                             returnBuffers();
864                         }
865                         // TODO adjust the _buffer to keep unconsumed content
866                         return 1;
867                     }
868 
869                     case STATE_CHUNKED_CONTENT:
870                     {
871                         ch=_buffer.peek();
872                         if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
873                             _eol=_buffer.get();
874                         else if (ch <= HttpTokens.SPACE)
875                             _buffer.get();
876                         else
877                         {
878                             _chunkLength=0;
879                             _chunkPosition=0;
880                             _state=STATE_CHUNK_SIZE;
881                         }
882                         break;
883                     }
884 
885                     case STATE_CHUNK_SIZE:
886                     {
887                         ch=_buffer.get();
888                         if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
889                         {
890                             _eol=ch;
891 
892                             if (_chunkLength == 0)
893                             {
894                                 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
895                                     _eol=_buffer.get();
896                                 _state=_persistent?STATE_END:STATE_SEEKING_EOF;
897                                 _handler.messageComplete(_contentPosition);
898                                 returnBuffers();
899                                 return 1;
900                             }
901                             else
902                                 _state=STATE_CHUNK;
903                         }
904                         else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
905                             _state=STATE_CHUNK_PARAMS;
906                         else if (ch >= '0' && ch <= '9')
907                             _chunkLength=_chunkLength * 16 + (ch - '0');
908                         else if (ch >= 'a' && ch <= 'f')
909                             _chunkLength=_chunkLength * 16 + (10 + ch - 'a');
910                         else if (ch >= 'A' && ch <= 'F')
911                             _chunkLength=_chunkLength * 16 + (10 + ch - 'A');
912                         else
913                             throw new IOException("bad chunk char: " + ch);
914                         break;
915                     }
916 
917                     case STATE_CHUNK_PARAMS:
918                     {
919                         ch=_buffer.get();
920                         if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
921                         {
922                             _eol=ch;
923                             if (_chunkLength == 0)
924                             {
925                                 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
926                                     _eol=_buffer.get();
927                                 _state=_persistent?STATE_END:STATE_SEEKING_EOF;
928                                 _handler.messageComplete(_contentPosition);
929                                 returnBuffers();
930                                 return 1;
931                             }
932                             else
933                                 _state=STATE_CHUNK;
934                         }
935                         break;
936                     }
937 
938                     case STATE_CHUNK:
939                     {
940                         int remaining=_chunkLength - _chunkPosition;
941                         if (remaining == 0)
942                         {
943                             _state=STATE_CHUNKED_CONTENT;
944                             break;
945                         }
946                         else if (length > remaining)
947                             length=remaining;
948                         chunk=_buffer.get(length);
949                         _contentPosition += chunk.length();
950                         _chunkPosition += chunk.length();
951                         _contentView.update(chunk);
952                         _handler.content(chunk); // May recurse here
953                         // TODO adjust the _buffer to keep unconsumed content
954                         return 1;
955                     }
956 
957                     case STATE_SEEKING_EOF:
958                     {                        
959                         // Close if there is more data than CRLF
960                         if (_buffer.length()>2)
961                         {
962                             _state=STATE_END;
963                             _endp.close();
964                         }
965                         else  
966                         {
967                             // or if the data is not white space
968                             while (_buffer.length()>0)
969                                 if (!Character.isWhitespace(_buffer.get()))
970                                 {
971                                     _state=STATE_END;
972                                     _endp.close();
973                                     _buffer.clear();
974                                 }
975                         }
976                         
977                         _buffer.clear();
978                         break;
979                     }
980                 }
981 
982                 length=_buffer.length();
983             }
984 
985             return progress;
986         }
987         catch(HttpException e)
988         {
989             _persistent=false;
990             _state=STATE_SEEKING_EOF;
991             throw e;
992         }
993     }
994 
995     /* ------------------------------------------------------------------------------- */
996     /** fill the buffers from the endpoint
997      *
998      */
999     protected int fill() throws IOException
1000     {
1001         // Do we have a buffer?
1002         if (_buffer==null)
1003             _buffer=getHeaderBuffer();
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                 getHeaderBuffer();
1073             else
1074             {
1075                 _header.setMarkIndex(-1);
1076                 _header.compact();
1077             }
1078             int take=_header.space();
1079             if (take>_body.length())
1080                 take=_body.length();
1081             _body.peek(_body.getIndex(),take);
1082             _body.skip(_header.put(_body.peek(_body.getIndex(),take)));
1083         }
1084 
1085         if (_header!=null)
1086         {
1087             _header.setMarkIndex(-1);
1088             _header.compact();
1089         }
1090         if (_body!=null)
1091             _body.setMarkIndex(-1);
1092 
1093         _buffer=_header;
1094         returnBuffers();
1095     }
1096 
1097 
1098     /* ------------------------------------------------------------------------------- */
1099     public void returnBuffers()
1100     {
1101         if (_body!=null && !_body.hasContent() && _body.markIndex()==-1 && _buffers!=null)
1102         {
1103             if (_buffer==_body)
1104                 _buffer=_header;
1105             if (_buffers!=null)
1106                 _buffers.returnBuffer(_body);
1107             _body=null;
1108         }
1109 
1110         if (_header!=null && !_header.hasContent() && _header.markIndex()==-1 && _buffers!=null)
1111         {
1112             if (_buffer==_header)
1113                 _buffer=null;
1114             _buffers.returnBuffer(_header);
1115             _header=null;
1116         }
1117     }
1118 
1119     /* ------------------------------------------------------------------------------- */
1120     public void setState(int state)
1121     {
1122         this._state=state;
1123         _contentLength=HttpTokens.UNKNOWN_CONTENT;
1124     }
1125 
1126     /* ------------------------------------------------------------------------------- */
1127     public String toString(Buffer buf)
1128     {
1129         return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
1130     }
1131 
1132     /* ------------------------------------------------------------------------------- */
1133     @Override
1134     public String toString()
1135     {
1136         return String.format("%s{s=%d,l=%d,c=%d}",
1137                 getClass().getSimpleName(),
1138                 _state,
1139                 _length,
1140                 _contentLength);
1141     }
1142 
1143     /* ------------------------------------------------------------ */
1144     public Buffer getHeaderBuffer()
1145     {
1146         if (_header == null)
1147         {
1148             _header=_buffers.getHeader();
1149             _tok0.update(_header);
1150             _tok1.update(_header);
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 }