2 // ========================================================================
3 // Copyright (c) 1995-2014 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.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.http;
21 import java.nio.ByteBuffer;
22 import java.nio.charset.StandardCharsets;
24 import org.eclipse.jetty.http.HttpTokens.EndOfContent;
25 import org.eclipse.jetty.util.ArrayTernaryTrie;
26 import org.eclipse.jetty.util.ArrayTrie;
27 import org.eclipse.jetty.util.BufferUtil;
28 import org.eclipse.jetty.util.StringUtil;
29 import org.eclipse.jetty.util.Trie;
30 import org.eclipse.jetty.util.TypeUtil;
31 import org.eclipse.jetty.util.log.Log;
32 import org.eclipse.jetty.util.log.Logger;
35 /* ------------------------------------------------------------ */
36 /** A Parser for HTTP 0.9, 1.0 and 1.1
38 * The is parser parses HTTP client and server messages from buffers
39 * passed in the {@link #parseNext(ByteBuffer)} method. The parsed
40 * elements of the HTTP message are passed as event calls to the
41 * {@link HttpHandler} instance the parser is constructed with.
42 * If the passed handler is a {@link RequestHandler} then server side
43 * parsing is performed and if it is a {@link ResponseHandler}, then
44 * client side parsing is done.
47 * The contract of the {@link HttpHandler} API is that if a call returns
48 * true then the call to {@link #parseNext(ByteBuffer)} will return as
49 * soon as possible also with a true response. Typically this indicates
50 * that the parsing has reached a stage where the caller should process
51 * the events accumulated by the handler. It is the preferred calling
52 * style that handling such as calling a servlet to process a request,
53 * should be done after a true return from {@link #parseNext(ByteBuffer)}
54 * rather than from within the scope of a call like
55 * {@link RequestHandler#messageComplete()}
58 * For performance, the parse is heavily dependent on the
59 * {@link Trie#getBest(ByteBuffer, int, int)} method to look ahead in a
60 * single pass for both the structure ( : and CRLF ) and semantic (which
61 * header and value) of a header. Specifically the static {@link HttpHeader#CACHE}
62 * is used to lookup common combinations of headers and values
63 * (eg. "Connection: close"), or just header names (eg. "Connection:" ).
64 * For headers who's value is not known statically (eg. Host, COOKIE) then a
65 * per parser dynamic Trie of {@link HttpFields} from previous parsed messages
66 * is used to help the parsing of subsequent messages.
69 * If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true,
70 * then the parser will strictly pass on the exact strings received for methods and header
71 * fields. Otherwise a fast case insensitive string lookup is used that may alter the
72 * case of the method and/or headers
75 public class HttpParser
77 public static final Logger LOG = Log.getLogger(HttpParser.class);
78 public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT");
79 public final static int INITIAL_URI_LENGTH=256;
82 * Cache of common {@link HttpField}s including: <UL>
83 * <LI>Common static combinations such as:<UL>
84 * <li>Connection: close
85 * <li>Accept-Encoding: gzip
86 * <li>Content-Length: 0
88 * <li>Combinations of Content-Type header for common mime types by common charsets
89 * <li>Most common headers with null values so that a lookup will at least
90 * determine the header name even if the name:value combination is not cached
93 public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
122 private final boolean DEBUG=LOG.isDebugEnabled(); // Cache debug to help branch prediction
123 private final HttpHandler<ByteBuffer> _handler;
124 private final RequestHandler<ByteBuffer> _requestHandler;
125 private final ResponseHandler<ByteBuffer> _responseHandler;
126 private final int _maxHeaderBytes;
127 private final boolean _strict;
128 private HttpField _field;
129 private HttpHeader _header;
130 private String _headerString;
131 private HttpHeaderValue _value;
132 private String _valueString;
133 private int _responseStatus;
134 private int _headerBytes;
135 private boolean _host;
137 /* ------------------------------------------------------------------------------- */
138 private volatile State _state=State.START;
139 private volatile boolean _eof;
140 private volatile boolean _closed;
141 private HttpMethod _method;
142 private String _methodString;
143 private HttpVersion _version;
144 private ByteBuffer _uri=ByteBuffer.allocate(INITIAL_URI_LENGTH); // Tune?
145 private EndOfContent _endOfContent;
146 private long _contentLength;
147 private long _contentPosition;
148 private int _chunkLength;
149 private int _chunkPosition;
150 private boolean _headResponse;
152 private ByteBuffer _contentChunk;
153 private Trie<HttpField> _connectionFields;
156 private final StringBuilder _string=new StringBuilder();
160 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
161 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
162 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.UPGRADE));
163 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip"));
164 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
165 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip,deflate,sdch"));
166 CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
167 CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-GB,en-US;q=0.8,en;q=0.6"));
168 CACHE.put(new HttpField(HttpHeader.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
169 CACHE.put(new HttpField(HttpHeader.ACCEPT,"*/*"));
170 CACHE.put(new HttpField(HttpHeader.ACCEPT,"image/png,image/*;q=0.8,*/*;q=0.5"));
171 CACHE.put(new HttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
172 CACHE.put(new HttpField(HttpHeader.PRAGMA,"no-cache"));
173 CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
174 CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
175 CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH,"0"));
176 CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
177 CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
178 CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
179 CACHE.put(new HttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
181 // Add common Content types as fields
182 for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/json","application/x-www-form-urlencoded"})
184 HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type);
187 for (String charset : new String[]{"UTF-8","ISO-8859-1"})
189 CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
190 CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset));
194 // Add headers with null values so HttpParser can avoid looking up name again for unknown values
195 for (HttpHeader h:HttpHeader.values())
196 if (!CACHE.put(new HttpField(h,(String)null)))
197 throw new IllegalStateException("CACHE FULL");
198 // Add some more common headers
199 CACHE.put(new HttpField(HttpHeader.REFERER,(String)null));
200 CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE,(String)null));
201 CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH,(String)null));
202 CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
203 CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
206 /* ------------------------------------------------------------------------------- */
207 public HttpParser(RequestHandler<ByteBuffer> handler)
209 this(handler,-1,__STRICT);
212 /* ------------------------------------------------------------------------------- */
213 public HttpParser(ResponseHandler<ByteBuffer> handler)
215 this(handler,-1,__STRICT);
218 /* ------------------------------------------------------------------------------- */
219 public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes)
221 this(handler,maxHeaderBytes,__STRICT);
224 /* ------------------------------------------------------------------------------- */
225 public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes)
227 this(handler,maxHeaderBytes,__STRICT);
230 /* ------------------------------------------------------------------------------- */
231 public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
234 _requestHandler=handler;
235 _responseHandler=null;
236 _maxHeaderBytes=maxHeaderBytes;
240 /* ------------------------------------------------------------------------------- */
241 public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
244 _requestHandler=null;
245 _responseHandler=handler;
246 _maxHeaderBytes=maxHeaderBytes;
250 /* ------------------------------------------------------------------------------- */
251 public long getContentLength()
253 return _contentLength;
256 /* ------------------------------------------------------------ */
257 public long getContentRead()
259 return _contentPosition;
262 /* ------------------------------------------------------------ */
263 /** Set if a HEAD response is expected
266 public void setHeadResponse(boolean head)
271 /* ------------------------------------------------------------------------------- */
272 protected void setResponseStatus(int status)
274 _responseStatus=status;
277 /* ------------------------------------------------------------------------------- */
278 public State getState()
283 /* ------------------------------------------------------------------------------- */
284 public boolean inContentState()
286 return _state.ordinal()>=State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal();
289 /* ------------------------------------------------------------------------------- */
290 public boolean inHeaderState()
292 return _state.ordinal() < State.CONTENT.ordinal();
295 /* ------------------------------------------------------------------------------- */
296 public boolean isChunking()
298 return _endOfContent==EndOfContent.CHUNKED_CONTENT;
301 /* ------------------------------------------------------------ */
302 public boolean isStart()
304 return isState(State.START);
307 /* ------------------------------------------------------------ */
308 public boolean isClosed()
310 return isState(State.CLOSED);
313 /* ------------------------------------------------------------ */
314 public boolean isIdle()
316 return isState(State.START)||isState(State.END)||isState(State.CLOSED);
319 /* ------------------------------------------------------------ */
320 public boolean isComplete()
322 return isState(State.END)||isState(State.CLOSED);
325 /* ------------------------------------------------------------------------------- */
326 public boolean isState(State state)
328 return _state == state;
331 /* ------------------------------------------------------------------------------- */
332 private static class BadMessage extends Error
334 private static final long serialVersionUID = 1L;
335 private final int _code;
336 private final String _message;
348 BadMessage(String message)
353 BadMessage(int code,String message)
361 /* ------------------------------------------------------------------------------- */
362 private byte next(ByteBuffer buffer)
364 byte ch = buffer.get();
368 if (ch!=HttpTokens.LINE_FEED)
369 throw new BadMessage("Bad EOL");
374 if (ch>=0 && ch<HttpTokens.SPACE)
376 if (ch==HttpTokens.CARRIAGE_RETURN)
378 if (buffer.hasRemaining())
380 if(_maxHeaderBytes>0 && _state.ordinal()<State.END.ordinal())
383 if (ch!=HttpTokens.LINE_FEED)
384 throw new BadMessage("Bad EOL");
389 // Can return 0 here to indicate the need for more characters,
390 // because a real 0 in the buffer would cause a BadMessage below
394 // Only LF or TAB acceptable special characters
395 else if (!(ch==HttpTokens.LINE_FEED || ch==HttpTokens.TAB))
396 throw new BadMessage("Illegal character");
402 /* ------------------------------------------------------------------------------- */
403 /* Quick lookahead for the start state looking for a request method or a HTTP version,
404 * otherwise skip white space until something else to parse.
406 private boolean quickStart(ByteBuffer buffer)
408 if (_requestHandler!=null)
410 _method = HttpMethod.lookAheadGet(buffer);
413 _methodString = _method.asString();
414 buffer.position(buffer.position()+_methodString.length()+1);
416 setState(State.SPACE1);
420 else if (_responseHandler!=null)
422 _version = HttpVersion.lookAheadGet(buffer);
425 buffer.position(buffer.position()+_version.asString().length()+1);
426 setState(State.SPACE1);
432 while (_state==State.START && buffer.hasRemaining())
436 if (ch > HttpTokens.SPACE)
438 _string.setLength(0);
439 _string.append((char)ch);
440 setState(_requestHandler!=null?State.METHOD:State.RESPONSE_VERSION);
446 throw new BadMessage();
448 // count this white space as a header byte to avoid DOS
449 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
451 LOG.warn("padding is too large >"+_maxHeaderBytes);
452 throw new BadMessage(HttpStatus.BAD_REQUEST_400);
458 /* ------------------------------------------------------------------------------- */
459 private void setString(String s)
461 _string.setLength(0);
466 /* ------------------------------------------------------------------------------- */
467 private String takeString()
469 _string.setLength(_length);
470 String s =_string.toString();
471 _string.setLength(0);
476 /* ------------------------------------------------------------------------------- */
477 /* Parse a request or response line
479 private boolean parseLine(ByteBuffer buffer)
481 boolean handle=false;
484 while (_state.ordinal()<State.HEADER.ordinal() && buffer.hasRemaining() && !handle)
486 // process each character
487 byte ch=next(buffer);
491 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
493 if (_state==State.URI)
495 LOG.warn("URI is too large >"+_maxHeaderBytes);
496 throw new BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414);
500 if (_requestHandler!=null)
501 LOG.warn("request is too large >"+_maxHeaderBytes);
503 LOG.warn("response is too large >"+_maxHeaderBytes);
504 throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
511 if (ch == HttpTokens.SPACE)
513 _length=_string.length();
514 _methodString=takeString();
515 HttpMethod method=HttpMethod.CACHE.get(_methodString);
516 if (method!=null && !_strict)
517 _methodString=method.asString();
518 setState(State.SPACE1);
520 else if (ch < HttpTokens.SPACE)
521 throw new BadMessage(ch<0?"Illegal character":"No URI");
523 _string.append((char)ch);
526 case RESPONSE_VERSION:
527 if (ch == HttpTokens.SPACE)
529 _length=_string.length();
530 String version=takeString();
531 _version=HttpVersion.CACHE.get(version);
533 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
534 setState(State.SPACE1);
536 else if (ch < HttpTokens.SPACE)
537 throw new BadMessage(ch<0?"Illegal character":"No Status");
539 _string.append((char)ch);
543 if (ch > HttpTokens.SPACE || ch<0)
545 if (_responseHandler!=null)
547 setState(State.STATUS);
548 setResponseStatus(ch-'0');
554 // quick scan for space or EoBuffer
555 if (buffer.hasArray())
557 byte[] array=buffer.array();
558 int p=buffer.arrayOffset()+buffer.position();
559 int l=buffer.arrayOffset()+buffer.limit();
561 while (i<l && array[i]>HttpTokens.SPACE)
567 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
569 LOG.warn("URI is too large >"+_maxHeaderBytes);
570 throw new BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414);
572 if (_uri.remaining()<=len)
574 ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()+2*len);
579 _uri.put(array,p-1,len+1);
580 buffer.position(i-buffer.arrayOffset());
586 else if (ch < HttpTokens.SPACE)
588 throw new BadMessage(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
593 if (ch == HttpTokens.SPACE)
595 setState(State.SPACE2);
597 else if (ch>='0' && ch<='9')
599 _responseStatus=_responseStatus*10+(ch-'0');
601 else if (ch < HttpTokens.SPACE && ch>=0)
603 handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
604 setState(State.HEADER);
608 throw new BadMessage();
613 if (ch == HttpTokens.SPACE)
615 setState(State.SPACE2);
617 else if (ch < HttpTokens.SPACE && ch>=0)
621 handle=_requestHandler.startRequest(_method,_methodString,_uri,null)||handle;
623 BufferUtil.clear(buffer);
624 handle=_handler.headerComplete()||handle;
625 handle=_handler.messageComplete()||handle;
629 if (!_uri.hasRemaining())
631 ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()*2);
641 if (ch > HttpTokens.SPACE)
643 _string.setLength(0);
644 _string.append((char)ch);
645 if (_responseHandler!=null)
648 setState(State.REASON);
652 setState(State.REQUEST_VERSION);
654 // try quick look ahead for HTTP Version
656 if (buffer.position()>0 && buffer.hasArray())
657 version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
659 version=HttpVersion.CACHE.getBest(buffer,0,buffer.remaining());
662 if (_method==HttpMethod.PROXY)
664 if (!(_requestHandler instanceof ProxyHandler))
665 throw new BadMessage();
668 String protocol=BufferUtil.toString(_uri);
669 // This is the proxy protocol, so we can assume entire first line is in buffer else 400
670 buffer.position(buffer.position()-1);
671 String sAddr = getProxyField(buffer);
672 String dAddr = getProxyField(buffer);
673 int sPort = BufferUtil.takeInt(buffer);
675 int dPort = BufferUtil.takeInt(buffer);
678 ((ProxyHandler)_requestHandler).proxied(protocol,sAddr,dAddr,sPort,dPort);
684 int pos = buffer.position()+version.asString().length()-1;
685 if (pos<buffer.limit())
687 byte n=buffer.get(pos);
688 if (n==HttpTokens.CARRIAGE_RETURN)
692 _string.setLength(0);
693 buffer.position(pos+1);
695 else if (n==HttpTokens.LINE_FEED)
698 _string.setLength(0);
699 buffer.position(pos);
705 else if (ch == HttpTokens.LINE_FEED)
707 if (_responseHandler!=null)
709 handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
710 setState(State.HEADER);
716 handle=_requestHandler.startRequest(_method,_methodString,_uri, null)||handle;
718 BufferUtil.clear(buffer);
719 handle=_handler.headerComplete()||handle;
720 handle=_handler.messageComplete()||handle;
724 throw new BadMessage();
727 case REQUEST_VERSION:
728 if (ch == HttpTokens.LINE_FEED)
732 _length=_string.length();
733 _version=HttpVersion.CACHE.get(takeString());
736 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
738 // Should we try to cache header fields?
739 if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
741 int header_cache = _handler.getHeaderCacheSize();
742 _connectionFields=new ArrayTernaryTrie<>(header_cache);
745 setState(State.HEADER);
747 handle=_requestHandler.startRequest(_method,_methodString,_uri, _version)||handle;
750 else if (ch>=HttpTokens.SPACE)
751 _string.append((char)ch);
753 throw new BadMessage();
758 if (ch == HttpTokens.LINE_FEED)
760 String reason=takeString();
762 setState(State.HEADER);
763 handle=_responseHandler.startResponse(_version, _responseStatus, reason)||handle;
766 else if (ch>=HttpTokens.SPACE)
768 _string.append((char)ch);
769 if (ch!=' '&&ch!='\t')
770 _length=_string.length();
773 throw new BadMessage();
777 throw new IllegalStateException(_state.toString());
785 private boolean handleKnownHeaders(ByteBuffer buffer)
787 boolean add_to_connection_trie=false;
791 if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
795 _contentLength=Long.parseLong(_valueString);
797 catch(NumberFormatException e)
800 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
802 if (_contentLength <= 0)
803 _endOfContent=EndOfContent.NO_CONTENT;
805 _endOfContent=EndOfContent.CONTENT_LENGTH;
809 case TRANSFER_ENCODING:
810 if (_value==HttpHeaderValue.CHUNKED)
811 _endOfContent=EndOfContent.CHUNKED_CONTENT;
814 if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
815 _endOfContent=EndOfContent.CHUNKED_CONTENT;
816 else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
818 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad chunking");
824 add_to_connection_trie=_connectionFields!=null && _field==null;
826 String host=_valueString;
828 if (host==null || host.length()==0)
830 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
833 int len=host.length();
834 loop: for (int i = len; i-- > 0;)
836 char c2 = (char)(0xff & host.charAt(i));
846 port = StringUtil.toInt(host.substring(i+1));
848 catch (NumberFormatException e)
852 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
857 if (host.charAt(0)=='[')
859 if (host.charAt(len-1)!=']')
861 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
863 host = host.substring(1,len-1);
865 else if (len!=host.length())
866 host = host.substring(0,len);
868 if (_requestHandler!=null)
869 _requestHandler.parsedHostHeader(host,port);
874 // Don't cache if not persistent
875 if (_valueString!=null && _valueString.contains("close"))
878 _connectionFields=null;
885 case ACCEPT_ENCODING:
886 case ACCEPT_LANGUAGE:
890 add_to_connection_trie=_connectionFields!=null && _field==null;
896 if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
898 _field=new HttpField(_header,_valueString);
899 _connectionFields.put(_field);
906 /* ------------------------------------------------------------------------------- */
908 * Parse the message headers and return true if the handler has signaled for a return
910 protected boolean parseHeaders(ByteBuffer buffer)
912 boolean handle=false;
915 while (_state.ordinal()<State.CONTENT.ordinal() && buffer.hasRemaining() && !handle)
917 // process each character
918 byte ch=next(buffer);
922 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
924 LOG.warn("Header is too large >"+_maxHeaderBytes);
925 throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
933 case HttpTokens.COLON:
934 case HttpTokens.SPACE:
937 // header value without name - continuation?
938 if (_valueString==null)
940 _string.setLength(0);
945 setString(_valueString);
950 setState(State.HEADER_VALUE);
956 // handler last header if any. Delayed to here just in case there was a continuation line (above)
957 if (_headerString!=null || _valueString!=null)
959 // Handle known headers
960 if (_header!=null && handleKnownHeaders(buffer))
962 _headerString=_valueString=null;
968 handle=_handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString))||handle;
970 _headerString=_valueString=null;
976 if (ch == HttpTokens.LINE_FEED)
982 // Was there a required host header?
983 if (!_host && _version!=HttpVersion.HTTP_1_0 && _requestHandler!=null)
985 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Host");
988 // is it a response that cannot have a body?
989 if (_responseHandler !=null && // response
990 (_responseStatus == 304 || // not-modified response
991 _responseStatus == 204 || // no-content response
992 _responseStatus < 200)) // 1xx response
993 _endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
995 // else if we don't know framing
996 else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
998 if (_responseStatus == 0 // request
999 || _responseStatus == 304 // not-modified response
1000 || _responseStatus == 204 // no-content response
1001 || _responseStatus < 200) // 1xx response
1002 _endOfContent=EndOfContent.NO_CONTENT;
1004 _endOfContent=EndOfContent.EOF_CONTENT;
1007 // How is the message ended?
1008 switch (_endOfContent)
1011 setState(State.EOF_CONTENT);
1012 handle=_handler.headerComplete()||handle;
1015 case CHUNKED_CONTENT:
1016 setState(State.CHUNKED_CONTENT);
1017 handle=_handler.headerComplete()||handle;
1021 handle=_handler.headerComplete()||handle;
1022 setState(State.END);
1023 handle=_handler.messageComplete()||handle;
1027 setState(State.CONTENT);
1028 handle=_handler.headerComplete()||handle;
1032 else if (ch<=HttpTokens.SPACE)
1033 throw new BadMessage();
1036 if (buffer.hasRemaining())
1038 // Try a look ahead for the known header name and value.
1039 HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
1041 field=CACHE.getBest(buffer,-1,buffer.remaining());
1050 // Have to get the fields exactly from the buffer to match case
1051 String fn=field.getName();
1052 String fv=field.getValue();
1053 n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII);
1058 v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1);
1059 field=new HttpField(field.getHeader(),n,v);
1068 _header=field.getHeader();
1074 setState(State.HEADER_VALUE);
1075 _string.setLength(0);
1077 buffer.position(buffer.position()+n.length()+1);
1083 int pos=buffer.position()+n.length()+v.length()+1;
1084 byte b=buffer.get(pos);
1086 if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
1090 setState(State.HEADER_IN_VALUE);
1092 if (b==HttpTokens.CARRIAGE_RETURN)
1095 buffer.position(pos+1);
1098 buffer.position(pos);
1103 setState(State.HEADER_IN_VALUE);
1105 buffer.position(pos);
1113 setState(State.HEADER_IN_NAME);
1114 _string.setLength(0);
1115 _string.append((char)ch);
1122 case HEADER_IN_NAME:
1123 if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED)
1125 if (_headerString==null)
1127 _headerString=takeString();
1128 _header=HttpHeader.CACHE.get(_headerString);
1132 setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE);
1136 if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB)
1140 setString(_header.asString());
1145 _string.append((char)ch);
1146 if (ch>HttpTokens.SPACE)
1147 _length=_string.length();
1151 throw new BadMessage("Illegal character");
1154 if (ch>HttpTokens.SPACE || ch<0)
1156 _string.append((char)(0xff&ch));
1157 _length=_string.length();
1158 setState(State.HEADER_IN_VALUE);
1162 if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
1165 if (ch==HttpTokens.LINE_FEED)
1170 _valueString=(_valueString==null)?takeString():(_valueString+" "+takeString());
1172 setState(State.HEADER);
1176 throw new BadMessage("Illegal character");
1178 case HEADER_IN_VALUE:
1179 if (ch>=HttpTokens.SPACE || ch<0 || ch==HttpTokens.TAB)
1181 if (_valueString!=null)
1183 setString(_valueString);
1187 _string.append((char)(0xff&ch));
1188 if (ch>HttpTokens.SPACE || ch<0)
1189 _length=_string.length();
1193 if (ch==HttpTokens.LINE_FEED)
1198 _valueString=takeString();
1201 setState(State.HEADER);
1204 throw new BadMessage("Illegal character");
1207 throw new IllegalStateException(_state.toString());
1215 /* ------------------------------------------------------------------------------- */
1217 * Parse until next Event.
1218 * @return True if an {@link RequestHandler} method was called and it returned true;
1220 public boolean parseNext(ByteBuffer buffer)
1223 LOG.debug("parseNext s={} {}",_state,BufferUtil.toDetailString(buffer));
1226 // Start a request/response
1227 if (_state==State.START)
1232 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
1234 if (quickStart(buffer))
1238 // Request/response line
1239 if (_state.ordinal()>= State.START.ordinal() && _state.ordinal()<State.HEADER.ordinal())
1241 if (parseLine(buffer))
1246 if (_state.ordinal()>= State.HEADER.ordinal() && _state.ordinal()<State.CONTENT.ordinal())
1248 if (parseHeaders(buffer))
1253 if (_state.ordinal()>= State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal())
1255 // Handle HEAD response
1256 if (_responseStatus>0 && _headResponse)
1258 setState(State.END);
1259 if (_handler.messageComplete())
1264 if (parseContent(buffer))
1269 // handle end states
1270 if (_state==State.END)
1273 while (buffer.remaining()>0 && buffer.get(buffer.position())<=HttpTokens.SPACE)
1276 else if (_state==State.CLOSED)
1278 if (BufferUtil.hasContent(buffer))
1280 // Just ignore data when closed
1281 _headerBytes+=buffer.remaining();
1282 BufferUtil.clear(buffer);
1283 if (_headerBytes>_maxHeaderBytes)
1285 // Don't want to waste time reading data of a closed request
1286 throw new IllegalStateException("too much data after closed");
1292 if (_eof && !buffer.hasRemaining())
1300 setState(State.CLOSED);
1301 _handler.earlyEOF();
1305 setState(State.CLOSED);
1309 setState(State.CLOSED);
1310 return _handler.messageComplete();
1313 case CHUNKED_CONTENT:
1317 setState(State.CLOSED);
1318 _handler.earlyEOF();
1323 LOG.debug("{} EOF in {}",this,_state);
1324 setState(State.CLOSED);
1325 _handler.badMessage(400,null);
1334 BufferUtil.clear(buffer);
1336 LOG.warn("badMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
1339 setState(State.CLOSED);
1340 _handler.badMessage(e._code, e._message);
1345 BufferUtil.clear(buffer);
1347 LOG.warn("badMessage: "+e.toString()+" for "+_handler);
1351 if (_state.ordinal()<=State.END.ordinal())
1353 setState(State.CLOSED);
1354 _handler.badMessage(400,null);
1358 _handler.earlyEOF();
1359 setState(State.CLOSED);
1366 protected boolean parseContent(ByteBuffer buffer)
1368 int remaining=buffer.remaining();
1369 if (remaining==0 && _state==State.CONTENT)
1371 long content=_contentLength - _contentPosition;
1374 setState(State.END);
1375 if (_handler.messageComplete())
1382 while (_state.ordinal() < State.END.ordinal() && remaining>0)
1387 _contentChunk=buffer.asReadOnlyBuffer();
1388 _contentPosition += remaining;
1389 buffer.position(buffer.position()+remaining);
1390 if (_handler.content(_contentChunk))
1396 long content=_contentLength - _contentPosition;
1399 setState(State.END);
1400 if (_handler.messageComplete())
1405 _contentChunk=buffer.asReadOnlyBuffer();
1407 // limit content by expected size
1408 if (remaining > content)
1410 // We can cast remaining to an int as we know that it is smaller than
1411 // or equal to length which is already an int.
1412 _contentChunk.limit(_contentChunk.position()+(int)content);
1415 _contentPosition += _contentChunk.remaining();
1416 buffer.position(buffer.position()+_contentChunk.remaining());
1418 if (_handler.content(_contentChunk))
1421 if(_contentPosition == _contentLength)
1423 setState(State.END);
1424 if (_handler.messageComplete())
1431 case CHUNKED_CONTENT:
1434 if (ch>HttpTokens.SPACE)
1436 _chunkLength=TypeUtil.convertHexDigit(ch);
1438 setState(State.CHUNK_SIZE);
1449 if (ch == HttpTokens.LINE_FEED)
1451 if (_chunkLength == 0)
1453 setState(State.END);
1454 if (_handler.messageComplete())
1458 setState(State.CHUNK);
1460 else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
1461 setState(State.CHUNK_PARAMS);
1463 _chunkLength=_chunkLength * 16 + TypeUtil.convertHexDigit(ch);
1470 if (ch == HttpTokens.LINE_FEED)
1472 if (_chunkLength == 0)
1474 setState(State.END);
1475 if (_handler.messageComplete())
1479 setState(State.CHUNK);
1486 int chunk=_chunkLength - _chunkPosition;
1489 setState(State.CHUNKED_CONTENT);
1493 _contentChunk=buffer.asReadOnlyBuffer();
1495 if (remaining > chunk)
1496 _contentChunk.limit(_contentChunk.position()+chunk);
1497 chunk=_contentChunk.remaining();
1499 _contentPosition += chunk;
1500 _chunkPosition += chunk;
1501 buffer.position(buffer.position()+chunk);
1502 if (_handler.content(_contentChunk))
1510 BufferUtil.clear(buffer);
1519 remaining=buffer.remaining();
1524 /* ------------------------------------------------------------------------------- */
1525 public boolean isAtEOF()
1531 /* ------------------------------------------------------------------------------- */
1536 LOG.debug("atEOF {}", this);
1540 /* ------------------------------------------------------------------------------- */
1544 LOG.debug("close {}", this);
1545 setState(State.CLOSED);
1548 /* ------------------------------------------------------------------------------- */
1552 LOG.debug("reset {}", this);
1554 if (_state==State.CLOSED)
1558 setState(State.CLOSED);
1562 setState(State.START);
1563 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
1572 /* ------------------------------------------------------------------------------- */
1573 protected void setState(State state)
1576 LOG.debug("{} --> {}",_state,state);
1580 /* ------------------------------------------------------------------------------- */
1582 public String toString()
1584 return String.format("%s{s=%s,%d of %d}",
1585 getClass().getSimpleName(),
1591 /* ------------------------------------------------------------ */
1592 /* ------------------------------------------------------------ */
1593 /* ------------------------------------------------------------ */
1594 /* Event Handler interface
1595 * These methods return true if the caller should process the events
1596 * so far received (eg return from parseNext and call HttpChannel.handle).
1597 * If multiple callbacks are called in sequence (eg
1598 * headerComplete then messageComplete) from the same point in the parsing
1599 * then it is sufficient for the caller to process the events only once.
1601 public interface HttpHandler<T>
1603 public boolean content(T item);
1605 public boolean headerComplete();
1607 public boolean messageComplete();
1610 * This is the method called by parser when a HTTP Header name and value is found
1611 * @param field The field parsed
1612 * @return True if the parser should return to its caller
1614 public boolean parsedHeader(HttpField field);
1616 /* ------------------------------------------------------------ */
1617 /** Called to signal that an EOF was received unexpectedly
1618 * during the parsing of a HTTP message
1620 public void earlyEOF();
1622 /* ------------------------------------------------------------ */
1623 /** Called to signal that a bad HTTP message has been received.
1624 * @param status The bad status to send
1625 * @param reason The textual reason for badness
1627 public void badMessage(int status, String reason);
1629 /* ------------------------------------------------------------ */
1630 /** @return the size in bytes of the per parser header cache
1632 public int getHeaderCacheSize();
1635 public interface ProxyHandler
1637 void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
1640 public interface RequestHandler<T> extends HttpHandler<T>
1643 * This is the method called by parser when the HTTP request line is parsed
1644 * @param method The method as enum if of a known type
1645 * @param methodString The method as a string
1646 * @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.
1648 * @return true if handling parsing should return.
1650 public abstract boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion version);
1653 * This is the method called by the parser after it has parsed the host header (and checked it's format). This is
1654 * called after the {@link HttpHandler#parsedHeader(HttpField)} methods and before
1655 * HttpHandler#headerComplete();
1657 public abstract boolean parsedHostHeader(String host,int port);
1660 public interface ResponseHandler<T> extends HttpHandler<T>
1663 * This is the method called by parser when the HTTP request line is parsed
1665 public abstract boolean startResponse(HttpVersion version, int status, String reason);
1668 public Trie<HttpField> getFieldCache()
1670 return _connectionFields;
1673 private String getProxyField(ByteBuffer buffer)
1675 _string.setLength(0);
1678 while (buffer.hasRemaining())
1680 // process each character
1681 byte ch=next(buffer);
1683 return _string.toString();
1684 _string.append((char)ch);
1686 throw new BadMessage();