Merge remote-tracking branch 'origin/master' into jetty-9.1

Conflicts:
	jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
	jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
This commit is contained in:
Greg Wilkins 2013-08-13 00:44:39 +10:00
commit 1e25778d57
7 changed files with 202 additions and 191 deletions

View File

@ -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

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.http;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
@ -64,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
@ -83,7 +89,6 @@ public class HttpParser
REQUEST_VERSION,
REASON,
HEADER,
HEADER_NAME,
HEADER_IN_NAME,
HEADER_VALUE,
HEADER_IN_VALUE,
@ -102,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;
@ -135,31 +141,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)
{
_handler=handler;
_requestHandler=handler;
_responseHandler=null;
_maxHeaderBytes=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,boolean strict)
{
_handler=handler;
_requestHandler=null;
_responseHandler=handler;
_maxHeaderBytes=maxHeaderBytes;
_strict=strict;
}
/* ------------------------------------------------------------------------------- */
@ -357,15 +377,15 @@ public class HttpParser
}
/* ------------------------------------------------------------------------------- */
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();
@ -411,9 +431,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);
}
@ -426,6 +447,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)
@ -605,7 +627,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");
@ -632,7 +657,7 @@ public class HttpParser
case REASON:
if (ch == HttpTokens.LINE_FEED)
{
String reason=takeLengthString();
String reason=takeString();
setState(State.HEADER);
handle=_responseHandler.startResponse(_version, _responseStatus, reason)||handle;
@ -810,14 +835,18 @@ public class HttpParser
case HttpTokens.TAB:
{
// header value without name - continuation?
_string.setLength(0);
if (_valueString!=null)
if (_valueString==null)
{
_string.append(_valueString);
_string.append(' ');
_string.setLength(0);
_length=0;
}
else
{
setString(_valueString);
_string.append(' ');
_length++;
_valueString=null;
}
_length=_string.length();
_valueString=null;
setState(State.HEADER_VALUE);
break;
}
@ -913,14 +942,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;
@ -936,8 +986,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);
@ -950,190 +998,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 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:
{
_string.append((char)ch);
_length=_string.length();
setState(State.HEADER_IN_NAME);
}
}
break;
case HEADER_IN_NAME:
if (ch<0)
throw new BadMessage("Illegal character");
switch(ch)
if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED)
{
case HttpTokens.LINE_FEED:
if (_headerString==null)
{
_headerString=takeString();
_length=-1;
_header=HttpHeader.CACHE.get(_headerString);
setState(State.HEADER);
break;
}
_length=-1;
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++;
setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE);
break;
}
break;
if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB)
{
if (_header!=null)
{
setString(_header.asString());
_header=null;
_headerString=null;
}
_string.append((char)ch);
if (ch>HttpTokens.SPACE)
_length=_string.length();
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);
}
_string.append((char)(0xff&ch));
_length=_string.length();
setState(State.HEADER_IN_VALUE);
break;
}
break;
if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
break;
if (ch==HttpTokens.LINE_FEED)
{
if (_length > 0)
{
_value=null;
_valueString=(_valueString==null)?takeString():(_valueString+" "+takeString());
}
setState(State.HEADER);
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());

View File

@ -31,7 +31,6 @@ import org.eclipse.jetty.http.HttpParser.State;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -404,6 +403,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, _headers);
}
@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, _headers);
}
@Test
@ -580,7 +623,6 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);

View File

@ -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;
import javax.servlet.WriteListener;

View File

@ -37,7 +37,6 @@ import java.io.OutputStream;
import java.net.Socket;
import java.net.URL;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Exchanger;
import java.util.concurrent.atomic.AtomicBoolean;
@ -57,7 +56,6 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.matchers.JUnitMatchers;
/**

View File

@ -34,7 +34,6 @@ import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -60,7 +59,6 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

View File

@ -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);