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.
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 static org.eclipse.jetty.http.HttpTokens.*;
23 import java.nio.ByteBuffer;
24 import java.nio.charset.StandardCharsets;
26 import org.eclipse.jetty.http.HttpTokens.EndOfContent;
27 import org.eclipse.jetty.util.ArrayTernaryTrie;
28 import org.eclipse.jetty.util.ArrayTrie;
29 import org.eclipse.jetty.util.BufferUtil;
30 import org.eclipse.jetty.util.StringUtil;
31 import org.eclipse.jetty.util.Trie;
32 import org.eclipse.jetty.util.TypeUtil;
33 import org.eclipse.jetty.util.log.Log;
34 import org.eclipse.jetty.util.log.Logger;
37 /* ------------------------------------------------------------ */
38 /** A Parser for HTTP 0.9, 1.0 and 1.1
40 * This parser parses HTTP client and server messages from buffers
41 * passed in the {@link #parseNext(ByteBuffer)} method. The parsed
42 * elements of the HTTP message are passed as event calls to the
43 * {@link HttpHandler} instance the parser is constructed with.
44 * If the passed handler is a {@link RequestHandler} then server side
45 * parsing is performed and if it is a {@link ResponseHandler}, then
46 * client side parsing is done.
49 * The contract of the {@link HttpHandler} API is that if a call returns
50 * true then the call to {@link #parseNext(ByteBuffer)} will return as
51 * soon as possible also with a true response. Typically this indicates
52 * that the parsing has reached a stage where the caller should process
53 * the events accumulated by the handler. It is the preferred calling
54 * style that handling such as calling a servlet to process a request,
55 * should be done after a true return from {@link #parseNext(ByteBuffer)}
56 * rather than from within the scope of a call like
57 * {@link RequestHandler#messageComplete()}
60 * For performance, the parse is heavily dependent on the
61 * {@link Trie#getBest(ByteBuffer, int, int)} method to look ahead in a
62 * single pass for both the structure ( : and CRLF ) and semantic (which
63 * header and value) of a header. Specifically the static {@link HttpHeader#CACHE}
64 * is used to lookup common combinations of headers and values
65 * (eg. "Connection: close"), or just header names (eg. "Connection:" ).
66 * For headers who's value is not known statically (eg. Host, COOKIE) then a
67 * per parser dynamic Trie of {@link HttpFields} from previous parsed messages
68 * is used to help the parsing of subsequent messages.
71 * If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true,
72 * then the parser will strictly pass on the exact strings received for methods and header
73 * fields. Otherwise a fast case insensitive string lookup is used that may alter the
74 * case of the method and/or headers
77 public class HttpParser
79 public static final Logger LOG = Log.getLogger(HttpParser.class);
80 public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT");
81 public final static int INITIAL_URI_LENGTH=256;
84 * Cache of common {@link HttpField}s including: <UL>
85 * <LI>Common static combinations such as:<UL>
86 * <li>Connection: close
87 * <li>Accept-Encoding: gzip
88 * <li>Content-Length: 0
90 * <li>Combinations of Content-Type header for common mime types by common charsets
91 * <li>Most common headers with null values so that a lookup will at least
92 * determine the header name even if the name:value combination is not cached
95 public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
125 private final boolean DEBUG=LOG.isDebugEnabled(); // Cache debug to help branch prediction
126 private final HttpHandler<ByteBuffer> _handler;
127 private final RequestHandler<ByteBuffer> _requestHandler;
128 private final ResponseHandler<ByteBuffer> _responseHandler;
129 private final int _maxHeaderBytes;
130 private final boolean _strict;
131 private HttpField _field;
132 private HttpHeader _header;
133 private String _headerString;
134 private HttpHeaderValue _value;
135 private String _valueString;
136 private int _responseStatus;
137 private int _headerBytes;
138 private boolean _host;
140 /* ------------------------------------------------------------------------------- */
141 private volatile State _state=State.START;
142 private volatile boolean _eof;
143 private volatile boolean _closed;
144 private HttpMethod _method;
145 private String _methodString;
146 private HttpVersion _version;
147 private ByteBuffer _uri=ByteBuffer.allocate(INITIAL_URI_LENGTH); // Tune?
148 private EndOfContent _endOfContent;
149 private long _contentLength;
150 private long _contentPosition;
151 private int _chunkLength;
152 private int _chunkPosition;
153 private boolean _headResponse;
155 private ByteBuffer _contentChunk;
156 private Trie<HttpField> _connectionFields;
159 private final StringBuilder _string=new StringBuilder();
163 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
164 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
165 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.UPGRADE));
166 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip"));
167 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
168 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip,deflate,sdch"));
169 CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
170 CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-GB,en-US;q=0.8,en;q=0.6"));
171 CACHE.put(new HttpField(HttpHeader.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
172 CACHE.put(new HttpField(HttpHeader.ACCEPT,"*/*"));
173 CACHE.put(new HttpField(HttpHeader.ACCEPT,"image/png,image/*;q=0.8,*/*;q=0.5"));
174 CACHE.put(new HttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
175 CACHE.put(new HttpField(HttpHeader.PRAGMA,"no-cache"));
176 CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
177 CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
178 CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH,"0"));
179 CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
180 CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
181 CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
182 CACHE.put(new HttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
184 // Add common Content types as fields
185 for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/json","application/x-www-form-urlencoded"})
187 HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type);
190 for (String charset : new String[]{"UTF-8","ISO-8859-1"})
192 CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
193 CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset));
197 // Add headers with null values so HttpParser can avoid looking up name again for unknown values
198 for (HttpHeader h:HttpHeader.values())
199 if (!CACHE.put(new HttpField(h,(String)null)))
200 throw new IllegalStateException("CACHE FULL");
201 // Add some more common headers
202 CACHE.put(new HttpField(HttpHeader.REFERER,(String)null));
203 CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE,(String)null));
204 CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH,(String)null));
205 CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
206 CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
209 /* ------------------------------------------------------------------------------- */
210 public HttpParser(RequestHandler<ByteBuffer> handler)
212 this(handler,-1,__STRICT);
215 /* ------------------------------------------------------------------------------- */
216 public HttpParser(ResponseHandler<ByteBuffer> handler)
218 this(handler,-1,__STRICT);
221 /* ------------------------------------------------------------------------------- */
222 public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes)
224 this(handler,maxHeaderBytes,__STRICT);
227 /* ------------------------------------------------------------------------------- */
228 public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes)
230 this(handler,maxHeaderBytes,__STRICT);
233 /* ------------------------------------------------------------------------------- */
234 public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
237 _requestHandler=handler;
238 _responseHandler=null;
239 _maxHeaderBytes=maxHeaderBytes;
243 /* ------------------------------------------------------------------------------- */
244 public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
247 _requestHandler=null;
248 _responseHandler=handler;
249 _maxHeaderBytes=maxHeaderBytes;
253 /* ------------------------------------------------------------------------------- */
254 public long getContentLength()
256 return _contentLength;
259 /* ------------------------------------------------------------ */
260 public long getContentRead()
262 return _contentPosition;
265 /* ------------------------------------------------------------ */
266 /** Set if a HEAD response is expected
269 public void setHeadResponse(boolean head)
274 /* ------------------------------------------------------------------------------- */
275 protected void setResponseStatus(int status)
277 _responseStatus=status;
280 /* ------------------------------------------------------------------------------- */
281 public State getState()
286 /* ------------------------------------------------------------------------------- */
287 public boolean inContentState()
289 return _state.ordinal()>=State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal();
292 /* ------------------------------------------------------------------------------- */
293 public boolean inHeaderState()
295 return _state.ordinal() < State.CONTENT.ordinal();
298 /* ------------------------------------------------------------------------------- */
299 public boolean isChunking()
301 return _endOfContent==EndOfContent.CHUNKED_CONTENT;
304 /* ------------------------------------------------------------ */
305 public boolean isStart()
307 return isState(State.START);
310 /* ------------------------------------------------------------ */
311 public boolean isClosed()
313 return isState(State.CLOSED);
316 /* ------------------------------------------------------------ */
317 public boolean isIdle()
319 return isState(State.START)||isState(State.END)||isState(State.CLOSED);
322 /* ------------------------------------------------------------ */
323 public boolean isComplete()
325 return isState(State.END)||isState(State.CLOSED);
328 /* ------------------------------------------------------------------------------- */
329 public boolean isState(State state)
331 return _state == state;
334 /* ------------------------------------------------------------------------------- */
335 @SuppressWarnings("serial")
336 private static class BadMessageException extends RuntimeException
338 private final int _code;
340 private BadMessageException()
345 private BadMessageException(int code)
350 private BadMessageException(String message)
355 private BadMessageException(int code,String message)
362 /* ------------------------------------------------------------------------------- */
363 private byte next(ByteBuffer buffer)
365 byte ch = buffer.get();
370 throw new BadMessageException("Bad EOL");
375 if (ch>=0 && ch<SPACE)
377 if (ch==CARRIAGE_RETURN)
379 if (buffer.hasRemaining())
381 if(_maxHeaderBytes>0 && _state.ordinal()<State.END.ordinal())
385 throw new BadMessageException("Bad EOL");
390 // Can return 0 here to indicate the need for more characters,
391 // because a real 0 in the buffer would cause a BadMessage below
395 // Only LF or TAB acceptable special characters
396 else if (!(ch==LINE_FEED || ch==TAB))
397 throw new IllegalCharacterException(_state,ch,buffer);
403 /* ------------------------------------------------------------------------------- */
404 /* Quick lookahead for the start state looking for a request method or a HTTP version,
405 * otherwise skip white space until something else to parse.
407 private boolean quickStart(ByteBuffer buffer)
409 if (_requestHandler!=null)
411 _method = HttpMethod.lookAheadGet(buffer);
414 _methodString = _method.asString();
415 buffer.position(buffer.position()+_methodString.length()+1);
417 setState(State.SPACE1);
421 else if (_responseHandler!=null)
423 _version = HttpVersion.lookAheadGet(buffer);
426 buffer.position(buffer.position()+_version.asString().length()+1);
427 setState(State.SPACE1);
433 while (_state==State.START && buffer.hasRemaining())
439 _string.setLength(0);
440 _string.append((char)ch);
441 setState(_requestHandler!=null?State.METHOD:State.RESPONSE_VERSION);
447 throw new BadMessageException();
449 // count this white space as a header byte to avoid DOS
450 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
452 LOG.warn("padding is too large >"+_maxHeaderBytes);
453 throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
459 /* ------------------------------------------------------------------------------- */
460 private void setString(String s)
462 _string.setLength(0);
467 /* ------------------------------------------------------------------------------- */
468 private String takeString()
470 _string.setLength(_length);
471 String s =_string.toString();
472 _string.setLength(0);
477 /* ------------------------------------------------------------------------------- */
478 /* Parse a request or response line
480 private boolean parseLine(ByteBuffer buffer)
482 boolean handle=false;
485 while (_state.ordinal()<State.HEADER.ordinal() && buffer.hasRemaining() && !handle)
487 // process each character
488 byte ch=next(buffer);
492 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
494 if (_state==State.URI)
496 LOG.warn("URI is too large >"+_maxHeaderBytes);
497 throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
501 if (_requestHandler!=null)
502 LOG.warn("request is too large >"+_maxHeaderBytes);
504 LOG.warn("response is too large >"+_maxHeaderBytes);
505 throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
514 _length=_string.length();
515 _methodString=takeString();
516 HttpMethod method=HttpMethod.CACHE.get(_methodString);
517 if (method!=null && !_strict)
518 _methodString=method.asString();
519 setState(State.SPACE1);
524 throw new BadMessageException("No URI");
526 throw new IllegalCharacterException(_state,ch,buffer);
529 _string.append((char)ch);
532 case RESPONSE_VERSION:
533 if (ch == HttpTokens.SPACE)
535 _length=_string.length();
536 String version=takeString();
537 _version=HttpVersion.CACHE.get(version);
539 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
540 setState(State.SPACE1);
542 else if (ch < HttpTokens.SPACE)
543 throw new IllegalCharacterException(_state,ch,buffer);
545 _string.append((char)ch);
549 if (ch > HttpTokens.SPACE || ch<0)
551 if (_responseHandler!=null)
553 setState(State.STATUS);
554 setResponseStatus(ch-'0');
560 // quick scan for space or EoBuffer
561 if (buffer.hasArray())
563 byte[] array=buffer.array();
564 int p=buffer.arrayOffset()+buffer.position();
565 int l=buffer.arrayOffset()+buffer.limit();
567 while (i<l && array[i]>HttpTokens.SPACE)
573 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
575 LOG.warn("URI is too large >"+_maxHeaderBytes);
576 throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
578 if (_uri.remaining()<=len)
580 ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()+2*len);
585 _uri.put(array,p-1,len+1);
586 buffer.position(i-buffer.arrayOffset());
592 else if (ch < HttpTokens.SPACE)
594 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
599 if (ch == HttpTokens.SPACE)
601 setState(State.SPACE2);
603 else if (ch>='0' && ch<='9')
605 _responseStatus=_responseStatus*10+(ch-'0');
607 else if (ch < HttpTokens.SPACE && ch>=0)
609 handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
610 setState(State.HEADER);
614 throw new BadMessageException();
619 if (ch == HttpTokens.SPACE)
621 setState(State.SPACE2);
623 else if (ch < HttpTokens.SPACE && ch>=0)
627 handle=_requestHandler.startRequest(_method,_methodString,_uri,null)||handle;
629 BufferUtil.clear(buffer);
630 handle=_handler.headerComplete()||handle;
631 handle=_handler.messageComplete()||handle;
636 if (!_uri.hasRemaining())
638 ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()*2);
648 if (ch > HttpTokens.SPACE)
650 _string.setLength(0);
651 _string.append((char)ch);
652 if (_responseHandler!=null)
655 setState(State.REASON);
659 setState(State.REQUEST_VERSION);
661 // try quick look ahead for HTTP Version
663 if (buffer.position()>0 && buffer.hasArray())
664 version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
666 version=HttpVersion.CACHE.getBest(buffer,0,buffer.remaining());
669 if (_method==HttpMethod.PROXY)
671 if (!(_requestHandler instanceof ProxyHandler))
672 throw new BadMessageException();
675 String protocol=BufferUtil.toString(_uri);
676 // This is the proxy protocol, so we can assume entire first line is in buffer else 400
677 buffer.position(buffer.position()-1);
678 String sAddr = getProxyField(buffer);
679 String dAddr = getProxyField(buffer);
680 int sPort = BufferUtil.takeInt(buffer);
682 int dPort = BufferUtil.takeInt(buffer);
685 ((ProxyHandler)_requestHandler).proxied(protocol,sAddr,dAddr,sPort,dPort);
691 int pos = buffer.position()+version.asString().length()-1;
692 if (pos<buffer.limit())
694 byte n=buffer.get(pos);
695 if (n==HttpTokens.CARRIAGE_RETURN)
699 _string.setLength(0);
700 buffer.position(pos+1);
702 else if (n==HttpTokens.LINE_FEED)
705 _string.setLength(0);
706 buffer.position(pos);
712 else if (ch == HttpTokens.LINE_FEED)
714 if (_responseHandler!=null)
716 handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
717 setState(State.HEADER);
723 handle=_requestHandler.startRequest(_method,_methodString,_uri, null)||handle;
725 BufferUtil.clear(buffer);
726 handle=_handler.headerComplete()||handle;
727 handle=_handler.messageComplete()||handle;
732 throw new BadMessageException();
735 case REQUEST_VERSION:
736 if (ch == HttpTokens.LINE_FEED)
740 _length=_string.length();
741 _version=HttpVersion.CACHE.get(takeString());
744 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
746 // Should we try to cache header fields?
747 if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
749 int header_cache = _handler.getHeaderCacheSize();
750 _connectionFields=new ArrayTernaryTrie<>(header_cache);
753 setState(State.HEADER);
755 handle=_requestHandler.startRequest(_method,_methodString,_uri, _version)||handle;
758 else if (ch>=HttpTokens.SPACE)
759 _string.append((char)ch);
761 throw new BadMessageException();
766 if (ch == HttpTokens.LINE_FEED)
768 String reason=takeString();
770 setState(State.HEADER);
771 handle=_responseHandler.startResponse(_version, _responseStatus, reason)||handle;
774 else if (ch>=HttpTokens.SPACE)
776 _string.append((char)ch);
777 if (ch!=' '&&ch!='\t')
778 _length=_string.length();
781 throw new BadMessageException();
785 throw new IllegalStateException(_state.toString());
793 private boolean handleKnownHeaders(ByteBuffer buffer)
795 boolean add_to_connection_trie=false;
799 if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
803 _contentLength=Long.parseLong(_valueString);
805 catch(NumberFormatException e)
808 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
810 if (_contentLength <= 0)
811 _endOfContent=EndOfContent.NO_CONTENT;
813 _endOfContent=EndOfContent.CONTENT_LENGTH;
817 case TRANSFER_ENCODING:
818 if (_value==HttpHeaderValue.CHUNKED)
819 _endOfContent=EndOfContent.CHUNKED_CONTENT;
822 if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
823 _endOfContent=EndOfContent.CHUNKED_CONTENT;
824 else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
826 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
832 add_to_connection_trie=_connectionFields!=null && _field==null;
834 String host=_valueString;
836 if (host==null || host.length()==0)
838 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Host header");
841 int len=host.length();
842 loop: for (int i = len; i-- > 0;)
844 char c2 = (char)(0xff & host.charAt(i));
854 port = StringUtil.toInt(host.substring(i+1));
856 catch (NumberFormatException e)
860 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Host header");
865 if (host.charAt(0)=='[')
867 if (host.charAt(len-1)!=']')
869 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
871 host = host.substring(0,len);
873 else if (len!=host.length())
874 host = host.substring(0,len);
876 if (_requestHandler!=null)
877 _requestHandler.parsedHostHeader(host,port);
882 // Don't cache if not persistent
883 if (_valueString!=null && _valueString.contains("close"))
886 _connectionFields=null;
893 case ACCEPT_ENCODING:
894 case ACCEPT_LANGUAGE:
898 add_to_connection_trie=_connectionFields!=null && _field==null;
904 if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
906 _field=new HttpField(_header,_valueString);
907 _connectionFields.put(_field);
914 /* ------------------------------------------------------------------------------- */
916 * Parse the message headers and return true if the handler has signaled for a return
918 protected boolean parseHeaders(ByteBuffer buffer)
920 boolean handle=false;
923 while (_state.ordinal()<State.CONTENT.ordinal() && buffer.hasRemaining() && !handle)
925 // process each character
926 byte ch=next(buffer);
930 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
932 LOG.warn("Header is too large >"+_maxHeaderBytes);
933 throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
941 case HttpTokens.COLON:
942 case HttpTokens.SPACE:
945 // header value without name - continuation?
946 if (_valueString==null)
948 _string.setLength(0);
953 setString(_valueString);
958 setState(State.HEADER_VALUE);
964 // handler last header if any. Delayed to here just in case there was a continuation line (above)
965 if (_headerString!=null || _valueString!=null)
967 // Handle known headers
968 if (_header!=null && handleKnownHeaders(buffer))
970 _headerString=_valueString=null;
976 handle=_handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString))||handle;
978 _headerString=_valueString=null;
984 if (ch == HttpTokens.LINE_FEED)
990 // Was there a required host header?
991 if (!_host && _version==HttpVersion.HTTP_1_1 && _requestHandler!=null)
993 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"No Host");
996 // is it a response that cannot have a body?
997 if (_responseHandler !=null && // response
998 (_responseStatus == 304 || // not-modified response
999 _responseStatus == 204 || // no-content response
1000 _responseStatus < 200)) // 1xx response
1001 _endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
1003 // else if we don't know framing
1004 else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
1006 if (_responseStatus == 0 // request
1007 || _responseStatus == 304 // not-modified response
1008 || _responseStatus == 204 // no-content response
1009 || _responseStatus < 200) // 1xx response
1010 _endOfContent=EndOfContent.NO_CONTENT;
1012 _endOfContent=EndOfContent.EOF_CONTENT;
1015 // How is the message ended?
1016 switch (_endOfContent)
1019 setState(State.EOF_CONTENT);
1020 handle=_handler.headerComplete()||handle;
1023 case CHUNKED_CONTENT:
1024 setState(State.CHUNKED_CONTENT);
1025 handle=_handler.headerComplete()||handle;
1029 handle=_handler.headerComplete()||handle;
1030 setState(State.END);
1031 handle=_handler.messageComplete()||handle;
1035 setState(State.CONTENT);
1036 handle=_handler.headerComplete()||handle;
1040 else if (ch<=HttpTokens.SPACE)
1041 throw new BadMessageException();
1044 if (buffer.hasRemaining())
1046 // Try a look ahead for the known header name and value.
1047 HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
1049 field=CACHE.getBest(buffer,-1,buffer.remaining());
1058 // Have to get the fields exactly from the buffer to match case
1059 String fn=field.getName();
1060 String fv=field.getValue();
1061 n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII);
1066 v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1);
1067 field=new HttpField(field.getHeader(),n,v);
1076 _header=field.getHeader();
1082 setState(State.HEADER_VALUE);
1083 _string.setLength(0);
1085 buffer.position(buffer.position()+n.length()+1);
1091 int pos=buffer.position()+n.length()+v.length()+1;
1092 byte b=buffer.get(pos);
1094 if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
1098 setState(State.HEADER_IN_VALUE);
1100 if (b==HttpTokens.CARRIAGE_RETURN)
1103 buffer.position(pos+1);
1106 buffer.position(pos);
1111 setState(State.HEADER_IN_VALUE);
1113 buffer.position(pos);
1121 setState(State.HEADER_IN_NAME);
1122 _string.setLength(0);
1123 _string.append((char)ch);
1130 case HEADER_IN_NAME:
1131 if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED)
1133 if (_headerString==null)
1135 _headerString=takeString();
1136 _header=HttpHeader.CACHE.get(_headerString);
1140 setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE);
1144 if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB)
1148 setString(_header.asString());
1153 _string.append((char)ch);
1154 if (ch>HttpTokens.SPACE)
1155 _length=_string.length();
1159 throw new IllegalCharacterException(_state,ch,buffer);
1162 if (ch>HttpTokens.SPACE || ch<0)
1164 _string.append((char)(0xff&ch));
1165 _length=_string.length();
1166 setState(State.HEADER_IN_VALUE);
1170 if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
1173 if (ch==HttpTokens.LINE_FEED)
1178 _valueString=(_valueString==null)?takeString():(_valueString+" "+takeString());
1180 setState(State.HEADER);
1184 throw new IllegalCharacterException(_state,ch,buffer);
1186 case HEADER_IN_VALUE:
1187 if (ch>=HttpTokens.SPACE || ch<0 || ch==HttpTokens.TAB)
1189 if (_valueString!=null)
1191 setString(_valueString);
1195 _string.append((char)(0xff&ch));
1196 if (ch>HttpTokens.SPACE || ch<0)
1197 _length=_string.length();
1201 if (ch==HttpTokens.LINE_FEED)
1206 _valueString=takeString();
1209 setState(State.HEADER);
1213 throw new IllegalCharacterException(_state,ch,buffer);
1216 throw new IllegalStateException(_state.toString());
1224 /* ------------------------------------------------------------------------------- */
1226 * Parse until next Event.
1227 * @return True if an {@link RequestHandler} method was called and it returned true;
1229 public boolean parseNext(ByteBuffer buffer)
1232 LOG.debug("parseNext s={} {}",_state,BufferUtil.toDetailString(buffer));
1235 // Start a request/response
1236 if (_state==State.START)
1241 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
1243 if (quickStart(buffer))
1247 // Request/response line
1248 if (_state.ordinal()>= State.START.ordinal() && _state.ordinal()<State.HEADER.ordinal())
1250 if (parseLine(buffer))
1255 if (_state.ordinal()>= State.HEADER.ordinal() && _state.ordinal()<State.CONTENT.ordinal())
1257 if (parseHeaders(buffer))
1262 if (_state.ordinal()>= State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal())
1264 // Handle HEAD response
1265 if (_responseStatus>0 && _headResponse)
1267 setState(State.END);
1268 return _handler.messageComplete();
1272 if (parseContent(buffer))
1277 // handle end states
1278 if (_state==State.END)
1281 while (buffer.remaining()>0 && buffer.get(buffer.position())<=HttpTokens.SPACE)
1284 else if (_state==State.CLOSED)
1286 if (BufferUtil.hasContent(buffer))
1288 // Just ignore data when closed
1289 _headerBytes+=buffer.remaining();
1290 BufferUtil.clear(buffer);
1291 if (_maxHeaderBytes>0 && _headerBytes>_maxHeaderBytes)
1293 // Don't want to waste time reading data of a closed request
1294 throw new IllegalStateException("too much data after closed");
1300 if (_eof && !buffer.hasRemaining())
1308 setState(State.CLOSED);
1309 _handler.earlyEOF();
1313 setState(State.CLOSED);
1317 setState(State.CLOSED);
1318 return _handler.messageComplete();
1321 case CHUNKED_CONTENT:
1325 setState(State.CLOSED);
1326 _handler.earlyEOF();
1331 LOG.debug("{} EOF in {}",this,_state);
1332 setState(State.CLOSED);
1333 _handler.badMessage(400,null);
1340 catch(BadMessageException e)
1342 BufferUtil.clear(buffer);
1344 LOG.warn("badMessage: "+e._code+(e.getMessage()!=null?" "+e.getMessage():"")+" for "+_handler);
1347 setState(State.CLOSED);
1348 _handler.badMessage(e._code, e.getMessage());
1353 BufferUtil.clear(buffer);
1355 LOG.warn("badMessage: "+e.toString()+" for "+_handler);
1359 if (_state.ordinal()<=State.END.ordinal())
1361 setState(State.CLOSED);
1362 _handler.badMessage(400,null);
1366 _handler.earlyEOF();
1367 setState(State.CLOSED);
1374 protected boolean parseContent(ByteBuffer buffer)
1376 int remaining=buffer.remaining();
1377 if (remaining==0 && _state==State.CONTENT)
1379 long content=_contentLength - _contentPosition;
1382 setState(State.END);
1383 return _handler.messageComplete();
1389 while (_state.ordinal() < State.END.ordinal() && remaining>0)
1394 _contentChunk=buffer.asReadOnlyBuffer();
1395 _contentPosition += remaining;
1396 buffer.position(buffer.position()+remaining);
1397 if (_handler.content(_contentChunk))
1403 long content=_contentLength - _contentPosition;
1406 setState(State.END);
1407 return _handler.messageComplete();
1411 _contentChunk=buffer.asReadOnlyBuffer();
1413 // limit content by expected size
1414 if (remaining > content)
1416 // We can cast remaining to an int as we know that it is smaller than
1417 // or equal to length which is already an int.
1418 _contentChunk.limit(_contentChunk.position()+(int)content);
1421 _contentPosition += _contentChunk.remaining();
1422 buffer.position(buffer.position()+_contentChunk.remaining());
1424 if (_handler.content(_contentChunk))
1427 if(_contentPosition == _contentLength)
1429 setState(State.END);
1430 return _handler.messageComplete();
1436 case CHUNKED_CONTENT:
1439 if (ch>HttpTokens.SPACE)
1441 _chunkLength=TypeUtil.convertHexDigit(ch);
1443 setState(State.CHUNK_SIZE);
1454 if (ch == HttpTokens.LINE_FEED)
1456 if (_chunkLength == 0)
1457 setState(State.CHUNK_END);
1459 setState(State.CHUNK);
1461 else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
1462 setState(State.CHUNK_PARAMS);
1464 _chunkLength=_chunkLength * 16 + TypeUtil.convertHexDigit(ch);
1471 if (ch == HttpTokens.LINE_FEED)
1473 if (_chunkLength == 0)
1474 setState(State.CHUNK_END);
1476 setState(State.CHUNK);
1483 int chunk=_chunkLength - _chunkPosition;
1486 setState(State.CHUNKED_CONTENT);
1490 _contentChunk=buffer.asReadOnlyBuffer();
1492 if (remaining > chunk)
1493 _contentChunk.limit(_contentChunk.position()+chunk);
1494 chunk=_contentChunk.remaining();
1496 _contentPosition += chunk;
1497 _chunkPosition += chunk;
1498 buffer.position(buffer.position()+chunk);
1499 if (_handler.content(_contentChunk))
1507 // TODO handle chunk trailer
1511 if (ch == HttpTokens.LINE_FEED)
1513 setState(State.END);
1514 return _handler.messageComplete();
1516 throw new IllegalCharacterException(_state,ch,buffer);
1521 BufferUtil.clear(buffer);
1530 remaining=buffer.remaining();
1535 /* ------------------------------------------------------------------------------- */
1536 public boolean isAtEOF()
1542 /* ------------------------------------------------------------------------------- */
1547 LOG.debug("atEOF {}", this);
1551 /* ------------------------------------------------------------------------------- */
1555 LOG.debug("close {}", this);
1556 setState(State.CLOSED);
1559 /* ------------------------------------------------------------------------------- */
1563 LOG.debug("reset {}", this);
1565 if (_state==State.CLOSED)
1569 setState(State.CLOSED);
1573 setState(State.START);
1574 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
1583 /* ------------------------------------------------------------------------------- */
1584 protected void setState(State state)
1587 LOG.debug("{} --> {}",_state,state);
1591 /* ------------------------------------------------------------------------------- */
1592 public Trie<HttpField> getFieldCache()
1594 return _connectionFields;
1597 /* ------------------------------------------------------------------------------- */
1598 private String getProxyField(ByteBuffer buffer)
1600 _string.setLength(0);
1603 while (buffer.hasRemaining())
1605 // process each character
1606 byte ch=next(buffer);
1608 return _string.toString();
1609 _string.append((char)ch);
1611 throw new BadMessageException();
1614 /* ------------------------------------------------------------------------------- */
1616 public String toString()
1618 return String.format("%s{s=%s,%d of %d}",
1619 getClass().getSimpleName(),
1625 /* ------------------------------------------------------------ */
1626 /* ------------------------------------------------------------ */
1627 /* ------------------------------------------------------------ */
1628 /* Event Handler interface
1629 * These methods return true if the caller should process the events
1630 * so far received (eg return from parseNext and call HttpChannel.handle).
1631 * If multiple callbacks are called in sequence (eg
1632 * headerComplete then messageComplete) from the same point in the parsing
1633 * then it is sufficient for the caller to process the events only once.
1635 public interface HttpHandler<T>
1637 public boolean content(T item);
1639 public boolean headerComplete();
1641 public boolean messageComplete();
1644 * This is the method called by parser when a HTTP Header name and value is found
1645 * @param field The field parsed
1646 * @return True if the parser should return to its caller
1648 public boolean parsedHeader(HttpField field);
1650 /* ------------------------------------------------------------ */
1651 /** Called to signal that an EOF was received unexpectedly
1652 * during the parsing of a HTTP message
1654 public void earlyEOF();
1656 /* ------------------------------------------------------------ */
1657 /** Called to signal that a bad HTTP message has been received.
1658 * @param status The bad status to send
1659 * @param reason The textual reason for badness
1661 public void badMessage(int status, String reason);
1663 /* ------------------------------------------------------------ */
1664 /** @return the size in bytes of the per parser header cache
1666 public int getHeaderCacheSize();
1669 /* ------------------------------------------------------------------------------- */
1670 /* ------------------------------------------------------------------------------- */
1671 /* ------------------------------------------------------------------------------- */
1672 public interface ProxyHandler
1674 void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
1677 /* ------------------------------------------------------------------------------- */
1678 /* ------------------------------------------------------------------------------- */
1679 /* ------------------------------------------------------------------------------- */
1680 public interface RequestHandler<T> extends HttpHandler<T>
1683 * This is the method called by parser when the HTTP request line is parsed
1684 * @param method The method as enum if of a known type
1685 * @param methodString The method as a string
1686 * @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.
1688 * @return true if handling parsing should return.
1690 public abstract boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion version);
1693 * This is the method called by the parser after it has parsed the host header (and checked it's format). This is
1694 * called after the {@link HttpHandler#parsedHeader(HttpField)} methods and before
1695 * HttpHandler#headerComplete();
1697 public abstract boolean parsedHostHeader(String host,int port);
1700 /* ------------------------------------------------------------------------------- */
1701 /* ------------------------------------------------------------------------------- */
1702 /* ------------------------------------------------------------------------------- */
1703 public interface ResponseHandler<T> extends HttpHandler<T>
1706 * This is the method called by parser when the HTTP request line is parsed
1708 public abstract boolean startResponse(HttpVersion version, int status, String reason);
1711 /* ------------------------------------------------------------------------------- */
1712 @SuppressWarnings("serial")
1713 private static class IllegalCharacterException extends BadMessageException
1715 private IllegalCharacterException(State state,byte ch,ByteBuffer buffer)
1717 super(400,String.format("Illegal character 0x%X",ch));
1718 // Bug #460642 - don't reveal buffers to end user
1719 LOG.warn(String.format("Illegal character 0x%X in state=%s for buffer %s",ch,state,BufferUtil.toDetailString(buffer)));