From acb28e3ac8d04feffa15cdceaeda1284b3338b7e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 30 Jul 2013 15:46:53 +0200 Subject: [PATCH] Multiple optimizations in the HttpObjectDecoder * Minimize allocation of StringBuilder and also minimize char array copy * Try to detect HttpVersion without calling toUpperCase() for performance reasons --- .../handler/codec/http/HttpObjectDecoder.java | 59 ++++++++++++------- .../netty/handler/codec/http/HttpVersion.java | 36 +++++++++-- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 524412dc8a..eb18298256 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -98,10 +98,25 @@ import java.util.List; */ public abstract class HttpObjectDecoder extends ReplayingDecoder { + private static final ThreadLocal BUILDERS = new ThreadLocal() { + @Override + protected StringBuilder initialValue() { + return new StringBuilder(512); + } + + @Override + public StringBuilder get() { + StringBuilder builder = super.get(); + builder.setLength(0); + return builder; + } + }; + private final int maxInitialLineLength; private final int maxHeaderSize; private final int maxChunkSize; private final boolean chunkedSupported; + private ByteBuf content; private HttpMessage message; private long chunkSize; @@ -315,8 +330,8 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0) { headers.clear(); do { char firstChar = line.charAt(0); if (name != null && (firstChar == ' ' || firstChar == '\t')) { - value = value + ' ' + line.trim(); + value = value + ' ' + line.toString().trim(); } else { if (name != null) { headers.add(name, value); @@ -584,7 +599,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0); // Add the last header. if (name != null) { @@ -609,9 +624,9 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0) { LastHttpContent trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER); do { char firstChar = line.charAt(0); @@ -619,7 +634,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder current = trailer.trailingHeaders().getAll(lastHeader); if (!current.isEmpty()) { int lastPos = current.size() - 1; - String newString = current.get(lastPos) + line.trim(); + String newString = current.get(lastPos) + line.toString().trim(); current.set(lastPos, newString); } else { // Content-Length, Transfer-Encoding, or Trailer @@ -636,7 +651,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0); return trailer; } @@ -644,8 +659,8 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder= maxLineLength) { // TODO: Respond with Bad Request and discard the traffic @@ -728,7 +743,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0; result --) { if (!Character.isWhitespace(sb.charAt(result - 1))) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java index 06cfce6526..3ef8281c33 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java @@ -28,6 +28,9 @@ public class HttpVersion implements Comparable { private static final Pattern VERSION_PATTERN = Pattern.compile("(\\S+)/(\\d+)\\.(\\d+)"); + private static final String HTTP_1_0_STRING = "HTTP/1.0"; + private static final String HTTP_1_1_STRING = "HTTP/1.1"; + /** * HTTP/1.0 */ @@ -51,14 +54,39 @@ public class HttpVersion implements Comparable { throw new NullPointerException("text"); } - text = text.trim().toUpperCase(); - if ("HTTP/1.1".equals(text)) { + text = text.trim(); + // Try to match without convert to uppercase first as this is what 99% of all clients + // will send anyway. Also there is a change to the RFC to make it clear that it is + // expected to be case-sensitive + // + // See: + // * http://trac.tools.ietf.org/wg/httpbis/trac/ticket/1 + // * http://trac.tools.ietf.org/wg/httpbis/trac/wiki + // + // TODO: Remove the uppercase conversion in 4.1.0 as the RFC state it must be HTTP (uppercase) + // See https://github.com/netty/netty/issues/1682 + // + HttpVersion version = version0(text); + if (version == null) { + text = text.toUpperCase(); + // try again after convert to uppercase + version = version0(text); + if (version == null) { + // still no match, construct a new one + version = new HttpVersion(text, true); + } + } + return version; + } + + private static HttpVersion version0(String text) { + if (HTTP_1_1_STRING.equals(text)) { return HTTP_1_1; } - if ("HTTP/1.0".equals(text)) { + if (HTTP_1_0_STRING.equals(text)) { return HTTP_1_0; } - return new HttpVersion(text, true); + return null; } private final String protocolName;