Allow to disable validation of HTTP headers which shows a 5k perf improvement here when disabled

This commit is contained in:
Norman Maurer 2013-11-13 15:05:12 +01:00
parent b65b4199dc
commit c4130e0cf7
13 changed files with 155 additions and 44 deletions

View File

@ -23,18 +23,24 @@ import io.netty.buffer.Unpooled;
*/
public class DefaultFullHttpRequest extends DefaultHttpRequest implements FullHttpRequest {
private final ByteBuf content;
private final HttpHeaders trailingHeader = new DefaultHttpHeaders();
private final HttpHeaders trailingHeader;
public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) {
this(httpVersion, method, uri, Unpooled.buffer(0));
}
public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, ByteBuf content) {
super(httpVersion, method, uri);
this(httpVersion, method, uri, content, true);
}
public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri,
ByteBuf content, boolean validateHeaders) {
super(httpVersion, method, uri, validateHeaders);
if (content == null) {
throw new NullPointerException("content");
}
this.content = content;
trailingHeader = new DefaultHttpHeaders(validateHeaders);
}
@Override

View File

@ -25,18 +25,24 @@ import io.netty.buffer.Unpooled;
public class DefaultFullHttpResponse extends DefaultHttpResponse implements FullHttpResponse {
private final ByteBuf content;
private final HttpHeaders trailingHeaders = new DefaultHttpHeaders();
private final HttpHeaders trailingHeaders;
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status) {
this(version, status, Unpooled.buffer(0));
}
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, ByteBuf content) {
super(version, status);
this(version, status, content, true);
}
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status,
ByteBuf content, boolean validateHeaders) {
super(version, status, validateHeaders);
if (content == null) {
throw new NullPointerException("content");
}
this.content = content;
trailingHeaders = new DefaultHttpHeaders(validateHeaders);
}
@Override

View File

@ -80,8 +80,14 @@ public class DefaultHttpHeaders extends HttpHeaders {
private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
private final HeaderEntry head = new HeaderEntry(-1, null, null);
protected final boolean validate;
public DefaultHttpHeaders() {
this(true);
}
public DefaultHttpHeaders(boolean validate) {
this.validate = validate;
head.before = head.after = head;
}
@ -91,9 +97,14 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override
public HttpHeaders add(final String name, final Object value) {
validateHeaderName0(name);
String strVal = toString(value);
validateHeaderValue(strVal);
String strVal;
if (validate) {
validateHeaderName0(name);
strVal = toString(value);
validateHeaderValue(strVal);
} else {
strVal = toString(value);
}
int h = hash(name);
int i = index(h);
add0(h, i, name, strVal);
@ -102,12 +113,16 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override
public HttpHeaders add(String name, Iterable<?> values) {
validateHeaderName0(name);
if (validate) {
validateHeaderName0(name);
}
int h = hash(name);
int i = index(h);
for (Object v: values) {
String vstr = toString(v);
validateHeaderValue(vstr);
if (validate) {
validateHeaderValue(vstr);
}
add0(h, i, name, vstr);
}
return this;
@ -173,9 +188,14 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override
public HttpHeaders set(final String name, final Object value) {
validateHeaderName0(name);
String strVal = toString(value);
validateHeaderValue(strVal);
String strVal;
if (validate) {
validateHeaderName0(name);
strVal = toString(value);
validateHeaderValue(strVal);
} else {
strVal = toString(value);
}
int h = hash(name);
int i = index(h);
remove0(h, i, name);
@ -188,8 +208,9 @@ public class DefaultHttpHeaders extends HttpHeaders {
if (values == null) {
throw new NullPointerException("values");
}
validateHeaderName0(name);
if (validate) {
validateHeaderName0(name);
}
int h = hash(name);
int i = index(h);
@ -200,7 +221,9 @@ public class DefaultHttpHeaders extends HttpHeaders {
break;
}
String strVal = toString(v);
validateHeaderValue(strVal);
if (validate) {
validateHeaderValue(strVal);
}
add0(h, i, name, strVal);
}

View File

@ -25,16 +25,21 @@ import java.util.Map;
public abstract class DefaultHttpMessage extends DefaultHttpObject implements HttpMessage {
private HttpVersion version;
private final HttpHeaders headers = new DefaultHttpHeaders();
private final HttpHeaders headers;
/**
* Creates a new instance.
*/
protected DefaultHttpMessage(final HttpVersion version) {
this(version, true);
}
protected DefaultHttpMessage(final HttpVersion version, boolean validate) {
if (version == null) {
throw new NullPointerException("version");
}
this.version = version;
headers = new DefaultHttpHeaders(validate);
}
@Override

View File

@ -33,7 +33,19 @@ public class DefaultHttpRequest extends DefaultHttpMessage implements HttpReques
* @param uri the URI or path of the request
*/
public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) {
super(httpVersion);
this(httpVersion, method, uri, true);
}
/**
* Creates a new instance.
*
* @param httpVersion the HTTP version of the request
* @param method the HTTP getMethod of the request
* @param uri the URI or path of the request
* @param validateHeaders validate the headers when adding them
*/
public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, boolean validateHeaders) {
super(httpVersion, validateHeaders);
if (method == null) {
throw new NullPointerException("method");
}

View File

@ -31,7 +31,18 @@ public class DefaultHttpResponse extends DefaultHttpMessage implements HttpRespo
* @param status the getStatus of this response
*/
public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status) {
super(version);
this(version, status, true);
}
/**
* Creates a new instance.
*
* @param version the HTTP version of this response
* @param status the getStatus of this response
* @param validateHeaders validate the headers when adding them
*/
public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders) {
super(version, validateHeaders);
if (status == null) {
throw new NullPointerException("status");
}

View File

@ -26,25 +26,19 @@ import java.util.Map;
*/
public class DefaultLastHttpContent extends DefaultHttpContent implements LastHttpContent {
private final HttpHeaders trailingHeaders = new DefaultHttpHeaders() {
@Override
void validateHeaderName0(String name) {
super.validateHeaderName0(name);
if (name.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH) ||
name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) ||
name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) {
throw new IllegalArgumentException(
"prohibited trailing header: " + name);
}
}
};
private final HttpHeaders trailingHeaders;
public DefaultLastHttpContent() {
this(Unpooled.buffer(0));
}
public DefaultLastHttpContent(ByteBuf content) {
this(content, true);
}
public DefaultLastHttpContent(ByteBuf content, boolean validateHeaders) {
super(content);
trailingHeaders = new TrailingHeaders(validateHeaders);
}
@Override
@ -97,4 +91,21 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
buf.append(StringUtil.NEWLINE);
}
}
private static final class TrailingHeaders extends DefaultHttpHeaders {
TrailingHeaders(boolean validate) {
super(validate);
}
@Override
void validateHeaderName0(String name) {
super.validateHeaderName0(name);
if (name.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH) ||
name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) ||
name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) {
throw new IllegalArgumentException(
"prohibited trailing header: " + name);
}
}
}
}

View File

@ -118,6 +118,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
private final int maxHeaderSize;
private final int maxChunkSize;
private final boolean chunkedSupported;
protected final boolean validateHeaders;
private ByteBuf content;
private HttpMessage message;
@ -159,13 +160,22 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
*/
protected HttpObjectDecoder(
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported) {
this(maxInitialLineLength, maxHeaderSize, maxChunkSize, chunkedSupported, true);
}
/**
* Creates a new instance with the specified parameters.
*/
protected HttpObjectDecoder(
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,
boolean chunkedSupported, boolean validateHeaders) {
super(State.SKIP_CONTROL_CHARS);
if (maxInitialLineLength <= 0) {
throw new IllegalArgumentException(
"maxInitialLineLength must be a positive integer: " +
maxInitialLineLength);
maxInitialLineLength);
}
if (maxHeaderSize <= 0) {
throw new IllegalArgumentException(
@ -181,6 +191,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
this.maxHeaderSize = maxHeaderSize;
this.maxChunkSize = maxChunkSize;
this.chunkedSupported = chunkedSupported;
this.validateHeaders = validateHeaders;
}
@Override
@ -279,7 +290,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
ByteBuf content = readBytes(ctx.alloc(), buffer, toRead);
if (!buffer.isReadable()) {
reset();
out.add(new DefaultLastHttpContent(content));
out.add(new DefaultLastHttpContent(content, validateHeaders));
return;
}
out.add(new DefaultHttpContent(content));
@ -321,7 +332,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
if (chunkSize == 0) {
// Read all content.
reset();
out.add(new DefaultLastHttpContent(content));
out.add(new DefaultLastHttpContent(content, validateHeaders));
return;
}
out.add(new DefaultHttpContent(content));
@ -468,7 +479,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
if (actualContentLength == 0) {
out.add(LastHttpContent.EMPTY_LAST_CONTENT);
} else {
out.add(new DefaultLastHttpContent(content));
out.add(new DefaultLastHttpContent(content, validateHeaders));
}
}
}
@ -513,7 +524,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
if (content == null || !content.isReadable()) {
httpContent = LastHttpContent.EMPTY_LAST_CONTENT;
} else {
httpContent = new DefaultLastHttpContent(content);
httpContent = new DefaultLastHttpContent(content, validateHeaders);
}
out.add(message);
@ -629,7 +640,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
StringBuilder line = readHeader(buffer);
String lastHeader = null;
if (line.length() > 0) {
LastHttpContent trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER);
LastHttpContent trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);
do {
char firstChar = line.charAt(0);
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {

View File

@ -70,15 +70,21 @@ public class HttpRequestDecoder extends HttpObjectDecoder {
super(maxInitialLineLength, maxHeaderSize, maxChunkSize, true);
}
public HttpRequestDecoder(
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders) {
super(maxInitialLineLength, maxHeaderSize, maxChunkSize, true, validateHeaders);
}
@Override
protected HttpMessage createMessage(String[] initialLine) throws Exception {
return new DefaultHttpRequest(
HttpVersion.valueOf(initialLine[2]), HttpMethod.valueOf(initialLine[0]), initialLine[1]);
HttpVersion.valueOf(initialLine[2]),
HttpMethod.valueOf(initialLine[0]), initialLine[1], validateHeaders);
}
@Override
protected HttpMessage createInvalidMessage() {
return new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request");
return new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request", validateHeaders);
}
@Override

View File

@ -101,16 +101,21 @@ public class HttpResponseDecoder extends HttpObjectDecoder {
super(maxInitialLineLength, maxHeaderSize, maxChunkSize, true);
}
public HttpResponseDecoder(
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders) {
super(maxInitialLineLength, maxHeaderSize, maxChunkSize, true, validateHeaders);
}
@Override
protected HttpMessage createMessage(String[] initialLine) {
return new DefaultHttpResponse(
HttpVersion.valueOf(initialLine[0]),
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]), validateHeaders);
}
@Override
protected HttpMessage createInvalidMessage() {
return new DefaultHttpResponse(HttpVersion.HTTP_1_0, UNKNOWN_STATUS);
return new DefaultHttpResponse(HttpVersion.HTTP_1_0, UNKNOWN_STATUS, validateHeaders);
}
@Override

View File

@ -66,6 +66,11 @@ public abstract class RtspObjectDecoder extends HttpObjectDecoder {
super(maxInitialLineLength, maxHeaderSize, maxContentLength * 2, false);
}
protected RtspObjectDecoder(
int maxInitialLineLength, int maxHeaderSize, int maxContentLength, boolean validateHeaders) {
super(maxInitialLineLength, maxHeaderSize, maxContentLength * 2, false, validateHeaders);
}
@Override
protected boolean isContentAlwaysEmpty(HttpMessage msg) {
// Unlike HTTP, RTSP always assumes zero-length body if Content-Length

View File

@ -65,15 +65,20 @@ public class RtspRequestDecoder extends RtspObjectDecoder {
super(maxInitialLineLength, maxHeaderSize, maxContentLength);
}
public RtspRequestDecoder(
int maxInitialLineLength, int maxHeaderSize, int maxContentLength, boolean validateHeaders) {
super(maxInitialLineLength, maxHeaderSize, maxContentLength, validateHeaders);
}
@Override
protected HttpMessage createMessage(String[] initialLine) throws Exception {
return new DefaultHttpRequest(RtspVersions.valueOf(initialLine[2]),
RtspMethods.valueOf(initialLine[0]), initialLine[1]);
RtspMethods.valueOf(initialLine[0]), initialLine[1], validateHeaders);
}
@Override
protected HttpMessage createInvalidMessage() {
return new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request");
return new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request", validateHeaders);
}
@Override

View File

@ -69,16 +69,21 @@ public class RtspResponseDecoder extends RtspObjectDecoder {
super(maxInitialLineLength, maxHeaderSize, maxContentLength);
}
public RtspResponseDecoder(int maxInitialLineLength, int maxHeaderSize,
int maxContentLength, boolean validateHeaders) {
super(maxInitialLineLength, maxHeaderSize, maxContentLength, validateHeaders);
}
@Override
protected HttpMessage createMessage(String[] initialLine) throws Exception {
return new DefaultHttpResponse(
RtspVersions.valueOf(initialLine[0]),
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]), validateHeaders);
}
@Override
protected HttpMessage createInvalidMessage() {
return new DefaultHttpResponse(RtspVersions.RTSP_1_0, UNKNOWN_STATUS);
return new DefaultHttpResponse(RtspVersions.RTSP_1_0, UNKNOWN_STATUS, validateHeaders);
}
@Override