View Javadoc

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