View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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 static org.eclipse.jetty.http.HttpTokens.CARRIAGE_RETURN;
22  import static org.eclipse.jetty.http.HttpTokens.LINE_FEED;
23  import static org.eclipse.jetty.http.HttpTokens.SPACE;
24  import static org.eclipse.jetty.http.HttpTokens.TAB;
25  
26  import java.nio.ByteBuffer;
27  import java.nio.charset.StandardCharsets;
28  import java.util.Arrays;
29  import java.util.EnumSet;
30  
31  import org.eclipse.jetty.http.HttpTokens.EndOfContent;
32  import org.eclipse.jetty.util.ArrayTernaryTrie;
33  import org.eclipse.jetty.util.ArrayTrie;
34  import org.eclipse.jetty.util.BufferUtil;
35  import org.eclipse.jetty.util.Trie;
36  import org.eclipse.jetty.util.TypeUtil;
37  import org.eclipse.jetty.util.Utf8StringBuilder;
38  import org.eclipse.jetty.util.log.Log;
39  import org.eclipse.jetty.util.log.Logger;
40  
41  
42  /* ------------------------------------------------------------ */
43  /** A Parser for 1.0 and 1.1 as defined by RFC7230
44   * <p>
45   * This parser parses HTTP client and server messages from buffers
46   * passed in the {@link #parseNext(ByteBuffer)} method.  The parsed
47   * elements of the HTTP message are passed as event calls to the 
48   * {@link HttpHandler} instance the parser is constructed with.
49   * If the passed handler is a {@link RequestHandler} then server side
50   * parsing is performed and if it is a {@link ResponseHandler}, then 
51   * client side parsing is done.
52   * </p>
53   * <p>
54   * The contract of the {@link HttpHandler} API is that if a call returns 
55   * true then the call to {@link #parseNext(ByteBuffer)} will return as 
56   * soon as possible also with a true response.  Typically this indicates
57   * that the parsing has reached a stage where the caller should process 
58   * the events accumulated by the handler.    It is the preferred calling
59   * style that handling such as calling a servlet to process a request, 
60   * should be done after a true return from {@link #parseNext(ByteBuffer)}
61   * rather than from within the scope of a call like 
62   * {@link RequestHandler#messageComplete()}
63   * </p>
64   * <p>
65   * For performance, the parse is heavily dependent on the 
66   * {@link Trie#getBest(ByteBuffer, int, int)} method to look ahead in a
67   * single pass for both the structure ( : and CRLF ) and semantic (which
68   * header and value) of a header.  Specifically the static {@link HttpHeader#CACHE}
69   * is used to lookup common combinations of headers and values 
70   * (eg. "Connection: close"), or just header names (eg. "Connection:" ).
71   * For headers who's value is not known statically (eg. Host, COOKIE) then a
72   * per parser dynamic Trie of {@link HttpFields} from previous parsed messages
73   * is used to help the parsing of subsequent messages.
74   * </p>
75   * <p>
76   * If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true,
77   * then the parser will strictly pass on the exact strings received for methods and header
78   * fields.  Otherwise a fast case insensitive string lookup is used that may alter the
79   * case of the method and/or headers
80   * </p>
81   * <p>
82   * @see <a href="http://tools.ietf.org/html/rfc7230">RFC 7230</a>
83   */
84  public class HttpParser
85  {
86      public static final Logger LOG = Log.getLogger(HttpParser.class);
87      public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT"); 
88      public final static int INITIAL_URI_LENGTH=256;
89  
90      /**
91       * Cache of common {@link HttpField}s including: <UL>
92       * <LI>Common static combinations such as:<UL>
93       *   <li>Connection: close
94       *   <li>Accept-Encoding: gzip
95       *   <li>Content-Length: 0
96       * </ul>
97       * <li>Combinations of Content-Type header for common mime types by common charsets
98       * <li>Most common headers with null values so that a lookup will at least
99       * determine the header name even if the name:value combination is not cached
100      * </ul>
101      */
102     public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
103     
104     // States
105     public enum State
106     {
107         START,
108         METHOD,
109         RESPONSE_VERSION,
110         SPACE1,
111         STATUS,
112         URI,
113         SPACE2,
114         REQUEST_VERSION,
115         REASON,
116         PROXY,
117         HEADER,
118         HEADER_IN_NAME,
119         HEADER_VALUE,
120         HEADER_IN_VALUE,
121         CONTENT,
122         EOF_CONTENT,
123         CHUNKED_CONTENT,
124         CHUNK_SIZE,
125         CHUNK_PARAMS,
126         CHUNK,
127         CHUNK_END,
128         END,
129         CLOSE,  // The associated stream/endpoint should be closed
130         CLOSED  // The associated stream/endpoint is at EOF
131     }
132 
133     private final static EnumSet<State> __idleStates = EnumSet.of(State.START,State.END,State.CLOSE,State.CLOSED);
134     private final static EnumSet<State> __completeStates = EnumSet.of(State.END,State.CLOSE,State.CLOSED);
135     
136     private final boolean DEBUG=LOG.isDebugEnabled(); // Cache debug to help branch prediction
137     private final HttpHandler _handler;
138     private final RequestHandler _requestHandler;
139     private final ResponseHandler _responseHandler;
140     private final int _maxHeaderBytes;
141     private final boolean _strict;
142     private HttpField _field;
143     private HttpHeader _header;
144     private String _headerString;
145     private HttpHeaderValue _value;
146     private String _valueString;
147     private int _responseStatus;
148     private int _headerBytes;
149     private boolean _host;
150 
151     /* ------------------------------------------------------------------------------- */
152     private volatile State _state=State.START;
153     private volatile boolean _eof;
154     private HttpMethod _method;
155     private String _methodString;
156     private HttpVersion _version;
157     private Utf8StringBuilder _uri=new Utf8StringBuilder(INITIAL_URI_LENGTH); // Tune?
158     private EndOfContent _endOfContent;
159     private long _contentLength;
160     private long _contentPosition;
161     private int _chunkLength;
162     private int _chunkPosition;
163     private boolean _headResponse;
164     private boolean _cr;
165     private ByteBuffer _contentChunk;
166     private Trie<HttpField> _connectionFields;
167 
168     private int _length;
169     private final StringBuilder _string=new StringBuilder();
170 
171     static
172     {
173         CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
174         CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
175         CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.UPGRADE));
176         CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip"));
177         CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
178         CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip,deflate,sdch"));
179         CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
180         CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-GB,en-US;q=0.8,en;q=0.6"));
181         CACHE.put(new HttpField(HttpHeader.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
182         CACHE.put(new HttpField(HttpHeader.ACCEPT,"*/*"));
183         CACHE.put(new HttpField(HttpHeader.ACCEPT,"image/png,image/*;q=0.8,*/*;q=0.5"));
184         CACHE.put(new HttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
185         CACHE.put(new HttpField(HttpHeader.PRAGMA,"no-cache"));
186         CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
187         CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
188         CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH,"0"));
189         CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
190         CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
191         CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
192         CACHE.put(new HttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
193         
194         // Add common Content types as fields
195         for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/json","application/x-www-form-urlencoded"})
196         {
197             HttpField field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type);
198             CACHE.put(field);
199             
200             for (String charset : new String[]{"utf-8","iso-8859-1"})
201             {
202                 CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
203                 CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset));
204                 CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset.toUpperCase()));
205                 CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset.toUpperCase()));
206             }
207         }
208     
209         // Add headers with null values so HttpParser can avoid looking up name again for unknown values
210         for (HttpHeader h:HttpHeader.values())
211             if (!CACHE.put(new HttpField(h,(String)null)))
212                 throw new IllegalStateException("CACHE FULL");
213         // Add some more common headers
214         CACHE.put(new HttpField(HttpHeader.REFERER,(String)null));
215         CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE,(String)null));
216         CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH,(String)null));
217         CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
218         CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
219     }
220 
221     /* ------------------------------------------------------------------------------- */
222     public HttpParser(RequestHandler handler)
223     {
224         this(handler,-1,__STRICT);
225     }
226 
227     /* ------------------------------------------------------------------------------- */
228     public HttpParser(ResponseHandler handler)
229     {
230         this(handler,-1,__STRICT);
231     }
232 
233     /* ------------------------------------------------------------------------------- */
234     public HttpParser(RequestHandler handler,int maxHeaderBytes)
235     {
236         this(handler,maxHeaderBytes,__STRICT);
237     }
238 
239     /* ------------------------------------------------------------------------------- */
240     public HttpParser(ResponseHandler handler,int maxHeaderBytes)
241     {
242         this(handler,maxHeaderBytes,__STRICT);
243     }
244     
245     /* ------------------------------------------------------------------------------- */
246     public HttpParser(RequestHandler handler,int maxHeaderBytes,boolean strict)
247     {
248         _handler=handler;
249         _requestHandler=handler;
250         _responseHandler=null;
251         _maxHeaderBytes=maxHeaderBytes;
252         _strict=strict;
253     }
254 
255     /* ------------------------------------------------------------------------------- */
256     public HttpParser(ResponseHandler handler,int maxHeaderBytes,boolean strict)
257     {
258         _handler=handler;
259         _requestHandler=null;
260         _responseHandler=handler;
261         _maxHeaderBytes=maxHeaderBytes;
262         _strict=strict;
263     }
264 
265     /* ------------------------------------------------------------------------------- */
266     public long getContentLength()
267     {
268         return _contentLength;
269     }
270 
271     /* ------------------------------------------------------------ */
272     public long getContentRead()
273     {
274         return _contentPosition;
275     }
276 
277     /* ------------------------------------------------------------ */
278     /** Set if a HEAD response is expected
279      * @param head true if head response is expected
280      */
281     public void setHeadResponse(boolean head)
282     {
283         _headResponse=head;
284     }
285 
286     /* ------------------------------------------------------------------------------- */
287     protected void setResponseStatus(int status)
288     {
289         _responseStatus=status;
290     }
291 
292     /* ------------------------------------------------------------------------------- */
293     public State getState()
294     {
295         return _state;
296     }
297 
298     /* ------------------------------------------------------------------------------- */
299     public boolean inContentState()
300     {
301         return _state.ordinal()>=State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal();
302     }
303 
304     /* ------------------------------------------------------------------------------- */
305     public boolean inHeaderState()
306     {
307         return _state.ordinal() < State.CONTENT.ordinal();
308     }
309 
310     /* ------------------------------------------------------------------------------- */
311     public boolean isChunking()
312     {
313         return _endOfContent==EndOfContent.CHUNKED_CONTENT;
314     }
315 
316     /* ------------------------------------------------------------ */
317     public boolean isStart()
318     {
319         return isState(State.START);
320     }
321 
322     /* ------------------------------------------------------------ */
323     public boolean isClose()
324     {
325         return isState(State.CLOSE);
326     }
327 
328     /* ------------------------------------------------------------ */
329     public boolean isClosed()
330     {
331         return isState(State.CLOSED);
332     }
333 
334     /* ------------------------------------------------------------ */
335     public boolean isIdle()
336     {
337         return __idleStates.contains(_state);
338     }
339 
340     /* ------------------------------------------------------------ */
341     public boolean isComplete()
342     {
343         return __completeStates.contains(_state);
344     }
345 
346     /* ------------------------------------------------------------------------------- */
347     public boolean isState(State state)
348     {
349         return _state == state;
350     }
351 
352     /* ------------------------------------------------------------------------------- */
353     enum CharState { ILLEGAL, CR, LF, LEGAL }
354     private final static CharState[] __charState;
355     static
356     {
357         // token          = 1*tchar
358         // tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
359         //                / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
360         //                / DIGIT / ALPHA
361         //                ; any VCHAR, except delimiters
362         // quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE
363         // qdtext         = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
364         // obs-text       = %x80-FF
365         // comment        = "(" *( ctext / quoted-pair / comment ) ")"
366         // ctext          = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
367         // quoted-pair    = "\" ( HTAB / SP / VCHAR / obs-text )
368    
369         __charState=new CharState[256];
370         Arrays.fill(__charState,CharState.ILLEGAL);
371         __charState[LINE_FEED]=CharState.LF;
372         __charState[CARRIAGE_RETURN]=CharState.CR;
373         __charState[TAB]=CharState.LEGAL;
374         __charState[SPACE]=CharState.LEGAL;
375         
376         __charState['!']=CharState.LEGAL;
377         __charState['#']=CharState.LEGAL;
378         __charState['$']=CharState.LEGAL;
379         __charState['%']=CharState.LEGAL;
380         __charState['&']=CharState.LEGAL;
381         __charState['\'']=CharState.LEGAL;
382         __charState['*']=CharState.LEGAL;
383         __charState['+']=CharState.LEGAL;
384         __charState['-']=CharState.LEGAL;
385         __charState['.']=CharState.LEGAL;
386         __charState['^']=CharState.LEGAL;
387         __charState['_']=CharState.LEGAL;
388         __charState['`']=CharState.LEGAL;
389         __charState['|']=CharState.LEGAL;
390         __charState['~']=CharState.LEGAL;
391         
392         __charState['"']=CharState.LEGAL;
393         
394         __charState['\\']=CharState.LEGAL;
395         __charState['(']=CharState.LEGAL;
396         __charState[')']=CharState.LEGAL;
397         Arrays.fill(__charState,0x21,0x27+1,CharState.LEGAL);
398         Arrays.fill(__charState,0x2A,0x5B+1,CharState.LEGAL);
399         Arrays.fill(__charState,0x5D,0x7E+1,CharState.LEGAL);
400         Arrays.fill(__charState,0x80,0xFF+1,CharState.LEGAL);
401         
402     }
403     
404     /* ------------------------------------------------------------------------------- */
405     private byte next(ByteBuffer buffer)
406     {
407         byte ch = buffer.get();
408         
409         CharState s = __charState[0xff & ch];
410         switch(s)
411         {
412             case ILLEGAL:
413                 throw new IllegalCharacterException(_state,ch,buffer);
414                 
415             case LF:
416                 _cr=false;
417                 break;
418                 
419             case CR:
420                 if (_cr)
421                     throw new BadMessageException("Bad EOL");
422 
423                 _cr=true;
424                 if (buffer.hasRemaining())
425                 {
426                     if(_maxHeaderBytes>0 && _state.ordinal()<State.END.ordinal())
427                         _headerBytes++;
428                     return next(buffer);
429                 }
430                 
431                 // Can return 0 here to indicate the need for more characters, 
432                 // because a real 0 in the buffer would cause a BadMessage below 
433                 return 0;
434                 
435             case LEGAL:
436                 if (_cr)
437                     throw new BadMessageException("Bad EOL");
438                 
439         }
440         
441         return ch;
442     }
443     
444     /* ------------------------------------------------------------------------------- */
445     /* Quick lookahead for the start state looking for a request method or a HTTP version,
446      * otherwise skip white space until something else to parse.
447      */
448     private boolean quickStart(ByteBuffer buffer)
449     {    	
450         if (_requestHandler!=null)
451         {
452             _method = HttpMethod.lookAheadGet(buffer);
453             if (_method!=null)
454             {
455                 _methodString = _method.asString();
456                 buffer.position(buffer.position()+_methodString.length()+1);
457                 
458                 setState(State.SPACE1);
459                 return false;
460             }
461         }
462         else if (_responseHandler!=null)
463         {
464             _version = HttpVersion.lookAheadGet(buffer);
465             if (_version!=null)
466             {
467                 buffer.position(buffer.position()+_version.asString().length()+1);
468                 setState(State.SPACE1);
469                 return false;
470             }
471         }
472         
473         // Quick start look
474         while (_state==State.START && buffer.hasRemaining())
475         {
476             int ch=next(buffer);
477 
478             if (ch > SPACE)
479             {
480                 _string.setLength(0);
481                 _string.append((char)ch);
482                 setState(_requestHandler!=null?State.METHOD:State.RESPONSE_VERSION);
483                 return false;
484             }
485             else if (ch==0)
486                 break;
487             else if (ch<0)
488                 throw new BadMessageException();
489             
490             // count this white space as a header byte to avoid DOS
491             if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
492             {
493                 LOG.warn("padding is too large >"+_maxHeaderBytes);
494                 throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
495             }
496         }
497         return false;
498     }
499 
500     /* ------------------------------------------------------------------------------- */
501     private void setString(String s)
502     {
503         _string.setLength(0);
504         _string.append(s);
505         _length=s.length();
506     }
507     
508     /* ------------------------------------------------------------------------------- */
509     private String takeString()
510     {
511         _string.setLength(_length);
512         String s =_string.toString();
513         _string.setLength(0);
514         _length=-1;
515         return s;
516     }
517 
518     /* ------------------------------------------------------------------------------- */
519     /* Parse a request or response line
520      */
521     private boolean parseLine(ByteBuffer buffer)
522     {
523         boolean handle=false;
524 
525         // Process headers
526         while (_state.ordinal()<State.HEADER.ordinal() && buffer.hasRemaining() && !handle)
527         {
528             // process each character
529             byte ch=next(buffer);
530             if (ch==0)
531                 break;
532 
533             if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
534             {
535                 if (_state==State.URI)
536                 {
537                     LOG.warn("URI is too large >"+_maxHeaderBytes);
538                     throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
539                 }
540                 else
541                 {
542                     if (_requestHandler!=null)
543                         LOG.warn("request is too large >"+_maxHeaderBytes);
544                     else
545                         LOG.warn("response is too large >"+_maxHeaderBytes);
546                     throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
547                 }
548             }
549 
550             switch (_state)
551             {
552                 case METHOD:
553                     if (ch == SPACE)
554                     {
555                         _length=_string.length();
556                         _methodString=takeString();
557                         HttpMethod method=HttpMethod.CACHE.get(_methodString);
558                         if (method!=null && !_strict)
559                             _methodString=method.asString();
560                         setState(State.SPACE1);
561                     }
562                     else if (ch < SPACE)
563                     {
564                         if (ch==LINE_FEED)
565                             throw new BadMessageException("No URI");
566                         else
567                             throw new IllegalCharacterException(_state,ch,buffer);
568                     }
569                     else
570                         _string.append((char)ch);
571                     break;
572 
573                 case RESPONSE_VERSION:
574                     if (ch == HttpTokens.SPACE)
575                     {
576                         _length=_string.length();
577                         String version=takeString();
578                         _version=HttpVersion.CACHE.get(version);
579                         if (_version==null)
580                             throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
581                         setState(State.SPACE1);
582                     }
583                     else if (ch < HttpTokens.SPACE)
584                         throw new IllegalCharacterException(_state,ch,buffer);
585                     else
586                         _string.append((char)ch);
587                     break;
588 
589                 case SPACE1:
590                     if (ch > HttpTokens.SPACE || ch<0)
591                     {
592                         if (_responseHandler!=null)
593                         {
594                             setState(State.STATUS);
595                             setResponseStatus(ch-'0');
596                         }
597                         else
598                         {
599                             _uri.reset();
600                             setState(State.URI);
601                             // quick scan for space or EoBuffer
602                             if (buffer.hasArray())
603                             {
604                                 byte[] array=buffer.array();
605                                 int p=buffer.arrayOffset()+buffer.position();
606                                 int l=buffer.arrayOffset()+buffer.limit();
607                                 int i=p;
608                                 while (i<l && array[i]>HttpTokens.SPACE)
609                                     i++;
610 
611                                 int len=i-p;
612                                 _headerBytes+=len;
613                                 
614                                 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
615                                 {
616                                     LOG.warn("URI is too large >"+_maxHeaderBytes);
617                                     throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
618                                 }
619                                 _uri.append(array,p-1,len+1);
620                                 buffer.position(i-buffer.arrayOffset());
621                             }
622                             else
623                                 _uri.append(ch);
624                         }
625                     }
626                     else if (ch < HttpTokens.SPACE)
627                     {
628                         throw new BadMessageException(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
629                     }
630                     break;
631 
632                 case STATUS:
633                     if (ch == HttpTokens.SPACE)
634                     {
635                         setState(State.SPACE2);
636                     }
637                     else if (ch>='0' && ch<='9')
638                     {
639                         _responseStatus=_responseStatus*10+(ch-'0');
640                     }
641                     else if (ch < HttpTokens.SPACE && ch>=0)
642                     {
643                         handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
644                         setState(State.HEADER);
645                     }
646                     else
647                     {
648                         throw new BadMessageException();
649                     }
650                     break;
651 
652                 case URI:
653                     if (ch == HttpTokens.SPACE)
654                     {
655                         setState(State.SPACE2);
656                     }
657                     else if (ch < HttpTokens.SPACE && ch>=0)
658                     {
659                         // HTTP/0.9
660                         throw new BadMessageException("HTTP/0.9 not supported");
661                     }
662                     else
663                     {
664                         _uri.append(ch);
665                     }
666                     break;
667 
668                 case SPACE2:
669                     if (ch > HttpTokens.SPACE)
670                     {
671                         _string.setLength(0);
672                         _string.append((char)ch);
673                         if (_responseHandler!=null)
674                         {
675                             _length=1;
676                             setState(State.REASON);
677                         }
678                         else
679                         {
680                             setState(State.REQUEST_VERSION);
681 
682                             // try quick look ahead for HTTP Version
683                             HttpVersion version;
684                             if (buffer.position()>0 && buffer.hasArray())
685                                 version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
686                             else
687                                 version=HttpVersion.CACHE.getBest(buffer,0,buffer.remaining());
688                             
689                             if (version!=null)
690                             {
691                                 int pos = buffer.position()+version.asString().length()-1;
692                                 if (pos<buffer.limit())
693                                 {
694                                     byte n=buffer.get(pos);
695                                     if (n==HttpTokens.CARRIAGE_RETURN)
696                                     {
697                                         _cr=true;
698                                         _version=version;
699                                         _string.setLength(0);
700                                         buffer.position(pos+1);
701                                     }
702                                     else if (n==HttpTokens.LINE_FEED)
703                                     {
704                                         _version=version;
705                                         _string.setLength(0);
706                                         buffer.position(pos);
707                                     }
708                                 }
709                             }
710                         }
711                     }
712                     else if (ch == HttpTokens.LINE_FEED)
713                     {
714                         if (_responseHandler!=null)
715                         {
716                             handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
717                             setState(State.HEADER);
718                         }
719                         else
720                         {
721                             // HTTP/0.9
722                             throw new BadMessageException("HTTP/0.9 not supported");
723                         }
724                     }
725                     else if (ch<0)
726                         throw new BadMessageException();
727                     break;
728 
729                 case REQUEST_VERSION:
730                     if (ch == HttpTokens.LINE_FEED)
731                     {
732                         if (_version==null)
733                         {
734                             _length=_string.length();
735                             _version=HttpVersion.CACHE.get(takeString());
736                         }
737                         if (_version==null)
738                             throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
739                         
740                         // Should we try to cache header fields?
741                         if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion() && _handler.getHeaderCacheSize()>0)
742                         {
743                             int header_cache = _handler.getHeaderCacheSize();
744                             _connectionFields=new ArrayTernaryTrie<>(header_cache);                            
745                         }
746 
747                         setState(State.HEADER);
748                         
749                         handle=_requestHandler.startRequest(_methodString,_uri.toString(), _version)||handle;
750                         continue;
751                     }
752                     else if (ch>=HttpTokens.SPACE)
753                         _string.append((char)ch);
754                     else
755                         throw new BadMessageException();
756 
757                     break;
758 
759                 case REASON:
760                     if (ch == HttpTokens.LINE_FEED)
761                     {
762                         String reason=takeString();
763 
764                         setState(State.HEADER);
765                         handle=_responseHandler.startResponse(_version, _responseStatus, reason)||handle;
766                         continue;
767                     }
768                     else if (ch>=HttpTokens.SPACE)
769                     {
770                         _string.append((char)ch);
771                         if (ch!=' '&&ch!='\t')
772                             _length=_string.length();
773                     } 
774                     else
775                         throw new BadMessageException();
776                     break;
777 
778                 default:
779                     throw new IllegalStateException(_state.toString());
780 
781             }
782         }
783 
784         return handle;
785     }
786 
787     private void parsedHeader()
788     {
789         // handler last header if any.  Delayed to here just in case there was a continuation line (above)
790         if (_headerString!=null || _valueString!=null)
791         {
792             // Handle known headers
793             if (_header!=null)
794             {
795                 boolean add_to_connection_trie=false;
796                 switch (_header)
797                 {
798                     case CONTENT_LENGTH:
799                         if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
800                         {
801                             try
802                             {
803                                 _contentLength=Long.parseLong(_valueString);
804                             }
805                             catch(NumberFormatException e)
806                             {
807                                 LOG.ignore(e);
808                                 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
809                             }
810                             if (_contentLength <= 0)
811                                 _endOfContent=EndOfContent.NO_CONTENT;
812                             else
813                                 _endOfContent=EndOfContent.CONTENT_LENGTH;
814                         }
815                         break;
816 
817                     case TRANSFER_ENCODING:
818                         if (_value==HttpHeaderValue.CHUNKED)
819                             _endOfContent=EndOfContent.CHUNKED_CONTENT;
820                         else
821                         {
822                             if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
823                                 _endOfContent=EndOfContent.CHUNKED_CONTENT;
824                             else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
825                             {
826                                 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
827                             }
828                         }
829                         break;
830 
831                     case HOST:
832                         _host=true;
833                         if (!(_field instanceof HostPortHttpField))
834                         {
835                             _field=new HostPortHttpField(_header,_strict?_headerString:_header.asString(),_valueString);
836                             add_to_connection_trie=_connectionFields!=null;
837                         }
838                       break;
839                       
840                     case CONNECTION:
841                         // Don't cache if not persistent
842                         if (_valueString!=null && _valueString.contains("close"))
843                             _connectionFields=null;
844                         
845                         break;
846 
847                     case AUTHORIZATION:
848                     case ACCEPT:
849                     case ACCEPT_CHARSET:
850                     case ACCEPT_ENCODING:
851                     case ACCEPT_LANGUAGE:
852                     case COOKIE:
853                     case CACHE_CONTROL:
854                     case USER_AGENT:
855                         add_to_connection_trie=_connectionFields!=null && _field==null;
856                         break;
857                         
858                     default: break;
859                 }
860             
861                 if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
862                 {
863                     if (_field==null)
864                         _field=new HttpField(_header,_strict?_headerString:_header.asString(),_valueString);
865                     _connectionFields.put(_field);
866                 }
867             }
868             _handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString));
869         }
870         
871         _headerString=_valueString=null;
872         _header=null;
873         _value=null;
874         _field=null;
875     }
876     
877     
878     /* ------------------------------------------------------------------------------- */
879     /*
880      * Parse the message headers and return true if the handler has signaled for a return
881      */
882     protected boolean parseHeaders(ByteBuffer buffer)
883     {
884         boolean handle=false;
885 
886         // Process headers
887         while (_state.ordinal()<State.CONTENT.ordinal() && buffer.hasRemaining() && !handle)
888         {
889             // process each character
890             byte ch=next(buffer);
891             if (ch==0)
892                 break;
893             
894             if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
895             {
896                 LOG.warn("Header is too large >"+_maxHeaderBytes);
897                 throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
898             }
899 
900             switch (_state)
901             {
902                 case HEADER:
903                     switch(ch)
904                     {
905                         case HttpTokens.COLON:
906                         case HttpTokens.SPACE:
907                         case HttpTokens.TAB:
908                             throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Continuation");
909 
910                         case HttpTokens.LINE_FEED:
911                         {
912                             _contentPosition=0;
913 
914                             // End of headers!
915 
916                             // Was there a required host header?
917                             if (!_host && _version==HttpVersion.HTTP_1_1 && _requestHandler!=null)
918                             {
919                                 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"No Host");
920                             }
921 
922                             // is it a response that cannot have a body?
923                             if (_responseHandler !=null  && // response  
924                                     (_responseStatus == 304  || // not-modified response
925                                     _responseStatus == 204 || // no-content response
926                                     _responseStatus < 200)) // 1xx response
927                                 _endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
928 
929                             // else if we don't know framing
930                             else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
931                             {
932                                 if (_responseStatus == 0  // request
933                                         || _responseStatus == 304 // not-modified response
934                                         || _responseStatus == 204 // no-content response
935                                         || _responseStatus < 200) // 1xx response
936                                     _endOfContent=EndOfContent.NO_CONTENT;
937                                 else
938                                     _endOfContent=EndOfContent.EOF_CONTENT;
939                             }
940 
941                             // How is the message ended?
942                             switch (_endOfContent)
943                             {
944                                 case EOF_CONTENT:
945                                     setState(State.EOF_CONTENT);
946                                     handle=_handler.headerComplete()||handle;
947                                     return handle;
948 
949                                 case CHUNKED_CONTENT:
950                                     setState(State.CHUNKED_CONTENT);
951                                     handle=_handler.headerComplete()||handle;
952                                     return handle;
953 
954                                 case NO_CONTENT:
955                                     handle=_handler.headerComplete()||handle;
956                                     setState(State.END);
957                                     handle=_handler.messageComplete()||handle;
958                                     return handle;
959 
960                                 default:
961                                     setState(State.CONTENT);
962                                     handle=_handler.headerComplete()||handle;
963                                     return handle;
964                             }
965                         }
966 
967                         default:
968                         {
969                             // now handle the ch
970                             if (ch<=HttpTokens.SPACE)
971                                 throw new BadMessageException();
972 
973                             if (buffer.hasRemaining())
974                             {
975                                 // Try a look ahead for the known header name and value.
976                                 HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
977                                 if (field==null)
978                                     field=CACHE.getBest(buffer,-1,buffer.remaining());
979 
980                                 if (field!=null)
981                                 {
982                                     final String n;
983                                     final String v;
984 
985                                     if (_strict)
986                                     {
987                                         // Have to get the fields exactly from the buffer to match case
988                                         String fn=field.getName();
989                                         String fv=field.getValue();
990                                         n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII);
991                                         if (fv==null)
992                                             v=null;
993                                         else
994                                         {
995                                             v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1);
996                                             field=new HttpField(field.getHeader(),n,v);
997                                         }
998                                     }
999                                     else
1000                                     {
1001                                         n=field.getName();
1002                                         v=field.getValue(); 
1003                                     }
1004 
1005                                     _header=field.getHeader();
1006                                     _headerString=n;
1007 
1008                                     if (v==null)
1009                                     {
1010                                         // Header only
1011                                         setState(State.HEADER_VALUE);
1012                                         _string.setLength(0);
1013                                         _length=0;
1014                                         buffer.position(buffer.position()+n.length()+1);
1015                                         break;
1016                                     }
1017                                     else
1018                                     {
1019                                         // Header and value
1020                                         int pos=buffer.position()+n.length()+v.length()+1;
1021                                         byte b=buffer.get(pos);
1022 
1023                                         if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
1024                                         {                     
1025                                             _field=field;
1026                                             _valueString=v;
1027                                             setState(State.HEADER_IN_VALUE);
1028 
1029                                             if (b==HttpTokens.CARRIAGE_RETURN)
1030                                             {
1031                                                 _cr=true;
1032                                                 buffer.position(pos+1);
1033                                             }
1034                                             else
1035                                                 buffer.position(pos);
1036                                             break;
1037                                         }
1038                                         else
1039                                         {
1040                                             setState(State.HEADER_IN_VALUE);
1041                                             setString(v);
1042                                             buffer.position(pos);
1043                                             break;
1044                                         }
1045                                     }
1046                                 }
1047                             }
1048 
1049                             // New header
1050                             setState(State.HEADER_IN_NAME);
1051                             _string.setLength(0);
1052                             _string.append((char)ch);
1053                             _length=1;
1054 
1055                         }
1056                     }
1057                     break;
1058 
1059                 case HEADER_IN_NAME:
1060                     if (ch==HttpTokens.COLON)
1061                     {
1062                         if (_headerString==null)
1063                         {
1064                             _headerString=takeString();
1065                             _header=HttpHeader.CACHE.get(_headerString);
1066                         }
1067                         _length=-1;
1068 
1069                         setState(State.HEADER_VALUE);
1070                         break;
1071                     }
1072                     
1073                     if (ch>HttpTokens.SPACE)
1074                     {
1075                         if (_header!=null)
1076                         {
1077                             setString(_header.asString());
1078                             _header=null;
1079                             _headerString=null;
1080                         }
1081 
1082                         _string.append((char)ch);
1083                         if (ch>HttpTokens.SPACE)
1084                             _length=_string.length();
1085                         break;
1086                     }
1087 
1088                     throw new IllegalCharacterException(_state,ch,buffer);
1089 
1090                 case HEADER_VALUE:
1091                     if (ch>HttpTokens.SPACE || ch<0)
1092                     {
1093                         _string.append((char)(0xff&ch));
1094                         _length=_string.length();
1095                         setState(State.HEADER_IN_VALUE);
1096                         break;
1097                     }
1098                     
1099                     if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
1100                         break;
1101 
1102                     if (ch==HttpTokens.LINE_FEED)
1103                     {
1104                         _value=null;
1105                         _string.setLength(0);
1106                         _valueString=null;
1107                         _length=-1;
1108                         
1109                         parsedHeader();
1110                         setState(State.HEADER);
1111                         break;
1112                     }
1113                     throw new IllegalCharacterException(_state,ch,buffer);
1114 
1115                 case HEADER_IN_VALUE:
1116                     if (ch>=HttpTokens.SPACE || ch<0 || ch==HttpTokens.TAB)
1117                     {
1118                         if (_valueString!=null)
1119                         {
1120                             setString(_valueString);
1121                             _valueString=null;
1122                             _field=null;
1123                         }
1124                         _string.append((char)(0xff&ch));
1125                         if (ch>HttpTokens.SPACE || ch<0)
1126                             _length=_string.length();
1127                         break;
1128                     }
1129                     
1130                     if (ch==HttpTokens.LINE_FEED)
1131                     {
1132                         if (_length > 0)
1133                         {
1134                             _value=null;
1135                             _valueString=takeString();
1136                             _length=-1;
1137                         }
1138                         parsedHeader();
1139                         setState(State.HEADER);
1140                         break;
1141                     }
1142 
1143                     throw new IllegalCharacterException(_state,ch,buffer);
1144                     
1145                 default:
1146                     throw new IllegalStateException(_state.toString());
1147 
1148             }
1149         }
1150 
1151         return handle;
1152     }
1153 
1154     /* ------------------------------------------------------------------------------- */
1155     /**
1156      * Parse until next Event.
1157      * @param buffer the buffer to parse
1158      * @return True if an {@link RequestHandler} method was called and it returned true;
1159      */
1160     public boolean parseNext(ByteBuffer buffer)
1161     {
1162         if (DEBUG)
1163             LOG.debug("parseNext s={} {}",_state,BufferUtil.toDetailString(buffer));
1164         try
1165         {
1166             // Start a request/response
1167             if (_state==State.START)
1168             {
1169                 _version=null;
1170                 _method=null;
1171                 _methodString=null;
1172                 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
1173                 _header=null;
1174                 if (quickStart(buffer))
1175                     return true;
1176             }
1177             
1178             // Request/response line
1179             if (_state.ordinal()>= State.START.ordinal() && _state.ordinal()<State.HEADER.ordinal())
1180             {
1181                 if (parseLine(buffer))
1182                     return true;
1183             }
1184 
1185             // parse headers
1186             if (_state.ordinal()>= State.HEADER.ordinal() && _state.ordinal()<State.CONTENT.ordinal())
1187             {
1188                 if (parseHeaders(buffer))
1189                     return true;
1190             }
1191             
1192             // parse content
1193             if (_state.ordinal()>= State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal())
1194             {
1195                 // Handle HEAD response
1196                 if (_responseStatus>0 && _headResponse)
1197                 {
1198                     setState(State.END);
1199                     return _handler.messageComplete();
1200                 }
1201                 else
1202                 {
1203                     if (parseContent(buffer))
1204                         return true;
1205                 }
1206             }
1207             
1208             // handle end states
1209             if (_state==State.END)
1210             {
1211                 // eat white space
1212                 while (buffer.remaining()>0 && buffer.get(buffer.position())<=HttpTokens.SPACE)
1213                     buffer.get();
1214             }
1215             else if (_state==State.CLOSE)
1216             {
1217                 // Seeking EOF
1218                 if (BufferUtil.hasContent(buffer))
1219                 {
1220                     // Just ignore data when closed
1221                     _headerBytes+=buffer.remaining();
1222                     BufferUtil.clear(buffer);
1223                     if (_maxHeaderBytes>0 && _headerBytes>_maxHeaderBytes)
1224                     {
1225                         // Don't want to waste time reading data of a closed request
1226                         throw new IllegalStateException("too much data seeking EOF");
1227                     }
1228                 }
1229             }
1230             else if (_state==State.CLOSED)
1231             {
1232                 BufferUtil.clear(buffer);
1233             }
1234             
1235             // Handle EOF
1236             if (_eof && !buffer.hasRemaining())
1237             {
1238                 switch(_state)
1239                 {
1240                     case CLOSED:
1241                         break;
1242                         
1243                     case START:
1244                         setState(State.CLOSED);
1245                         _handler.earlyEOF();
1246                         break;
1247                         
1248                     case END:
1249                     case CLOSE:
1250                         setState(State.CLOSED);
1251                         break;
1252                         
1253                     case EOF_CONTENT:
1254                         setState(State.CLOSED);
1255                         return _handler.messageComplete();
1256 
1257                     case  CONTENT:
1258                     case  CHUNKED_CONTENT:
1259                     case  CHUNK_SIZE:
1260                     case  CHUNK_PARAMS:
1261                     case  CHUNK:
1262                         setState(State.CLOSED);
1263                         _handler.earlyEOF();
1264                         break;
1265 
1266                     default:
1267                         if (DEBUG)
1268                             LOG.debug("{} EOF in {}",this,_state);
1269                         setState(State.CLOSED);
1270                         _handler.badMessage(400,null);
1271                         break;
1272                 }
1273             }
1274         }
1275         catch(BadMessageException e)
1276         {
1277             BufferUtil.clear(buffer);
1278 
1279             Throwable cause = e.getCause();
1280             boolean stack = LOG.isDebugEnabled() || 
1281                     (!(cause instanceof NumberFormatException )  && (cause instanceof RuntimeException || cause instanceof Error));
1282             
1283             if (stack)
1284                 LOG.warn("bad HTTP parsed: "+e._code+(e.getReason()!=null?" "+e.getReason():"")+" for "+_handler,e);
1285             else
1286                 LOG.warn("bad HTTP parsed: "+e._code+(e.getReason()!=null?" "+e.getReason():"")+" for "+_handler);
1287             setState(State.CLOSE);
1288             _handler.badMessage(e.getCode(), e.getReason());
1289         }
1290         catch(NumberFormatException|IllegalStateException e)
1291         {
1292             BufferUtil.clear(buffer);
1293             LOG.warn("parse exception: {} in {} for {}",e.toString(),_state,_handler);
1294             if (DEBUG)
1295                 LOG.debug(e);
1296             
1297             switch(_state)
1298             {
1299                 case CLOSED:
1300                     break;
1301                 case CLOSE:
1302                     _handler.earlyEOF();
1303                     break;
1304                 default:
1305                     setState(State.CLOSE);
1306                     _handler.badMessage(400,null);
1307             }
1308         }
1309         catch(Exception|Error e)
1310         {
1311             BufferUtil.clear(buffer);
1312 
1313             LOG.warn("parse exception: "+e.toString()+" for "+_handler,e);
1314 
1315             switch(_state)
1316             {
1317                 case CLOSED:
1318                     break;
1319                 case CLOSE:
1320                     _handler.earlyEOF();
1321                     break;
1322                 default:
1323                     setState(State.CLOSE);
1324                     _handler.badMessage(400,null);
1325             }
1326         }
1327         return false;
1328     }
1329 
1330     protected boolean parseContent(ByteBuffer buffer)
1331     {
1332         int remaining=buffer.remaining();
1333         if (remaining==0 && _state==State.CONTENT)
1334         {
1335             long content=_contentLength - _contentPosition;
1336             if (content == 0)
1337             {
1338                 setState(State.END);
1339                 return _handler.messageComplete();
1340             }
1341         }
1342         
1343         // Handle _content
1344         byte ch;
1345         while (_state.ordinal() < State.END.ordinal() && remaining>0)
1346         {
1347             switch (_state)
1348             {
1349                 case EOF_CONTENT:
1350                     _contentChunk=buffer.asReadOnlyBuffer();
1351                     _contentPosition += remaining;
1352                     buffer.position(buffer.position()+remaining);
1353                     if (_handler.content(_contentChunk))
1354                         return true;
1355                     break;
1356 
1357                 case CONTENT:
1358                 {
1359                     long content=_contentLength - _contentPosition;
1360                     if (content == 0)
1361                     {
1362                         setState(State.END);
1363                         return _handler.messageComplete();
1364                     }
1365                     else
1366                     {
1367                         _contentChunk=buffer.asReadOnlyBuffer();
1368 
1369                         // limit content by expected size
1370                         if (remaining > content)
1371                         {
1372                             // We can cast remaining to an int as we know that it is smaller than
1373                             // or equal to length which is already an int.
1374                             _contentChunk.limit(_contentChunk.position()+(int)content);
1375                         }
1376 
1377                         _contentPosition += _contentChunk.remaining();
1378                         buffer.position(buffer.position()+_contentChunk.remaining());
1379 
1380                         if (_handler.content(_contentChunk))
1381                             return true;
1382 
1383                         if(_contentPosition == _contentLength)
1384                         {
1385                             setState(State.END);
1386                             return _handler.messageComplete();
1387                         }
1388                     }
1389                     break;
1390                 }
1391 
1392                 case CHUNKED_CONTENT:
1393                 {
1394                     ch=next(buffer);
1395                     if (ch>HttpTokens.SPACE)
1396                     {
1397                         _chunkLength=TypeUtil.convertHexDigit(ch);
1398                         _chunkPosition=0;
1399                         setState(State.CHUNK_SIZE);
1400                     }
1401 
1402                     break;
1403                 }
1404 
1405                 case CHUNK_SIZE:
1406                 {
1407                     ch=next(buffer);
1408                     if (ch==0)
1409                         break;
1410                     if (ch == HttpTokens.LINE_FEED)
1411                     {
1412                         if (_chunkLength == 0)
1413                             setState(State.CHUNK_END);
1414                         else
1415                             setState(State.CHUNK);
1416                     }
1417                     else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
1418                         setState(State.CHUNK_PARAMS);
1419                     else
1420                         _chunkLength=_chunkLength * 16 + TypeUtil.convertHexDigit(ch);
1421                     break;
1422                 }
1423 
1424                 case CHUNK_PARAMS:
1425                 {
1426                     ch=next(buffer);
1427                     if (ch == HttpTokens.LINE_FEED)
1428                     {
1429                         if (_chunkLength == 0)
1430                             setState(State.CHUNK_END);
1431                         else
1432                             setState(State.CHUNK);
1433                     }
1434                     break;
1435                 }
1436 
1437                 case CHUNK:
1438                 {
1439                     int chunk=_chunkLength - _chunkPosition;
1440                     if (chunk == 0)
1441                     {
1442                         setState(State.CHUNKED_CONTENT);
1443                     }
1444                     else
1445                     {
1446                         _contentChunk=buffer.asReadOnlyBuffer();
1447 
1448                         if (remaining > chunk)
1449                             _contentChunk.limit(_contentChunk.position()+chunk);
1450                         chunk=_contentChunk.remaining();
1451 
1452                         _contentPosition += chunk;
1453                         _chunkPosition += chunk;
1454                         buffer.position(buffer.position()+chunk);
1455                         if (_handler.content(_contentChunk))
1456                             return true;
1457                     }
1458                     break;
1459                 }
1460                 
1461                 case CHUNK_END:
1462                 {
1463                     // TODO handle chunk trailer
1464                     ch=next(buffer);
1465                     if (ch==0)
1466                         break;
1467                     if (ch == HttpTokens.LINE_FEED)
1468                     {
1469                         setState(State.END);
1470                         return _handler.messageComplete();
1471                     }
1472                     throw new IllegalCharacterException(_state,ch,buffer);
1473                 }
1474                 
1475                 case CLOSED:
1476                 {
1477                     BufferUtil.clear(buffer);
1478                     return false;
1479                 }
1480 
1481                 default: 
1482                     break;
1483                     
1484             }
1485             
1486             remaining=buffer.remaining();
1487         }
1488         return false;
1489     }
1490 
1491     /* ------------------------------------------------------------------------------- */
1492     public boolean isAtEOF()
1493  
1494     {
1495         return _eof;
1496     }
1497     
1498     /* ------------------------------------------------------------------------------- */
1499     /** Signal that the associated data source is at EOF
1500      */
1501     public void atEOF()
1502     {        
1503         if (DEBUG)
1504             LOG.debug("atEOF {}", this);
1505         _eof=true;
1506     }
1507 
1508     /* ------------------------------------------------------------------------------- */
1509     /** Request that the associated data source be closed
1510      */
1511     public void close()
1512     {
1513         if (DEBUG)
1514             LOG.debug("close {}", this);
1515         setState(State.CLOSE);
1516     }
1517     
1518     /* ------------------------------------------------------------------------------- */
1519     public void reset()
1520     {
1521         if (DEBUG)
1522             LOG.debug("reset {}", this);
1523 
1524         // reset state
1525         if (_state==State.CLOSE || _state==State.CLOSED)
1526             return;
1527         
1528         setState(State.START);
1529         _endOfContent=EndOfContent.UNKNOWN_CONTENT;
1530         _contentLength=-1;
1531         _contentPosition=0;
1532         _responseStatus=0;
1533         _contentChunk=null;
1534         _headerBytes=0;
1535         _host=false;
1536     }
1537 
1538     /* ------------------------------------------------------------------------------- */
1539     protected void setState(State state)
1540     {
1541         if (DEBUG)
1542             LOG.debug("{} --> {}",_state,state);
1543         _state=state;
1544     }
1545 
1546     /* ------------------------------------------------------------------------------- */
1547     public Trie<HttpField> getFieldCache()
1548     {
1549         return _connectionFields;
1550     }
1551 
1552     /* ------------------------------------------------------------------------------- */
1553     private String getProxyField(ByteBuffer buffer)
1554     {
1555         _string.setLength(0);
1556         _length=0;
1557         
1558         while (buffer.hasRemaining())
1559         {
1560             // process each character
1561             byte ch=next(buffer);
1562             if (ch<=' ')
1563                 return _string.toString();
1564             _string.append((char)ch);    
1565         }
1566         throw new BadMessageException();
1567     }
1568     
1569     /* ------------------------------------------------------------------------------- */
1570     @Override
1571     public String toString()
1572     {
1573         return String.format("%s{s=%s,%d of %d}",
1574                 getClass().getSimpleName(),
1575                 _state,
1576                 _contentPosition,
1577                 _contentLength);
1578     }
1579 
1580     /* ------------------------------------------------------------ */
1581     /* ------------------------------------------------------------ */
1582     /* ------------------------------------------------------------ */
1583     /* Event Handler interface
1584      * These methods return true if the caller should process the events
1585      * so far received (eg return from parseNext and call HttpChannel.handle).
1586      * If multiple callbacks are called in sequence (eg 
1587      * headerComplete then messageComplete) from the same point in the parsing
1588      * then it is sufficient for the caller to process the events only once.
1589      */
1590     public interface HttpHandler
1591     {
1592         public boolean content(ByteBuffer item);
1593 
1594         public boolean headerComplete();
1595 
1596         public boolean messageComplete();
1597 
1598         /**
1599          * This is the method called by parser when a HTTP Header name and value is found
1600          * @param field The field parsed
1601          */
1602         public void parsedHeader(HttpField field);
1603         
1604         /* ------------------------------------------------------------ */
1605         /** Called to signal that an EOF was received unexpectedly
1606          * during the parsing of a HTTP message
1607          */
1608         public void earlyEOF();
1609 
1610         /* ------------------------------------------------------------ */
1611         /** Called to signal that a bad HTTP message has been received.
1612          * @param status The bad status to send
1613          * @param reason The textual reason for badness
1614          */
1615         public void badMessage(int status, String reason);
1616         
1617         /* ------------------------------------------------------------ */
1618         /** @return the size in bytes of the per parser header cache
1619          */
1620         public int getHeaderCacheSize();
1621     }
1622 
1623     /* ------------------------------------------------------------------------------- */
1624     /* ------------------------------------------------------------------------------- */
1625     /* ------------------------------------------------------------------------------- */
1626     public interface RequestHandler extends HttpHandler
1627     {
1628         /**
1629          * This is the method called by parser when the HTTP request line is parsed
1630          * @param method The method 
1631          * @param uri The raw bytes of the URI.  These are copied into a ByteBuffer that will not be changed until this parser is reset and reused.
1632          * @param version the http version in use
1633          * @return true if handling parsing should return.
1634          */
1635         public boolean startRequest(String method, String uri, HttpVersion version);
1636 
1637     }
1638 
1639     /* ------------------------------------------------------------------------------- */
1640     /* ------------------------------------------------------------------------------- */
1641     /* ------------------------------------------------------------------------------- */
1642     public interface ResponseHandler extends HttpHandler
1643     {
1644         /**
1645          * This is the method called by parser when the HTTP request line is parsed
1646          * @param version the http version in use
1647          * @param status the response status
1648          * @param reason the response reason phrase
1649          * @return true if handling parsing should return
1650          */
1651         public boolean startResponse(HttpVersion version, int status, String reason);
1652     }
1653 
1654     /* ------------------------------------------------------------------------------- */
1655     @SuppressWarnings("serial")
1656     private static class IllegalCharacterException extends BadMessageException
1657     {
1658         private IllegalCharacterException(State state,byte ch,ByteBuffer buffer)
1659         {
1660             super(400,String.format("Illegal character 0x%X",ch));
1661             // Bug #460642 - don't reveal buffers to end user
1662             LOG.warn(String.format("Illegal character 0x%X in state=%s for buffer %s",ch,state,BufferUtil.toDetailString(buffer)));
1663         }
1664     }
1665 }