diff options
6 files changed, 199 insertions, 183 deletions
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java index aedfd2aec2..6d835dc2d5 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java @@ -68,6 +68,7 @@ public class HttpField CACHE.put(new CachedHttpField(HttpHeader.CONTENT_LENGTH,"0")); CACHE.put(new CachedHttpField(HttpHeader.CONTENT_ENCODING,"gzip")); CACHE.put(new CachedHttpField(HttpHeader.CONTENT_ENCODING,"deflate")); + CACHE.put(new CachedHttpField(HttpHeader.TRANSFER_ENCODING,"chunked")); CACHE.put(new CachedHttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT")); // Content types diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 99c2231996..db4642957d 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -63,11 +63,18 @@ import org.eclipse.jetty.util.log.Logger; * per parser dynamic Trie of {@link HttpFields} from previous parsed messages * is used to help the parsing of subsequent messages. * </p> + * <p> + * If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true, + * then the parser will strictly pass on the exact strings received for methods and header + * fields. Otherwise a fast case insensitive string lookup is used that may alter the + * case of the method and/or headers + * </p> */ public class HttpParser { public static final Logger LOG = Log.getLogger(HttpParser.class); - static final int INITIAL_URI_LENGTH=256; + public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT"); + public final static int INITIAL_URI_LENGTH=256; // States public enum State @@ -82,7 +89,6 @@ public class HttpParser REQUEST_VERSION, REASON, HEADER, - HEADER_NAME, HEADER_IN_NAME, HEADER_VALUE, HEADER_IN_VALUE, @@ -101,6 +107,7 @@ public class HttpParser private final RequestHandler<ByteBuffer> _requestHandler; private final ResponseHandler<ByteBuffer> _responseHandler; private final int _maxHeaderBytes; + private final boolean _strict; private HttpField _field; private HttpHeader _header; private String _headerString; @@ -132,31 +139,45 @@ public class HttpParser /* ------------------------------------------------------------------------------- */ public HttpParser(RequestHandler<ByteBuffer> handler) { - this(handler,-1); + this(handler,-1,__STRICT); } /* ------------------------------------------------------------------------------- */ public HttpParser(ResponseHandler<ByteBuffer> handler) { - this(handler,-1); + this(handler,-1,__STRICT); } /* ------------------------------------------------------------------------------- */ public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes) { + this(handler,maxHeaderBytes,__STRICT); + } + + /* ------------------------------------------------------------------------------- */ + public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes) + { + this(handler,maxHeaderBytes,__STRICT); + } + + /* ------------------------------------------------------------------------------- */ + public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict) + { _handler=handler; _requestHandler=handler; _responseHandler=null; _maxHeaderBytes=maxHeaderBytes; + _strict=strict; } /* ------------------------------------------------------------------------------- */ - public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes) + public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict) { _handler=handler; _requestHandler=null; _responseHandler=handler; _maxHeaderBytes=maxHeaderBytes; + _strict=strict; } /* ------------------------------------------------------------------------------- */ @@ -376,14 +397,16 @@ public class HttpParser return false; } - private String takeString() + /* ------------------------------------------------------------------------------- */ + private void setString(String s) { - String s =_string.toString(); _string.setLength(0); - return s; + _string.append(s); + _length=s.length(); } - - private String takeLengthString() + + /* ------------------------------------------------------------------------------- */ + private String takeString() { _string.setLength(_length); String s =_string.toString(); @@ -429,9 +452,10 @@ public class HttpParser case METHOD: if (ch == HttpTokens.SPACE) { + _length=_string.length(); _methodString=takeString(); HttpMethod method=HttpMethod.CACHE.get(_methodString); - if (method!=null) + if (method!=null && !_strict) _methodString=method.asString(); setState(State.SPACE1); } @@ -444,6 +468,7 @@ public class HttpParser case RESPONSE_VERSION: if (ch == HttpTokens.SPACE) { + _length=_string.length(); String version=takeString(); _version=HttpVersion.CACHE.get(version); if (_version==null) @@ -622,7 +647,10 @@ public class HttpParser if (ch == HttpTokens.LINE_FEED) { if (_version==null) + { + _length=_string.length(); _version=HttpVersion.CACHE.get(takeString()); + } if (_version==null) { throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version"); @@ -651,7 +679,7 @@ public class HttpParser case REASON: if (ch == HttpTokens.LINE_FEED) { - String reason=takeLengthString(); + String reason=takeString(); setState(State.HEADER); return_from_parse=_responseHandler.startResponse(_version, _responseStatus, reason)||return_from_parse; @@ -826,14 +854,18 @@ public class HttpParser case HttpTokens.TAB: { // header value without name - continuation? - _string.setLength(0); - if (_valueString!=null) + if (_valueString==null) + { + _string.setLength(0); + _length=0; + } + else { - _string.append(_valueString); + setString(_valueString); _string.append(' '); + _length++; + _valueString=null; } - _length=_string.length(); - _valueString=null; setState(State.HEADER_VALUE); break; } @@ -929,14 +961,35 @@ public class HttpParser if (field!=null) { - String n=field.getName(); - String v=field.getValue(); + final String n; + final String v; + + if (_strict) + { + // Have to get the fields exactly from the buffer to match case + String fn=field.getName(); + String fv=field.getValue(); + n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StringUtil.__US_ASCII_CHARSET); + if (fv==null) + v=null; + else + { + v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StringUtil.__ISO_8859_1_CHARSET); + field=new HttpField(field.getHeader(),n,v); + } + } + else + { + n=field.getName(); + v=field.getValue(); + } + + _header=field.getHeader(); + _headerString=n; if (v==null) { // Header only - _header=field.getHeader(); - _headerString=n; setState(State.HEADER_VALUE); _string.setLength(0); _length=0; @@ -952,8 +1005,6 @@ public class HttpParser if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED) { _field=field; - _header=_field.getHeader(); - _headerString=n; _valueString=v; setState(State.HEADER_IN_VALUE); @@ -966,194 +1017,111 @@ public class HttpParser buffer.position(pos); break; } + else + { + setState(State.HEADER_IN_VALUE); + setString(v); + buffer.position(pos); + break; + } } } } // New header - setState(State.HEADER_NAME); + setState(State.HEADER_IN_NAME); _string.setLength(0); _string.append((char)ch); _length=1; } } } - break; - case HEADER_NAME: - if (ch<0) - throw new BadMessage(); - switch(ch) + case HEADER_IN_NAME: + if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED) { - case HttpTokens.LINE_FEED: - if (_headerString==null) - { - _headerString=takeLengthString(); - _header=HttpHeader.CACHE.get(_headerString); - } - setState(State.HEADER); - break; - - case HttpTokens.COLON: - if (_headerString==null) - { - _headerString=takeLengthString(); - _header=HttpHeader.CACHE.get(_headerString); - } - setState(State.HEADER_VALUE); - break; - - case HttpTokens.SPACE: - case HttpTokens.TAB: - break; - - default: + if (_headerString==null) { - _string.append((char)ch); - _length=_string.length(); - setState(State.HEADER_IN_NAME); + _headerString=takeString(); + _header=HttpHeader.CACHE.get(_headerString); } - } - - break; + _length=-1; - case HEADER_IN_NAME: - if (ch<HttpTokens.SPACE) - { - + setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE); + break; } - if (ch<0) - throw new BadMessage("Illegal character"); - switch(ch) + + if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB) { - case HttpTokens.LINE_FEED: - _headerString=takeString(); - _length=-1; - _header=HttpHeader.CACHE.get(_headerString); - setState(State.HEADER); - break; + if (_header!=null) + { + setString(_header.asString()); + _header=null; + _headerString=null; + } - case HttpTokens.COLON: - if (_headerString==null) - { - _headerString=takeString(); - _header=HttpHeader.CACHE.get(_headerString); - } - _length=-1; - setState(State.HEADER_VALUE); - break; - case HttpTokens.SPACE: - case HttpTokens.TAB: - if (_header!=null) - { - _string.setLength(0); - _string.append(_header.asString()); - _length=_string.length(); - _header=null; - _headerString=null; - } - setState(State.HEADER_NAME); - _string.append((char)ch); - break; - default: - if (_header!=null) - { - _string.setLength(0); - _string.append(_header.asString()); - _length=_string.length(); - _header=null; - _headerString=null; - } - _string.append((char)ch); - _length++; + _string.append((char)ch); + if (ch>HttpTokens.SPACE) + _length=_string.length(); + break; } - break; + + throw new BadMessage("Illegal character"); case HEADER_VALUE: - switch(ch) + if (ch>HttpTokens.SPACE || ch<0) { - case HttpTokens.LINE_FEED: - if (_length > 0) - { - if (_valueString!=null) - { - // multi line value! - _value=null; - _valueString+=" "+takeLengthString(); - } - else if (HttpHeaderValue.hasKnownValues(_header)) - { - _valueString=takeLengthString(); - _value=HttpHeaderValue.CACHE.get(_valueString); - } - else - { - _value=null; - _valueString=takeLengthString(); - } - } - setState(State.HEADER); - break; - case HttpTokens.SPACE: - case HttpTokens.TAB: - break; - default: + _string.append((char)(0xff&ch)); + _length=_string.length(); + setState(State.HEADER_IN_VALUE); + break; + } + + if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB) + break; + + if (ch==HttpTokens.LINE_FEED) + { + if (_length > 0) { - _string.append((char)(0xff&ch)); - _length=_string.length(); - setState(State.HEADER_IN_VALUE); + _value=null; + _valueString=(_valueString==null)?takeString():(_valueString+" "+takeString()); } + setState(State.HEADER); + break; } - break; + + throw new BadMessage("Illegal character"); case HEADER_IN_VALUE: - switch(ch) + if (ch>=HttpTokens.SPACE || ch<0) { - case HttpTokens.LINE_FEED: - if (_length > 0) - { - if (HttpHeaderValue.hasKnownValues(_header)) - { - _valueString=takeString(); - _value=HttpHeaderValue.CACHE.get(_valueString); - } - else - { - _value=null; - _valueString=takeString(); - } - _length=-1; - } - setState(State.HEADER); - break; - case HttpTokens.SPACE: - case HttpTokens.TAB: - if (_valueString!=null) - { - _string.setLength(0); - _string.append(_valueString); - _length=_valueString.length(); - _valueString=null; - _field=null; - } - _string.append((char)ch); - setState(State.HEADER_VALUE); - break; - default: - if (_valueString!=null) - { - _string.setLength(0); - _string.append(_valueString); - _length=_valueString.length(); - _valueString=null; - _field=null; - } - _string.append((char)(0xff&ch)); - _length++; + if (_valueString!=null) + { + setString(_valueString); + _valueString=null; + _field=null; + } + _string.append((char)(0xff&ch)); + if (ch>HttpTokens.SPACE || ch<0) + _length=_string.length(); + break; } - break; - + + if (ch==HttpTokens.LINE_FEED) + { + if (_length > 0) + { + _value=null; + _valueString=takeString(); + _length=-1; + } + setState(State.HEADER); + break; + } + throw new BadMessage("Illegal character"); + default: throw new IllegalStateException(_state.toString()); @@ -1377,7 +1345,7 @@ public class HttpParser { BufferUtil.clear(buffer); - LOG.warn("badMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler); + LOG.warn("BadMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler); if (DEBUG) LOG.debug(e); setState(State.CLOSED); @@ -1388,7 +1356,7 @@ public class HttpParser { BufferUtil.clear(buffer); - LOG.warn("badMessage: "+e.toString()+" for "+_handler); + LOG.warn("Parsing Exception: "+e.toString()+" for "+_handler); if (DEBUG) LOG.debug(e); diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index 51b0de6037..654474d726 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -379,6 +379,50 @@ public class HttpParserTest HttpParser parser= new HttpParser(handler); parseAll(parser,buffer); assertThat(_bad,Matchers.notNullValue()); + } + + @Test + public void testNonStrict() throws Exception + { + ByteBuffer buffer= BufferUtil.toBuffer( + "get / http/1.0\015\012" + + "HOST: localhost\015\012" + + "cOnNeCtIoN: ClOsE\015\012"+ + "\015\012"); + HttpParser.RequestHandler<ByteBuffer> handler = new Handler(); + HttpParser parser= new HttpParser(handler,-1,false); + parseAll(parser,buffer); + + assertEquals("GET", _methodOrVersion); + assertEquals("/", _uriOrStatus); + assertEquals("HTTP/1.0", _versionOrReason); + assertEquals("Host", _hdr[0]); + assertEquals("localhost", _val[0]); + assertEquals("Connection", _hdr[1]); + assertEquals("close", _val[1]); + assertEquals(1, _h); + } + + @Test + public void testStrict() throws Exception + { + ByteBuffer buffer= BufferUtil.toBuffer( + "gEt / http/1.0\015\012" + + "HOST: localhost\015\012" + + "cOnNeCtIoN: ClOsE\015\012"+ + "\015\012"); + HttpParser.RequestHandler<ByteBuffer> handler = new Handler(); + HttpParser parser= new HttpParser(handler,-1,true); + parseAll(parser,buffer); + + assertEquals("gEt", _methodOrVersion); + assertEquals("/", _uriOrStatus); + assertEquals("HTTP/1.0", _versionOrReason); + assertEquals("HOST", _hdr[0]); + assertEquals("localhost", _val[0]); + assertEquals("cOnNeCtIoN", _hdr[1]); + assertEquals("ClOsE", _val[1]); + assertEquals(1, _h); } @Test @@ -494,7 +538,6 @@ public class HttpParserTest + "\015\012" + "0123456789\015\012"); - HttpParser.RequestHandler<ByteBuffer> handler = new Handler(); HttpParser parser= new HttpParser(handler); parser.parseNext(buffer); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index ae280489a1..7d542303bc 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; + import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java index 5a5f5945e1..d98e65452b 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java @@ -48,6 +48,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java index 29db2113ad..58ede8b0b1 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java @@ -53,12 +53,14 @@ public class StringUtil public final static Charset __UTF8_CHARSET; public final static Charset __ISO_8859_1_CHARSET; public final static Charset __UTF16_CHARSET; + public final static Charset __US_ASCII_CHARSET; static { __UTF8_CHARSET=Charset.forName(__UTF8); __ISO_8859_1_CHARSET=Charset.forName(__ISO_8859_1); __UTF16_CHARSET=Charset.forName(__UTF16); + __US_ASCII_CHARSET=Charset.forName("US-ASCII"); CHARSETS.put("UTF-8",__UTF8); CHARSETS.put("UTF8",__UTF8); |