From 741cced06763c83aabc5e62c25b267b474cc0099 Mon Sep 17 00:00:00 2001 From: gregw Date: Mon, 4 Mar 2024 20:31:09 +0100 Subject: [PATCH] updates from review Added a fallback int lookup --- .../org/eclipse/jetty/http/HttpMethod.java | 50 ++++++++-------- .../org/eclipse/jetty/http/HttpParser.java | 60 ++++++++++++------- 2 files changed, 62 insertions(+), 48 deletions(-) diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index dd43a7e618e3..10d9e1e83248 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -163,34 +163,34 @@ public String toString() public static HttpMethod lookAheadGet(ByteBuffer buffer) { int len = buffer.remaining(); - // Short cut for 3 char methods, mostly for GET optimisation - if (len > 3) - { - switch (buffer.getInt(buffer.position())) - { - case ACL_AS_INT: - return ACL; - case GET_AS_INT: - return GET; - case PRI_AS_INT: - return PRI; - case PUT_AS_INT: - return PUT; - case POST_AS_INT: - if (len > 4 && buffer.get(buffer.position() + 4) == ' ') - return POST; - break; - case HEAD_AS_INT: - if (len > 4 && buffer.get(buffer.position() + 4) == ' ') - return HEAD; - break; - default: - break; - } - } + // Shortcut for 3 or 4 char methods, mostly for GET optimisation + if (len > 4) + return lookAheadGet(buffer, buffer.getInt(buffer.position())); return LOOK_AHEAD.getBest(buffer, 0, len); } + /** + * Optimized lookup to find a method name and trailing space in a byte array. + * + * @param buffer buffer containing ISO-8859-1 characters, it is not modified, which must have at least 5 bytes remaining + * @param lookAhead The integer representation of the first 4 bytes of the buffer that has previously been fetched + * with the equivalent of {@code buffer.getInt(buffer.position())} + * @return An HttpMethod if a match or null if no easy match. + */ + public static HttpMethod lookAheadGet(ByteBuffer buffer, int lookAhead) + { + return switch (lookAhead) + { + case ACL_AS_INT -> ACL; + case GET_AS_INT -> GET; + case PRI_AS_INT -> PRI; + case PUT_AS_INT -> PUT; + case POST_AS_INT -> (buffer.get(buffer.position() + 4) == ' ') ? POST : LOOK_AHEAD.getBest(buffer, 0, buffer.remaining()); + case HEAD_AS_INT -> (buffer.get(buffer.position() + 4) == ' ') ? HEAD : LOOK_AHEAD.getBest(buffer, 0, buffer.remaining()); + default -> LOOK_AHEAD.getBest(buffer, 0, buffer.remaining()); + }; + } + /** * Converts the given String parameter to an HttpMethod. * The string may differ from the Enum name as a '-' in the method diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 23751ba686f7..bd5e4b20466f 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -178,12 +178,13 @@ public class HttpParser .maxCapacity(0) .build(); + private static final int BYTES_IN_INT = 4; private static final int BYTES_IN_LONG = 8; private static final long HTTP_1_0_AS_LONG = stringAsLong("HTTP/1.0"); private static final long HTTP_1_1_AS_LONG = stringAsLong("HTTP/1.1"); private static final long GET_SLASH_HT_AS_LONG = stringAsLong("GET / HT"); - private static final long TP_SLASH_1_0_CRLF = stringAsLong("TP/1.0\r\n"); - private static final long TP_SLASH_1_1_CRLF = stringAsLong("TP/1.1\r\n"); + private static final long TP_SLASH_1_0_CRLF_AS_LONG = stringAsLong("TP/1.0\r\n"); + private static final long TP_SLASH_1_1_CRLF_AS_LONG = stringAsLong("TP/1.1\r\n"); private static final long SPACE_200_OK_CR_AS_LONG = stringAsLong(" 200 OK\r"); private static long stringAsLong(String s) @@ -527,34 +528,47 @@ private HttpTokens.Token next(ByteBuffer buffer) private void quickStart(ByteBuffer buffer) { int position = buffer.position(); + int remaining = buffer.remaining(); if (_requestHandler != null) { // Try to match "GET / HTTP/1.x\r\n" with two longs - if (buffer.remaining() >= 16 && buffer.getLong(position) == GET_SLASH_HT_AS_LONG) + if (remaining >= 2 * BYTES_IN_LONG) { - long v = buffer.getLong(position + 8); - if (v == TP_SLASH_1_1_CRLF) + long lookahead = buffer.getLong(position); + if (lookahead == GET_SLASH_HT_AS_LONG) { - buffer.position(position + 16); - _methodString = HttpMethod.GET.asString(); - _version = HttpVersion.HTTP_1_1; - setState(State.HEADER); - _requestHandler.startRequest(_methodString, "/", _version); - return; + long v = buffer.getLong(position + BYTES_IN_LONG); + if (v == TP_SLASH_1_1_CRLF_AS_LONG) + { + buffer.position(position + 2 * BYTES_IN_LONG); + _methodString = HttpMethod.GET.asString(); + _version = HttpVersion.HTTP_1_1; + setState(State.HEADER); + _requestHandler.startRequest(_methodString, "/", _version); + return; + } + if (v == TP_SLASH_1_0_CRLF_AS_LONG) + { + buffer.position(position + 2 * BYTES_IN_LONG); + _methodString = HttpMethod.GET.asString(); + _version = HttpVersion.HTTP_1_0; + setState(State.HEADER); + _requestHandler.startRequest(_methodString, "/", _version); + return; + } } - if (v == TP_SLASH_1_0_CRLF) + else { - buffer.position(position + 16); - _methodString = HttpMethod.GET.asString(); - _version = HttpVersion.HTTP_1_0; - setState(State.HEADER); - _requestHandler.startRequest(_methodString, "/", _version); - return; + // lookup the method using the first 4 bytes of the already fetched long as a lookahead. + _method = HttpMethod.lookAheadGet(buffer, (int)((lookahead >> 32))); } } + else + { + // otherwise try a lookahead to match the method + _method = HttpMethod.lookAheadGet(buffer); + } - // otherwise just try to match the method - _method = HttpMethod.lookAheadGet(buffer); if (_method != null) { _methodString = _method.asString(); @@ -566,7 +580,7 @@ private void quickStart(ByteBuffer buffer) return; } } - else if (_responseHandler != null && buffer.remaining() > BYTES_IN_LONG) + else if (_responseHandler != null && remaining > BYTES_IN_LONG) { // Match version as a long long v = buffer.getLong(position); @@ -580,8 +594,8 @@ else if (v == HTTP_1_0_AS_LONG) position += BYTES_IN_LONG; // Try to make 200 OK as a long - if (buffer.remaining() > 16 && - buffer.get(position + 8) == '\n' && + if (remaining > 2 * BYTES_IN_LONG && + buffer.get(position + BYTES_IN_LONG) == '\n' && buffer.getLong(position) == SPACE_200_OK_CR_AS_LONG) { buffer.position(position + 9);