View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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 as long as there is
229      * no unconsumed content. It does not care about the {@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 && !_contentView.hasContent())
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)
354                 {
355                     if (ch == HttpTokens.LINE_FEED)
356                     {
357                         _eol=HttpTokens.LINE_FEED;
358                         continue;
359                     }
360                     throw new HttpException(HttpStatus.BAD_REQUEST_400);
361                 }
362                 _eol=0;
363 
364                 switch (_state)
365                 {
366                     case STATE_START:
367                         _contentLength=HttpTokens.UNKNOWN_CONTENT;
368                         _cached=null;
369                         if (ch > HttpTokens.SPACE || ch<0)
370                         {
371                             _buffer.mark();
372                             _state=STATE_FIELD0;
373                         }
374                         break;
375 
376                     case STATE_FIELD0:
377                         if (ch == HttpTokens.SPACE)
378                         {
379                             _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1);
380                             _responseStatus=HttpVersions.CACHE.get(_tok0)==null?-1:0;
381                             _state=STATE_SPACE1;
382                             continue;
383                         }
384                         else if (ch < HttpTokens.SPACE && ch>=0)
385                         {
386                             throw new HttpException(HttpStatus.BAD_REQUEST_400);
387                         }
388                         break;
389 
390                     case STATE_SPACE1:
391                         if (ch > HttpTokens.SPACE || ch<0)
392                         {
393                             _buffer.mark();
394                             if (_responseStatus>=0)
395                             {
396                                 _state=STATE_STATUS;
397                                 _responseStatus=ch-'0';
398                             }
399                             else
400                                 _state=STATE_URI;
401                         }
402                         else if (ch < HttpTokens.SPACE)
403                         {
404                             throw new HttpException(HttpStatus.BAD_REQUEST_400);
405                         }
406                         break;
407 
408                     case STATE_STATUS:
409                         if (ch == HttpTokens.SPACE)
410                         {
411                             _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
412                             _state=STATE_SPACE2;
413                             continue;
414                         }
415                         else if (ch>='0' && ch<='9')
416                         {
417                             _responseStatus=_responseStatus*10+(ch-'0');
418                             continue;
419                         }
420                         else if (ch < HttpTokens.SPACE && ch>=0)
421                         {
422                             _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null);
423                             _eol=ch;
424                             _state=STATE_HEADER;
425                             _tok0.setPutIndex(_tok0.getIndex());
426                             _tok1.setPutIndex(_tok1.getIndex());
427                             _multiLineValue=null;
428                             continue;
429                         }
430                         // not a digit, so must be a URI
431                         _state=STATE_URI;
432                         _responseStatus=-1;
433                         break;
434 
435                     case STATE_URI:
436                         if (ch == HttpTokens.SPACE)
437                         {
438                             _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
439                             _state=STATE_SPACE2;
440                             continue;
441                         }
442                         else if (ch < HttpTokens.SPACE && ch>=0)
443                         {
444                             // HTTP/0.9
445                             _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer.sliceFromMark(), null);
446                             _persistent=false;
447                             _state=STATE_SEEKING_EOF;
448                             _handler.headerComplete();
449                             _handler.messageComplete(_contentPosition);
450                             return 1;
451                         }
452                         break;
453 
454                     case STATE_SPACE2:
455                         if (ch > HttpTokens.SPACE || ch<0)
456                         {
457                             _buffer.mark();
458                             _state=STATE_FIELD2;
459                         }
460                         else if (ch < HttpTokens.SPACE)
461                         {
462                             if (_responseStatus>0)
463                             {
464                                 _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null);
465                                 _eol=ch;
466                                 _state=STATE_HEADER;
467                                 _tok0.setPutIndex(_tok0.getIndex());
468                                 _tok1.setPutIndex(_tok1.getIndex());
469                                 _multiLineValue=null;
470                             }
471                             else
472                             {
473                                 // HTTP/0.9
474                                 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null);
475                                 _persistent=false;
476                                 _state=STATE_SEEKING_EOF;
477                                 _handler.headerComplete();
478                                 _handler.messageComplete(_contentPosition);
479                                 return 1;
480                             }
481                         }
482                         break;
483 
484                     case STATE_FIELD2:
485                         if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
486                         {
487                             Buffer version;
488                             if (_responseStatus>0)
489                                 _handler.startResponse(version=HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark());
490                             else
491                                 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, version=HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
492                             _eol=ch;
493                             _persistent=HttpVersions.CACHE.getOrdinal(version)>=HttpVersions.HTTP_1_1_ORDINAL;
494                             _state=STATE_HEADER;
495                             _tok0.setPutIndex(_tok0.getIndex());
496                             _tok1.setPutIndex(_tok1.getIndex());
497                             _multiLineValue=null;
498                             continue;
499                         }
500                         break;
501 
502                     case STATE_HEADER:
503                         switch(ch)
504                         {
505                             case HttpTokens.COLON:
506                             case HttpTokens.SPACE:
507                             case HttpTokens.TAB:
508                             {
509                                 // header value without name - continuation?
510                                 _length=-1;
511                                 _state=STATE_HEADER_VALUE;
512                                 break;
513                             }
514 
515                             default:
516                             {
517                                 // handler last header if any
518                                 if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
519                                 {
520                                     Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
521                                     _cached=null;
522                                     Buffer value=_multiLineValue == null ? _tok1 : new ByteArrayBuffer(_multiLineValue);
523 
524                                     int ho=HttpHeaders.CACHE.getOrdinal(header);
525                                     if (ho >= 0)
526                                     {
527                                         int vo;
528 
529                                         switch (ho)
530                                         {
531                                             case HttpHeaders.CONTENT_LENGTH_ORDINAL:
532                                                 if (_contentLength != HttpTokens.CHUNKED_CONTENT )
533                                                 {
534                                                     try
535                                                     {
536                                                         _contentLength=BufferUtil.toLong(value);
537                                                     }
538                                                     catch(NumberFormatException e)
539                                                     {
540                                                         LOG.ignore(e);
541                                                         throw new HttpException(HttpStatus.BAD_REQUEST_400);
542                                                     }
543                                                     if (_contentLength <= 0)
544                                                         _contentLength=HttpTokens.NO_CONTENT;
545                                                 }
546                                                 break;
547 
548                                             case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
549                                                 value=HttpHeaderValues.CACHE.lookup(value);
550                                                 vo=HttpHeaderValues.CACHE.getOrdinal(value);
551                                                 if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
552                                                     _contentLength=HttpTokens.CHUNKED_CONTENT;
553                                                 else
554                                                 {
555                                                     String c=value.toString(StringUtil.__ISO_8859_1);
556                                                     if (c.endsWith(HttpHeaderValues.CHUNKED))
557                                                         _contentLength=HttpTokens.CHUNKED_CONTENT;
558 
559                                                     else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
560                                                         throw new HttpException(400,null);
561                                                 }
562                                                 break;
563 
564                                             case HttpHeaders.CONNECTION_ORDINAL:
565                                                 switch(HttpHeaderValues.CACHE.getOrdinal(value))
566                                                 {
567                                                     case HttpHeaderValues.CLOSE_ORDINAL:
568                                                         _persistent=false;
569                                                         break;
570 
571                                                     case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
572                                                         _persistent=true;
573                                                         break;
574 
575                                                     case -1: // No match, may be multi valued
576                                                     {
577                                                         for (String v : value.toString().split(","))
578                                                         {
579                                                             switch(HttpHeaderValues.CACHE.getOrdinal(v.trim()))
580                                                             {
581                                                                 case HttpHeaderValues.CLOSE_ORDINAL:
582                                                                     _persistent=false;
583                                                                     break;
584 
585                                                                 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
586                                                                     _persistent=true;
587                                                                     break;
588                                                             }
589                                                         }
590                                                         break;
591                                                     }
592                                                 }
593                                         }
594                                     }
595 
596                                     _handler.parsedHeader(header, value);
597                                     _tok0.setPutIndex(_tok0.getIndex());
598                                     _tok1.setPutIndex(_tok1.getIndex());
599                                     _multiLineValue=null;
600                                 }
601                                 _buffer.setMarkIndex(-1);
602 
603                                 // now handle ch
604                                 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
605                                 {
606                                     // is it a response that cannot have a body?
607                                     if (_responseStatus > 0  && // response  
608                                        (_responseStatus == 304  || // not-modified response
609                                         _responseStatus == 204 || // no-content response
610                                         _responseStatus < 200)) // 1xx response
611                                         _contentLength=HttpTokens.NO_CONTENT; // ignore any other headers set
612                                     // else if we don't know framing
613                                     else if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
614                                     {
615                                         if (_responseStatus == 0  // request
616                                                 || _responseStatus == 304 // not-modified response
617                                                 || _responseStatus == 204 // no-content response
618                                                 || _responseStatus < 200) // 1xx response
619                                             _contentLength=HttpTokens.NO_CONTENT;
620                                         else
621                                             _contentLength=HttpTokens.EOF_CONTENT;
622                                     }
623 
624                                     _contentPosition=0;
625                                     _eol=ch;
626                                     if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
627                                         _eol=_buffer.get();
628 
629                                     // We convert _contentLength to an int for this switch statement because
630                                     // we don't care about the amount of data available just whether there is some.
631                                     switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
632                                     {
633                                         case HttpTokens.EOF_CONTENT:
634                                             _state=STATE_EOF_CONTENT;
635                                             _handler.headerComplete(); // May recurse here !
636                                             break;
637 
638                                         case HttpTokens.CHUNKED_CONTENT:
639                                             _state=STATE_CHUNKED_CONTENT;
640                                             _handler.headerComplete(); // May recurse here !
641                                             break;
642 
643                                         case HttpTokens.NO_CONTENT:
644                                             _handler.headerComplete();
645                                             _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF;
646                                             _handler.messageComplete(_contentPosition);
647                                             return 1;
648 
649                                         default:
650                                             _state=STATE_CONTENT;
651                                             _handler.headerComplete(); // May recurse here !
652                                             break;
653                                     }
654                                     return 1;
655                                 }
656                                 else
657                                 {
658                                     // New header
659                                     _length=1;
660                                     _buffer.mark();
661                                     _state=STATE_HEADER_NAME;
662 
663                                     // try cached name!
664                                     if (array!=null)
665                                     {
666                                         _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1);
667 
668                                         if (_cached!=null)
669                                         {
670                                             _length=_cached.length();
671                                             _buffer.setGetIndex(_buffer.markIndex()+_length);
672                                             length=_buffer.length();
673                                         }
674                                     }
675                                 }
676                             }
677                         }
678 
679                         break;
680 
681                     case STATE_HEADER_NAME:
682                         switch(ch)
683                         {
684                             case HttpTokens.CARRIAGE_RETURN:
685                             case HttpTokens.LINE_FEED:
686                                 if (_length > 0)
687                                     _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
688                                 _eol=ch;
689                                 _state=STATE_HEADER;
690                                 break;
691                             case HttpTokens.COLON:
692                                 if (_length > 0 && _cached==null)
693                                     _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
694                                 _length=-1;
695                                 _state=STATE_HEADER_VALUE;
696                                 break;
697                             case HttpTokens.SPACE:
698                             case HttpTokens.TAB:
699                                 break;
700                             default:
701                             {
702                                 _cached=null;
703                                 if (_length == -1)
704                                     _buffer.mark();
705                                 _length=_buffer.getIndex() - _buffer.markIndex();
706                                 _state=STATE_HEADER_IN_NAME;
707                             }
708                         }
709 
710                         break;
711 
712                     case STATE_HEADER_IN_NAME:
713                         switch(ch)
714                         {
715                             case HttpTokens.CARRIAGE_RETURN:
716                             case HttpTokens.LINE_FEED:
717                                 if (_length > 0)
718                                     _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
719                                 _eol=ch;
720                                 _state=STATE_HEADER;
721                                 break;
722                             case HttpTokens.COLON:
723                                 if (_length > 0 && _cached==null)
724                                     _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
725                                 _length=-1;
726                                 _state=STATE_HEADER_VALUE;
727                                 break;
728                             case HttpTokens.SPACE:
729                             case HttpTokens.TAB:
730                                 _state=STATE_HEADER_NAME;
731                                 break;
732                             default:
733                             {
734                                 _cached=null;
735                                 _length++;
736                             }
737                         }
738                         break;
739 
740                     case STATE_HEADER_VALUE:
741                         switch(ch)
742                         {
743                             case HttpTokens.CARRIAGE_RETURN:
744                             case HttpTokens.LINE_FEED:
745                                 if (_length > 0)
746                                 {
747                                     if (_tok1.length() == 0)
748                                         _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
749                                     else
750                                     {
751                                         // Continuation line!
752                                         if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1);
753                                         _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
754                                         _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1);
755                                     }
756                                 }
757                                 _eol=ch;
758                                 _state=STATE_HEADER;
759                                 break;
760                             case HttpTokens.SPACE:
761                             case HttpTokens.TAB:
762                                 break;
763                             default:
764                             {
765                                 if (_length == -1)
766                                     _buffer.mark();
767                                 _length=_buffer.getIndex() - _buffer.markIndex();
768                                 _state=STATE_HEADER_IN_VALUE;
769                             }
770                         }
771                         break;
772 
773                     case STATE_HEADER_IN_VALUE:
774                         switch(ch)
775                         {
776                             case HttpTokens.CARRIAGE_RETURN:
777                             case HttpTokens.LINE_FEED:
778                                 if (_length > 0)
779                                 {
780                                     if (_tok1.length() == 0)
781                                         _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
782                                     else
783                                     {
784                                         // Continuation line!
785                                         if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1);
786                                         _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
787                                         _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1);
788                                     }
789                                 }
790                                 _eol=ch;
791                                 _state=STATE_HEADER;
792                                 break;
793                             case HttpTokens.SPACE:
794                             case HttpTokens.TAB:
795                                 _state=STATE_HEADER_VALUE;
796                                 break;
797                             default:
798                                 _length++;
799                         }
800                         break;
801                 }
802             } // end of HEADER states loop
803 
804             // ==========================
805 
806             // Handle HEAD response
807             if (_responseStatus>0 && _headResponse)
808             {
809                 _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF;
810                 _handler.messageComplete(_contentLength);
811             }
812 
813 
814             // ==========================
815 
816             // Handle _content
817             length=_buffer.length();
818             Buffer chunk;
819             last=_state;
820             while (_state > STATE_END && length > 0)
821             {
822                 if (last!=_state)
823                 {
824                     progress++;
825                     last=_state;
826                 }
827 
828                 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
829                 {
830                     _eol=_buffer.get();
831                     length=_buffer.length();
832                     continue;
833                 }
834                 _eol=0;
835                 switch (_state)
836                 {
837                     case STATE_EOF_CONTENT:
838                         chunk=_buffer.get(_buffer.length());
839                         _contentPosition += chunk.length();
840                         _contentView.update(chunk);
841                         _handler.content(chunk); // May recurse here
842                         // TODO adjust the _buffer to keep unconsumed content
843                         return 1;
844 
845                     case STATE_CONTENT:
846                     {
847                         long remaining=_contentLength - _contentPosition;
848                         if (remaining == 0)
849                         {
850                             _state=_persistent?STATE_END:STATE_SEEKING_EOF;
851                             _handler.messageComplete(_contentPosition);
852                             return 1;
853                         }
854 
855                         if (length > remaining)
856                         {
857                             // We can cast reamining to an int as we know that it is smaller than
858                             // or equal to length which is already an int.
859                             length=(int)remaining;
860                         }
861 
862                         chunk=_buffer.get(length);
863                         _contentPosition += chunk.length();
864                         _contentView.update(chunk);
865                         _handler.content(chunk); // May recurse here
866 
867                         if(_contentPosition == _contentLength)
868                         {
869                             _state=_persistent?STATE_END:STATE_SEEKING_EOF;
870                             _handler.messageComplete(_contentPosition);
871                         }
872                         // TODO adjust the _buffer to keep unconsumed content
873                         return 1;
874                     }
875 
876                     case STATE_CHUNKED_CONTENT:
877                     {
878                         ch=_buffer.peek();
879                         if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
880                             _eol=_buffer.get();
881                         else if (ch <= HttpTokens.SPACE)
882                             _buffer.get();
883                         else
884                         {
885                             _chunkLength=0;
886                             _chunkPosition=0;
887                             _state=STATE_CHUNK_SIZE;
888                         }
889                         break;
890                     }
891 
892                     case STATE_CHUNK_SIZE:
893                     {
894                         ch=_buffer.get();
895                         if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
896                         {
897                             _eol=ch;
898 
899                             if (_chunkLength == 0)
900                             {
901                                 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
902                                     _eol=_buffer.get();
903                                 _state=_persistent?STATE_END:STATE_SEEKING_EOF;
904                                 _handler.messageComplete(_contentPosition);
905                                 return 1;
906                             }
907                             else
908                                 _state=STATE_CHUNK;
909                         }
910                         else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
911                             _state=STATE_CHUNK_PARAMS;
912                         else if (ch >= '0' && ch <= '9')
913                             _chunkLength=_chunkLength * 16 + (ch - '0');
914                         else if (ch >= 'a' && ch <= 'f')
915                             _chunkLength=_chunkLength * 16 + (10 + ch - 'a');
916                         else if (ch >= 'A' && ch <= 'F')
917                             _chunkLength=_chunkLength * 16 + (10 + ch - 'A');
918                         else
919                             throw new IOException("bad chunk char: " + ch);
920                         break;
921                     }
922 
923                     case STATE_CHUNK_PARAMS:
924                     {
925                         ch=_buffer.get();
926                         if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
927                         {
928                             _eol=ch;
929                             if (_chunkLength == 0)
930                             {
931                                 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
932                                     _eol=_buffer.get();
933                                 _state=_persistent?STATE_END:STATE_SEEKING_EOF;
934                                 _handler.messageComplete(_contentPosition);
935                                 return 1;
936                             }
937                             else
938                                 _state=STATE_CHUNK;
939                         }
940                         break;
941                     }
942 
943                     case STATE_CHUNK:
944                     {
945                         int remaining=_chunkLength - _chunkPosition;
946                         if (remaining == 0)
947                         {
948                             _state=STATE_CHUNKED_CONTENT;
949                             break;
950                         }
951                         else if (length > remaining)
952                             length=remaining;
953                         chunk=_buffer.get(length);
954                         _contentPosition += chunk.length();
955                         _chunkPosition += chunk.length();
956                         _contentView.update(chunk);
957                         _handler.content(chunk); // May recurse here
958                         // TODO adjust the _buffer to keep unconsumed content
959                         return 1;
960                     }
961 
962                     case STATE_SEEKING_EOF:
963                     {                        
964                         // Close if there is more data than CRLF
965                         if (_buffer.length()>2)
966                         {
967                             _state=STATE_END;
968                             _endp.close();
969                         }
970                         else  
971                         {
972                             // or if the data is not white space
973                             while (_buffer.length()>0)
974                                 if (!Character.isWhitespace(_buffer.get()))
975                                 {
976                                     _state=STATE_END;
977                                     _endp.close();
978                                     _buffer.clear();
979                                 }
980                         }
981                         
982                         _buffer.clear();
983                         break;
984                     }
985                 }
986 
987                 length=_buffer.length();
988             }
989 
990             return progress;
991         }
992         catch(HttpException e)
993         {
994             _persistent=false;
995             _state=STATE_SEEKING_EOF;
996             throw e;
997         }
998     }
999 
1000     /* ------------------------------------------------------------------------------- */
1001     /** fill the buffers from the endpoint
1002      *
1003      */
1004     protected int fill() throws IOException
1005     {
1006         // Do we have a buffer?
1007         if (_buffer==null)
1008             _buffer=getHeaderBuffer();
1009 
1010         // Is there unconsumed content in body buffer
1011         if (_state>STATE_END && _buffer==_header && _header!=null && !_header.hasContent() && _body!=null && _body.hasContent())
1012         {
1013             _buffer=_body;
1014             return _buffer.length();
1015         }
1016 
1017         // Shall we switch to a body buffer?
1018         if (_buffer==_header && _state>STATE_END && _header.length()==0 && (_forceContentBuffer || (_contentLength-_contentPosition)>_header.capacity()) && (_body!=null||_buffers!=null))
1019         {
1020             if (_body==null)
1021                 _body=_buffers.getBuffer();
1022             _buffer=_body;
1023         }
1024 
1025         // Do we have somewhere to fill from?
1026         if (_endp != null )
1027         {
1028             // Shall we compact the body?
1029             if (_buffer==_body || _state>STATE_END)
1030             {
1031                 _buffer.compact();
1032             }
1033 
1034             // Are we full?
1035             if (_buffer.space() == 0)
1036             {
1037                 LOG.warn("HttpParser Full for {} ",_endp);
1038                 _buffer.clear();
1039                 throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
1040             }
1041 
1042             try
1043             {
1044                 int filled = _endp.fill(_buffer);
1045                 return filled;
1046             }
1047             catch(IOException e)
1048             {
1049                 LOG.debug(e);
1050                 throw (e instanceof EofException) ? e:new EofException(e);
1051             }
1052         }
1053 
1054         return -1;
1055     }
1056 
1057     /* ------------------------------------------------------------------------------- */
1058     public void reset()
1059     {
1060         // reset state
1061         _contentView.setGetIndex(_contentView.putIndex());
1062         _state=_persistent?STATE_START:(_endp.isInputShutdown()?STATE_END:STATE_SEEKING_EOF);
1063         _contentLength=HttpTokens.UNKNOWN_CONTENT;
1064         _contentPosition=0;
1065         _length=0;
1066         _responseStatus=0;
1067 
1068         // Consume LF if CRLF
1069         if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer!=null && _buffer.hasContent() && _buffer.peek() == HttpTokens.LINE_FEED)
1070             _eol=_buffer.get();
1071 
1072         if (_body!=null && _body.hasContent())
1073         {
1074             // There is content in the body after the end of the request.
1075             // This is probably a pipelined header of the next request, so we need to
1076             // copy it to the header buffer.
1077             if (_header==null)
1078                 getHeaderBuffer();
1079             else
1080             {
1081                 _header.setMarkIndex(-1);
1082                 _header.compact();
1083             }
1084             int take=_header.space();
1085             if (take>_body.length())
1086                 take=_body.length();
1087             _body.peek(_body.getIndex(),take);
1088             _body.skip(_header.put(_body.peek(_body.getIndex(),take)));
1089         }
1090 
1091         if (_header!=null)
1092         {
1093             _header.setMarkIndex(-1);
1094             _header.compact();
1095         }
1096         if (_body!=null)
1097             _body.setMarkIndex(-1);
1098 
1099         _buffer=_header;
1100         returnBuffers();
1101     }
1102 
1103 
1104     /* ------------------------------------------------------------------------------- */
1105     public void returnBuffers()
1106     {
1107         if (_body!=null && !_body.hasContent() && _body.markIndex()==-1 && _buffers!=null)
1108         {
1109             if (_buffer==_body)
1110                 _buffer=_header;
1111             if (_buffers!=null)
1112                 _buffers.returnBuffer(_body);
1113             _body=null;
1114         }
1115 
1116         if (_header!=null && !_header.hasContent() && _header.markIndex()==-1 && _buffers!=null)
1117         {
1118             if (_buffer==_header)
1119                 _buffer=null;
1120             _buffers.returnBuffer(_header);
1121             _header=null;
1122         }
1123     }
1124 
1125     /* ------------------------------------------------------------------------------- */
1126     public void setState(int state)
1127     {
1128         this._state=state;
1129         _contentLength=HttpTokens.UNKNOWN_CONTENT;
1130     }
1131 
1132     /* ------------------------------------------------------------------------------- */
1133     public String toString(Buffer buf)
1134     {
1135         return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
1136     }
1137 
1138     /* ------------------------------------------------------------------------------- */
1139     @Override
1140     public String toString()
1141     {
1142         return String.format("%s{s=%d,l=%d,c=%d}",
1143                 getClass().getSimpleName(),
1144                 _state,
1145                 _length,
1146                 _contentLength);
1147     }
1148 
1149     /* ------------------------------------------------------------ */
1150     public Buffer getHeaderBuffer()
1151     {
1152         if (_header == null)
1153         {
1154             _header=_buffers.getHeader();
1155             _tok0.update(_header);
1156             _tok1.update(_header);
1157         }
1158         return _header;
1159     }
1160 
1161     /* ------------------------------------------------------------ */
1162     public Buffer getBodyBuffer()
1163     {
1164         return _body;
1165     }
1166 
1167     /* ------------------------------------------------------------ */
1168     /**
1169      * @param force True if a new buffer will be forced to be used for content and the header buffer will not be used.
1170      */
1171     public void setForceContentBuffer(boolean force)
1172     {
1173         _forceContentBuffer=force;
1174     }
1175 
1176     /* ------------------------------------------------------------ */
1177     public Buffer blockForContent(long maxIdleTime) throws IOException
1178     {
1179         if (_contentView.length()>0)
1180             return _contentView;
1181 
1182         if (getState() <= STATE_END || isState(STATE_SEEKING_EOF))
1183             return null;
1184 
1185         try
1186         {
1187             parseNext();
1188 
1189             // parse until some progress is made (or IOException thrown for timeout)
1190             while(_contentView.length() == 0 && !(isState(HttpParser.STATE_END)||isState(HttpParser.STATE_SEEKING_EOF)) && _endp!=null && _endp.isOpen())
1191             {
1192                 if (!_endp.isBlocking())
1193                 {
1194                     if (parseNext()>0)
1195                         continue;
1196 
1197                     if (!_endp.blockReadable(maxIdleTime))
1198                     {
1199                         _endp.close();
1200                         throw new EofException("timeout");
1201                     }
1202                 }
1203 
1204                 parseNext();
1205             }
1206         }
1207         catch(IOException e)
1208         {
1209             // TODO is this needed?
1210             _endp.close();
1211             throw e;
1212         }
1213 
1214         return _contentView.length()>0?_contentView:null;
1215     }
1216 
1217     /* ------------------------------------------------------------ */
1218     /* (non-Javadoc)
1219      * @see java.io.InputStream#available()
1220      */
1221     public int available() throws IOException
1222     {
1223         if (_contentView!=null && _contentView.length()>0)
1224             return _contentView.length();
1225 
1226         if (_endp.isBlocking())
1227         {
1228             if (_state>0 && _endp instanceof StreamEndPoint)
1229                 return ((StreamEndPoint)_endp).getInputStream().available()>0?1:0;
1230 
1231             return 0;
1232         }
1233 
1234         parseNext();
1235         return _contentView==null?0:_contentView.length();
1236     }
1237 
1238     /* ------------------------------------------------------------ */
1239     /* ------------------------------------------------------------ */
1240     /* ------------------------------------------------------------ */
1241     public static abstract class EventHandler
1242     {
1243         public abstract void content(Buffer ref) throws IOException;
1244 
1245         public void headerComplete() throws IOException
1246         {
1247         }
1248 
1249         public void messageComplete(long contentLength) throws IOException
1250         {
1251         }
1252 
1253         /**
1254          * This is the method called by parser when a HTTP Header name and value is found
1255          */
1256         public void parsedHeader(Buffer name, Buffer value) throws IOException
1257         {
1258         }
1259 
1260         /**
1261          * This is the method called by parser when the HTTP request line is parsed
1262          */
1263         public abstract void startRequest(Buffer method, Buffer url, Buffer version)
1264                 throws IOException;
1265 
1266         /**
1267          * This is the method called by parser when the HTTP request line is parsed
1268          */
1269         public abstract void startResponse(Buffer version, int status, Buffer reason)
1270                 throws IOException;
1271 
1272         public void earlyEOF()
1273         {}
1274     }
1275 
1276 
1277 
1278 
1279 }