From d5c72e2c46ac232d14bea88e90cb99b7fdda11ce Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Thu, 8 Feb 2024 10:51:32 +0100 Subject: [PATCH] Cache Accept headers on HTTP 1.1 requests (#13824) Motivation: The Accept header is frequently used on HTTP 1.1 requests: there's no point to allocate a fresh new AsciiString while decoding HTTP requests on them Modification: Add Accept caching while decoding HTTP 1.1 requests Result: Less garbage and faster HTTP request decoding --- .../codec/http/HttpRequestDecoder.java | 22 +++++++++++++++++-- .../codec/http/HttpRequestDecoderTest.java | 4 +++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestDecoder.java index 59fdd8d6e3ce..2bb38243bfe2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestDecoder.java @@ -90,6 +90,7 @@ */ public class HttpRequestDecoder extends HttpObjectDecoder { + private static final AsciiString Accept = AsciiString.cached("Accept"); private static final AsciiString Host = AsciiString.cached("Host"); private static final AsciiString Connection = AsciiString.cached("Connection"); private static final AsciiString ContentType = AsciiString.cached("Content-Type"); @@ -118,6 +119,9 @@ public class HttpRequestDecoder extends HttpObjectDecoder { private static final long LENGTH_AS_LONG = 'L' | 'e' << 8 | 'n' << 16 | 'g' << 24 | (long) 't' << 32 | (long) 'h' << 40; + private static final long ACCEPT_AS_LONG = 'A' | 'c' << 8 | 'c' << 16 | 'e' << 24 | + (long) 'p' << 32 | (long) 't' << 40; + /** * Creates a new instance with the default * {@code maxInitialLineLength (4096)}, {@code maxHeaderSize (8192)}, and @@ -200,10 +204,14 @@ protected HttpMessage createMessage(String[] initialLine) throws Exception { @Override protected AsciiString splitHeaderName(final byte[] sb, final int start, final int length) { final byte firstChar = sb[start]; - if (firstChar == 'H' && length == 4) { - if (isHost(sb, start)) { + if (firstChar == 'H') { + if (length == 4 && isHost(sb, start)) { return Host; } + } else if (firstChar == 'A') { + if (length == 6 && isAccept(sb, start)) { + return Accept; + } } else if (firstChar == 'C') { if (length == 10) { if (isConnection(sb, start)) { @@ -222,6 +230,16 @@ protected AsciiString splitHeaderName(final byte[] sb, final int start, final in return super.splitHeaderName(sb, start, length); } + private static boolean isAccept(byte[] sb, int start) { + final long maybeAccept = sb[start] | + sb[start + 1] << 8 | + sb[start + 2] << 16 | + sb[start + 3] << 24 | + (long) sb[start + 4] << 32 | + (long) sb[start + 5] << 40; + return maybeAccept == ACCEPT_AS_LONG; + } + private static boolean isHost(byte[] sb, int start) { final int maybeHost = sb[start] | sb[start + 1] << 8 | diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java index 6a6d3589e139..c7fb121c75d0 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java @@ -60,6 +60,7 @@ private static byte[] createContent(String... lineDelimiters) { "Upgrade: WebSocket" + lineDelimiter2 + "Connection: Upgrade" + lineDelimiter + "Host: localhost" + lineDelimiter2 + + "Accept: */*" + lineDelimiter + "Origin: http://localhost:8080" + lineDelimiter + "Sec-WebSocket-Key1: 10 28 8V7 8 48 0" + lineDelimiter2 + "Sec-WebSocket-Key2: 8 Xt754O3Q3QW 0 _60" + lineDelimiter + @@ -114,10 +115,11 @@ private static void testDecodeWholeRequestAtOnce(byte[] content, int maxHeaderSi } private static void checkHeaders(HttpHeaders headers) { - assertEquals(7, headers.names().size()); + assertEquals(8, headers.names().size()); checkHeader(headers, "Upgrade", "WebSocket"); checkHeader(headers, "Connection", "Upgrade"); checkHeader(headers, "Host", "localhost"); + checkHeader(headers, "Accept", "*/*"); checkHeader(headers, "Origin", "http://localhost:8080"); checkHeader(headers, "Sec-WebSocket-Key1", "10 28 8V7 8 48 0"); checkHeader(headers, "Sec-WebSocket-Key2", "8 Xt754O3Q3QW 0 _60");