From 17cee2ac6469049b2820ec34b51badb7aa1c6db3 Mon Sep 17 00:00:00 2001 From: gregw Date: Thu, 5 Nov 2020 10:14:12 +0100 Subject: [PATCH 1/9] Fix #5575 SEARCH method Fix #5575 SEARCH and PATCH methods --- .../org/eclipse/jetty/http/HttpMethod.java | 43 ++++++++++++++----- .../eclipse/jetty/http/HttpParserTest.java | 14 +++--- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index 83f6bfc7365e..744711aeefcd 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -40,7 +40,9 @@ public enum HttpMethod CONNECT, MOVE, PROXY, - PRI; + PRI, + PATCH, + SEARCH; /** * Optimized lookup to find a method name and trailing space in a byte array. @@ -62,15 +64,30 @@ public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limi return GET; break; case 'P': - if (bytes[position + 1] == 'O' && bytes[position + 2] == 'S' && bytes[position + 3] == 'T' && length >= 5 && bytes[position + 4] == ' ') - return POST; - if (bytes[position + 1] == 'R' && bytes[position + 2] == 'O' && bytes[position + 3] == 'X' && length >= 6 && bytes[position + 4] == 'Y' && bytes[position + 5] == ' ') - return PROXY; - if (bytes[position + 1] == 'U' && bytes[position + 2] == 'T' && bytes[position + 3] == ' ') - return PUT; - if (bytes[position + 1] == 'R' && bytes[position + 2] == 'I' && bytes[position + 3] == ' ') - return PRI; - break; + switch (bytes[position + 1]) + { + case 'O': + if (bytes[position + 2] == 'S' && bytes[position + 3] == 'T' && length >= 5 && bytes[position + 4] == ' ') + return POST; + return null; + case 'R': + if (bytes[position + 2] == 'I' && bytes[position + 3] == ' ') + return PRI; + if (bytes[position + 2] == 'O' && bytes[position + 3] == 'X' && length >= 6 && bytes[position + 4] == 'Y' && bytes[position + 5] == ' ') + return PROXY; + return null; + case 'U': + if (bytes[position + 2] == 'T' && bytes[position + 3] == ' ') + return PUT; + return null; + case 'A': + if (bytes[position + 2] == 'T' && bytes[position + 3] == 'C' && length >= 6 && bytes[position + 4] == 'H' && bytes[position + 5] == ' ') + return PATCH; + return null; + default: + return null; + } + case 'H': if (bytes[position + 1] == 'E' && bytes[position + 2] == 'A' && bytes[position + 3] == 'D' && length >= 5 && bytes[position + 4] == ' ') return HEAD; @@ -99,7 +116,11 @@ public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limi if (bytes[position + 1] == 'O' && bytes[position + 2] == 'V' && bytes[position + 3] == 'E' && length >= 5 && bytes[position + 4] == ' ') return MOVE; break; - + case 'S': + if (bytes[position + 1] == 'E' && bytes[position + 2] == 'A' && bytes[position + 3] == 'R' && length >= 7 && + bytes[position + 4] == 'C' && bytes[position + 5] == 'H' && bytes[position + 6] == ' ') + return SEARCH; + break; default: break; } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index d454d5e49eb8..9374a243f075 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -85,12 +85,14 @@ public static void parseAll(HttpParser parser, ByteBuffer buffer) @Test public void httpMethodTest() { - assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("Wibble "))); - assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("GET"))); - assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("MO"))); - - assertEquals(HttpMethod.GET, HttpMethod.lookAheadGet(BufferUtil.toBuffer("GET "))); - assertEquals(HttpMethod.MOVE, HttpMethod.lookAheadGet(BufferUtil.toBuffer("MOVE "))); + for (HttpMethod m : HttpMethod.values()) + { + assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer(m.asString().substring(0,2)))); + assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer(m.asString()))); + assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer(m.asString() + "FOO"))); + assertEquals(m, HttpMethod.lookAheadGet(BufferUtil.toBuffer(m.asString() + " "))); + assertEquals(m, HttpMethod.lookAheadGet(BufferUtil.toBuffer(m.asString() + " /foo/bar"))); + } ByteBuffer b = BufferUtil.allocateDirect(128); BufferUtil.append(b, BufferUtil.toBuffer("GET")); From b8f03e2a8d5d80f4c253ca5234ab2c15608d5cb4 Mon Sep 17 00:00:00 2001 From: gregw Date: Thu, 5 Nov 2020 11:33:23 +0100 Subject: [PATCH 2/9] Added REPORT method Better test --- .../src/main/java/org/eclipse/jetty/http/HttpMethod.java | 8 +++++++- .../test/java/org/eclipse/jetty/http/HttpParserTest.java | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index 744711aeefcd..8f52e28681a3 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -42,7 +42,8 @@ public enum HttpMethod PROXY, PRI, PATCH, - SEARCH; + SEARCH, + REPORT; /** * Optimized lookup to find a method name and trailing space in a byte array. @@ -121,6 +122,11 @@ public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limi bytes[position + 4] == 'C' && bytes[position + 5] == 'H' && bytes[position + 6] == ' ') return SEARCH; break; + case 'R': + if (bytes[position + 1] == 'E' && bytes[position + 2] == 'P' && bytes[position + 3] == 'O' && length >= 7 && + bytes[position + 4] == 'R' && bytes[position + 5] == 'T' && bytes[position + 6] == ' ') + return REPORT; + break; default: break; } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index 9374a243f075..1935ca2fd780 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -92,6 +92,12 @@ public void httpMethodTest() assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer(m.asString() + "FOO"))); assertEquals(m, HttpMethod.lookAheadGet(BufferUtil.toBuffer(m.asString() + " "))); assertEquals(m, HttpMethod.lookAheadGet(BufferUtil.toBuffer(m.asString() + " /foo/bar"))); + + assertNull(HttpMethod.lookAheadGet(m.asString().substring(0,2).getBytes(), 0,2)); + assertNull(HttpMethod.lookAheadGet(m.asString().getBytes(), 0, m.asString().length())); + assertNull(HttpMethod.lookAheadGet((m.asString() + "FOO").getBytes(), 0, m.asString().length() + 3)); + assertEquals(m, HttpMethod.lookAheadGet(("\n" + m.asString() + " ").getBytes(), 1, m.asString().length() + 2)); + assertEquals(m, HttpMethod.lookAheadGet(("\n" + m.asString() + " /foo").getBytes(), 1, m.asString().length() + 6)); } ByteBuffer b = BufferUtil.allocateDirect(128); From d10b15ccc61afafb3abdcccaf54a8b9f8661edb2 Mon Sep 17 00:00:00 2001 From: gregw Date: Thu, 5 Nov 2020 18:25:34 +0100 Subject: [PATCH 3/9] + Added all IANA methods + Used Trie for most lookups + Fixed ArrayTernayTrie lookup --- .../org/eclipse/jetty/http/HttpMethod.java | 270 ++++++++---------- .../eclipse/jetty/util/ArrayTernaryTrie.java | 6 + 2 files changed, 127 insertions(+), 149 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index 8f52e28681a3..81f4c291568b 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -26,189 +26,161 @@ import org.eclipse.jetty.util.Trie; /** - * + * Known HTTP Methods */ public enum HttpMethod { - GET, - POST, - HEAD, - PUT, - OPTIONS, - DELETE, - TRACE, - CONNECT, - MOVE, - PROXY, - PRI, - PATCH, - SEARCH, - REPORT; + // From https://www.iana.org/assignments/http-methods/http-methods.xhtml + ACL(false, true), + BASELINE_CONTROL(false, true), + BIND(false, true), + CHECKIN(false, true), + CHECKOUT(false, true), + CONNECT(false, false), + COPY(false, true), + DELETE(false, true), + GET(true, true), + HEAD(true, true), + LABEL(false, true), + LINK(false, true), + LOCK(false, false), + MERGE(false, true), + MKACTIVITY(false, true), + MKCALENDAR(false, true), + MKCOL(false, true), + MKREDIRECTREF(false, true), + MKWORKSPACE(false, true), + MOVE(false, true), + OPTIONS(true, true), + ORDERPATCH(false, true), + PATCH(false, false), + POST(false, false), + PRI(true, true), + PROPFIND(true, true), + PROPPATCH(false, true), + PUT(false, true), + REBIND(false, true), + REPORT(true, true), + SEARCH(true, true), + TRACE(true, true), + UNBIND(false, true), + UNCHECKOUT(false, true), + UNLINK(false, true), + UNLOCK(false, true), + UPDATE(false, true), + UPDATEREDIRECTREF(false, true), + VERSION_CONTROL(false, true), + + // Other methods + PROXY(false, false); + + private final String _method; + private final byte[] _bytes; + private final ByteBuffer _buffer; + private final boolean _safe; + private final boolean _idempotent; - /** - * Optimized lookup to find a method name and trailing space in a byte array. - * - * @param bytes Array containing ISO-8859-1 characters - * @param position The first valid index - * @param limit The first non valid index - * @return An HttpMethod if a match or null if no easy match. - */ - public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limit) + HttpMethod(boolean safe, boolean idempotent) { - int length = limit - position; - if (length < 4) - return null; - switch (bytes[position]) - { - case 'G': - if (bytes[position + 1] == 'E' && bytes[position + 2] == 'T' && bytes[position + 3] == ' ') - return GET; - break; - case 'P': - switch (bytes[position + 1]) - { - case 'O': - if (bytes[position + 2] == 'S' && bytes[position + 3] == 'T' && length >= 5 && bytes[position + 4] == ' ') - return POST; - return null; - case 'R': - if (bytes[position + 2] == 'I' && bytes[position + 3] == ' ') - return PRI; - if (bytes[position + 2] == 'O' && bytes[position + 3] == 'X' && length >= 6 && bytes[position + 4] == 'Y' && bytes[position + 5] == ' ') - return PROXY; - return null; - case 'U': - if (bytes[position + 2] == 'T' && bytes[position + 3] == ' ') - return PUT; - return null; - case 'A': - if (bytes[position + 2] == 'T' && bytes[position + 3] == 'C' && length >= 6 && bytes[position + 4] == 'H' && bytes[position + 5] == ' ') - return PATCH; - return null; - default: - return null; - } - - case 'H': - if (bytes[position + 1] == 'E' && bytes[position + 2] == 'A' && bytes[position + 3] == 'D' && length >= 5 && bytes[position + 4] == ' ') - return HEAD; - break; - case 'O': - if (bytes[position + 1] == 'P' && bytes[position + 2] == 'T' && bytes[position + 3] == 'I' && length >= 8 && - bytes[position + 4] == 'O' && bytes[position + 5] == 'N' && bytes[position + 6] == 'S' && bytes[position + 7] == ' ') - return OPTIONS; - break; - case 'D': - if (bytes[position + 1] == 'E' && bytes[position + 2] == 'L' && bytes[position + 3] == 'E' && length >= 7 && - bytes[position + 4] == 'T' && bytes[position + 5] == 'E' && bytes[position + 6] == ' ') - return DELETE; - break; - case 'T': - if (bytes[position + 1] == 'R' && bytes[position + 2] == 'A' && bytes[position + 3] == 'C' && length >= 6 && - bytes[position + 4] == 'E' && bytes[position + 5] == ' ') - return TRACE; - break; - case 'C': - if (bytes[position + 1] == 'O' && bytes[position + 2] == 'N' && bytes[position + 3] == 'N' && length >= 8 && - bytes[position + 4] == 'E' && bytes[position + 5] == 'C' && bytes[position + 6] == 'T' && bytes[position + 7] == ' ') - return CONNECT; - break; - case 'M': - if (bytes[position + 1] == 'O' && bytes[position + 2] == 'V' && bytes[position + 3] == 'E' && length >= 5 && bytes[position + 4] == ' ') - return MOVE; - break; - case 'S': - if (bytes[position + 1] == 'E' && bytes[position + 2] == 'A' && bytes[position + 3] == 'R' && length >= 7 && - bytes[position + 4] == 'C' && bytes[position + 5] == 'H' && bytes[position + 6] == ' ') - return SEARCH; - break; - case 'R': - if (bytes[position + 1] == 'E' && bytes[position + 2] == 'P' && bytes[position + 3] == 'O' && length >= 7 && - bytes[position + 4] == 'R' && bytes[position + 5] == 'T' && bytes[position + 6] == ' ') - return REPORT; - break; - default: - break; - } - return null; + _method = toString().replace('_', '-'); + _safe = safe; + _idempotent = idempotent; + _bytes = StringUtil.getBytes(_method); + _buffer = ByteBuffer.wrap(_bytes); } - /** - * 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. - * @return An HttpMethod if a match or null if no easy match. - */ - public static HttpMethod lookAheadGet(ByteBuffer buffer) + public byte[] getBytes() { - if (buffer.hasArray()) - return lookAheadGet(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.arrayOffset() + buffer.limit()); - - int l = buffer.remaining(); - if (l >= 4) - { - HttpMethod m = CACHE.getBest(buffer, 0, l); - if (m != null) - { - int ml = m.asString().length(); - if (l > ml && buffer.get(buffer.position() + ml) == ' ') - return m; - } - } - return null; + return _bytes; } - public static final Trie INSENSITIVE_CACHE = new ArrayTrie<>(); - - static + public boolean is(String s) { - for (HttpMethod method : HttpMethod.values()) - { - INSENSITIVE_CACHE.put(method.toString(), method); - } + return toString().equalsIgnoreCase(s); } - public static final Trie CACHE = new ArrayTernaryTrie<>(false); + public boolean isSafe() + { + return _safe; + } - static + public boolean isIdempotent() { - for (HttpMethod method : HttpMethod.values()) - { - CACHE.put(method.toString(), method); - } + return _idempotent; } - private final ByteBuffer _buffer; - private final byte[] _bytes; + public ByteBuffer asBuffer() + { + return _buffer.asReadOnlyBuffer(); + } - HttpMethod() + public String asString() { - _bytes = StringUtil.getBytes(toString()); - _buffer = ByteBuffer.wrap(_bytes); + return _method; } - public byte[] getBytes() + public String toString() { - return _bytes; + return _method; } - public boolean is(String s) + public static final Trie INSENSITIVE_CACHE = new ArrayTrie<>(252); + public static final Trie CACHE = new ArrayTernaryTrie<>(false, 300); + public static final Trie LOOK_AHEAD = new ArrayTernaryTrie<>(false, 330); + static { - return toString().equalsIgnoreCase(s); + for (HttpMethod method : HttpMethod.values()) + { + if (!INSENSITIVE_CACHE.put(method.asString(), method)) + throw new IllegalStateException("INSENSITIVE_CACHE too small: " + method); + + if (!CACHE.put(method.asString(), method)) + throw new IllegalStateException("CACHE too small: " + method); + + if (!LOOK_AHEAD.put(method.asString() + ' ', method)) + throw new IllegalStateException("LOOK_AHEAD too small: " + method); + } } - public ByteBuffer asBuffer() + /** + * Optimized lookup to find a method name and trailing space in a byte array. + * + * @param bytes Array containing ISO-8859-1 characters + * @param position The first valid index + * @param limit The first non valid index + * @return An HttpMethod if a match or null if no easy match. + */ + public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limit) { - return _buffer.asReadOnlyBuffer(); + int len = limit - position; + if (limit > 3) + { + // Short cut for GET + if (bytes[position] == 'G' && bytes[position + 1] == 'E' && bytes[position + 2] == 'T' && bytes[position + 3] == ' ') + return GET; + // Otherwise lookup in the Trie + return LOOK_AHEAD.getBest(bytes, position, len); + } + return null; } - public String asString() + /** + * 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. + * @return An HttpMethod if a match or null if no easy match. + * @deprecated Not used + */ + @Deprecated + public static HttpMethod lookAheadGet(ByteBuffer buffer) { - return toString(); + return LOOK_AHEAD.getBest(buffer, 0, buffer.remaining()); } /** - * Converts the given String parameter to an HttpMethod + * Converts the given String parameter to an HttpMethod. + * The string may differ from the Enum name as a '-' in the method + * name is represented as a '_' in the Enum name. * * @param method the String to get the equivalent HttpMethod from * @return the HttpMethod or null if the parameter method is unknown diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java index a1eecc1846c1..1c8b976d605e 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java @@ -368,6 +368,12 @@ public V getBest(ByteBuffer b, int offset, int len) return getBest(0, b, offset, len); } + @Override + public V getBest(byte[] b, int offset, int len) + { + return getBest(0, b, offset, len); + } + private V getBest(int t, byte[] b, int offset, int len) { int node = t; From f4349fab5668d278476d7b3effaf9baa4a90c601 Mon Sep 17 00:00:00 2001 From: gregw Date: Fri, 6 Nov 2020 09:01:16 +0100 Subject: [PATCH 4/9] + Added javadoc for isSafe and isIdempotent --- .../main/java/org/eclipse/jetty/http/HttpMethod.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index 81f4c291568b..2ba10e77104a 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -99,11 +99,23 @@ public boolean is(String s) return toString().equalsIgnoreCase(s); } + /** + * An HTTP method is safe if it doesn't alter the state of the server. + * In other words, a method is safe if it leads to a read-only operation. + * Several common HTTP methods are safe: GET , HEAD , or OPTIONS . + * All safe methods are also idempotent, but not all idempotent methods are safe + * @return if the method is safe. + */ public boolean isSafe() { return _safe; } + /** + * An idempotent HTTP method is an HTTP method that can be called many times without different outcomes. + * It would not matter if the method is called only once, or ten times over. The result should be the same. + * @return true if the method is idempotent. + */ public boolean isIdempotent() { return _idempotent; From 064158e3dbcc7d6d8f41f2cc020c80e12a56f49d Mon Sep 17 00:00:00 2001 From: gregw Date: Fri, 6 Nov 2020 09:28:43 +0100 Subject: [PATCH 5/9] + Use enum to encapsulate fact that all safe methods are idempotent... helps with readability --- .../org/eclipse/jetty/http/HttpMethod.java | 100 ++++++++++-------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index 2ba10e77104a..7bbb6a317840 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -31,60 +31,66 @@ public enum HttpMethod { // From https://www.iana.org/assignments/http-methods/http-methods.xhtml - ACL(false, true), - BASELINE_CONTROL(false, true), - BIND(false, true), - CHECKIN(false, true), - CHECKOUT(false, true), - CONNECT(false, false), - COPY(false, true), - DELETE(false, true), - GET(true, true), - HEAD(true, true), - LABEL(false, true), - LINK(false, true), - LOCK(false, false), - MERGE(false, true), - MKACTIVITY(false, true), - MKCALENDAR(false, true), - MKCOL(false, true), - MKREDIRECTREF(false, true), - MKWORKSPACE(false, true), - MOVE(false, true), - OPTIONS(true, true), - ORDERPATCH(false, true), - PATCH(false, false), - POST(false, false), - PRI(true, true), - PROPFIND(true, true), - PROPPATCH(false, true), - PUT(false, true), - REBIND(false, true), - REPORT(true, true), - SEARCH(true, true), - TRACE(true, true), - UNBIND(false, true), - UNCHECKOUT(false, true), - UNLINK(false, true), - UNLOCK(false, true), - UPDATE(false, true), - UPDATEREDIRECTREF(false, true), - VERSION_CONTROL(false, true), + ACL(Type.IDEMPOTENT), + BASELINE_CONTROL(Type.IDEMPOTENT), + BIND(Type.IDEMPOTENT), + CHECKIN(Type.IDEMPOTENT), + CHECKOUT(Type.IDEMPOTENT), + CONNECT(Type.NORMAL), + COPY(Type.IDEMPOTENT), + DELETE(Type.IDEMPOTENT), + GET(Type.SAFE), + HEAD(Type.SAFE), + LABEL(Type.IDEMPOTENT), + LINK(Type.IDEMPOTENT), + LOCK(Type.NORMAL), + MERGE(Type.IDEMPOTENT), + MKACTIVITY(Type.IDEMPOTENT), + MKCALENDAR(Type.IDEMPOTENT), + MKCOL(Type.IDEMPOTENT), + MKREDIRECTREF(Type.IDEMPOTENT), + MKWORKSPACE(Type.IDEMPOTENT), + MOVE(Type.IDEMPOTENT), + OPTIONS(Type.SAFE), + ORDERPATCH(Type.IDEMPOTENT), + PATCH(Type.NORMAL), + POST(Type.NORMAL), + PRI(Type.SAFE), + PROPFIND(Type.SAFE), + PROPPATCH(Type.IDEMPOTENT), + PUT(Type.IDEMPOTENT), + REBIND(Type.IDEMPOTENT), + REPORT(Type.SAFE), + SEARCH(Type.SAFE), + TRACE(Type.SAFE), + UNBIND(Type.IDEMPOTENT), + UNCHECKOUT(Type.IDEMPOTENT), + UNLINK(Type.IDEMPOTENT), + UNLOCK(Type.IDEMPOTENT), + UPDATE(Type.IDEMPOTENT), + UPDATEREDIRECTREF(Type.IDEMPOTENT), + VERSION_CONTROL(Type.IDEMPOTENT), // Other methods - PROXY(false, false); + PROXY(Type.NORMAL); + // The type of the method + private enum Type + { + NORMAL, + IDEMPOTENT, + SAFE + } + private final String _method; private final byte[] _bytes; private final ByteBuffer _buffer; - private final boolean _safe; - private final boolean _idempotent; + private final Type _type; - HttpMethod(boolean safe, boolean idempotent) + HttpMethod(Type type) { _method = toString().replace('_', '-'); - _safe = safe; - _idempotent = idempotent; + _type = type; _bytes = StringUtil.getBytes(_method); _buffer = ByteBuffer.wrap(_bytes); } @@ -108,7 +114,7 @@ public boolean is(String s) */ public boolean isSafe() { - return _safe; + return _type == Type.SAFE; } /** @@ -118,7 +124,7 @@ public boolean isSafe() */ public boolean isIdempotent() { - return _idempotent; + return _type.ordinal() >= Type.IDEMPOTENT.ordinal(); } public ByteBuffer asBuffer() From 235923acb70825c63c9eabfd94a023b6954b72c8 Mon Sep 17 00:00:00 2001 From: gregw Date: Fri, 6 Nov 2020 09:32:20 +0100 Subject: [PATCH 6/9] + replaced dodgey call to toString in constructor with explicit method string --- .../org/eclipse/jetty/http/HttpMethod.java | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index 7bbb6a317840..ea3a9425a5ee 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -31,48 +31,48 @@ public enum HttpMethod { // From https://www.iana.org/assignments/http-methods/http-methods.xhtml - ACL(Type.IDEMPOTENT), - BASELINE_CONTROL(Type.IDEMPOTENT), - BIND(Type.IDEMPOTENT), - CHECKIN(Type.IDEMPOTENT), - CHECKOUT(Type.IDEMPOTENT), - CONNECT(Type.NORMAL), - COPY(Type.IDEMPOTENT), - DELETE(Type.IDEMPOTENT), - GET(Type.SAFE), - HEAD(Type.SAFE), - LABEL(Type.IDEMPOTENT), - LINK(Type.IDEMPOTENT), - LOCK(Type.NORMAL), - MERGE(Type.IDEMPOTENT), - MKACTIVITY(Type.IDEMPOTENT), - MKCALENDAR(Type.IDEMPOTENT), - MKCOL(Type.IDEMPOTENT), - MKREDIRECTREF(Type.IDEMPOTENT), - MKWORKSPACE(Type.IDEMPOTENT), - MOVE(Type.IDEMPOTENT), - OPTIONS(Type.SAFE), - ORDERPATCH(Type.IDEMPOTENT), - PATCH(Type.NORMAL), - POST(Type.NORMAL), - PRI(Type.SAFE), - PROPFIND(Type.SAFE), - PROPPATCH(Type.IDEMPOTENT), - PUT(Type.IDEMPOTENT), - REBIND(Type.IDEMPOTENT), - REPORT(Type.SAFE), - SEARCH(Type.SAFE), - TRACE(Type.SAFE), - UNBIND(Type.IDEMPOTENT), - UNCHECKOUT(Type.IDEMPOTENT), - UNLINK(Type.IDEMPOTENT), - UNLOCK(Type.IDEMPOTENT), - UPDATE(Type.IDEMPOTENT), - UPDATEREDIRECTREF(Type.IDEMPOTENT), - VERSION_CONTROL(Type.IDEMPOTENT), + ACL("ACL", Type.IDEMPOTENT), + BASELINE_CONTROL("BASELINE-CONTROL", Type.IDEMPOTENT), + BIND("BIND", Type.IDEMPOTENT), + CHECKIN("CHECKIN", Type.IDEMPOTENT), + CHECKOUT("CHECKOUT", Type.IDEMPOTENT), + CONNECT("CONNECT", Type.NORMAL), + COPY("COPY", Type.IDEMPOTENT), + DELETE("DELETE", Type.IDEMPOTENT), + GET("GET", Type.SAFE), + HEAD("HEAD", Type.SAFE), + LABEL("LABEL", Type.IDEMPOTENT), + LINK("LINK", Type.IDEMPOTENT), + LOCK("LOCK", Type.NORMAL), + MERGE("MERGE", Type.IDEMPOTENT), + MKACTIVITY("MKACTIVITY", Type.IDEMPOTENT), + MKCALENDAR("MKCALENDAR", Type.IDEMPOTENT), + MKCOL("MKCOL", Type.IDEMPOTENT), + MKREDIRECTREF("MKREDIRECTREF", Type.IDEMPOTENT), + MKWORKSPACE("MKWORKSPACE", Type.IDEMPOTENT), + MOVE("MOVE", Type.IDEMPOTENT), + OPTIONS("OPTIONS", Type.SAFE), + ORDERPATCH("ORDERPATCH", Type.IDEMPOTENT), + PATCH("PATCH", Type.NORMAL), + POST("POST", Type.NORMAL), + PRI("PRI", Type.SAFE), + PROPFIND("PROPFIND", Type.SAFE), + PROPPATCH("PROPPATCH", Type.IDEMPOTENT), + PUT("PUT", Type.IDEMPOTENT), + REBIND("REBIND", Type.IDEMPOTENT), + REPORT("REPORT", Type.SAFE), + SEARCH("SEARCH", Type.SAFE), + TRACE("TRACE", Type.SAFE), + UNBIND("UNBIND", Type.IDEMPOTENT), + UNCHECKOUT("UNCHECKOUT", Type.IDEMPOTENT), + UNLINK("UNLINK", Type.IDEMPOTENT), + UNLOCK("UNLOCK", Type.IDEMPOTENT), + UPDATE("UPDATE", Type.IDEMPOTENT), + UPDATEREDIRECTREF("UPDATEREDIRECTREF", Type.IDEMPOTENT), + VERSION_CONTROL("VERSION-CONTROL", Type.IDEMPOTENT), // Other methods - PROXY(Type.NORMAL); + PROXY("PROXY", Type.NORMAL); // The type of the method private enum Type @@ -87,9 +87,9 @@ private enum Type private final ByteBuffer _buffer; private final Type _type; - HttpMethod(Type type) + HttpMethod(String method, Type type) { - _method = toString().replace('_', '-'); + _method = method; _type = type; _bytes = StringUtil.getBytes(_method); _buffer = ByteBuffer.wrap(_bytes); From f1802a23eb91c650baf31938ecdada78c69c3b94 Mon Sep 17 00:00:00 2001 From: gregw Date: Fri, 6 Nov 2020 16:04:38 +0100 Subject: [PATCH 7/9] Updates from review --- .../org/eclipse/jetty/http/HttpMethod.java | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index ea3a9425a5ee..fc6b27c953c6 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -145,6 +145,10 @@ public String toString() public static final Trie INSENSITIVE_CACHE = new ArrayTrie<>(252); public static final Trie CACHE = new ArrayTernaryTrie<>(false, 300); public static final Trie LOOK_AHEAD = new ArrayTernaryTrie<>(false, 330); + private static final int ACL_AS_INT = ('A' & 0xff) << 24 | ('C' & 0xFF) << 16 | ('L' & 0xFF) << 8 | (' ' & 0xFF); + private static final int GET_AS_INT = ('G' & 0xff) << 24 | ('E' & 0xFF) << 16 | ('T' & 0xFF) << 8 | (' ' & 0xFF); + private static final int PRI_AS_INT = ('P' & 0xff) << 24 | ('R' & 0xFF) << 16 | ('I' & 0xFF) << 8 | (' ' & 0xFF); + private static final int PUT_AS_INT = ('P' & 0xff) << 24 | ('U' & 0xFF) << 16 | ('T' & 0xFF) << 8 | (' ' & 0xFF); static { for (HttpMethod method : HttpMethod.values()) @@ -167,19 +171,12 @@ public String toString() * @param position The first valid index * @param limit The first non valid index * @return An HttpMethod if a match or null if no easy match. + * @deprecated Not used */ + @Deprecated public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limit) { - int len = limit - position; - if (limit > 3) - { - // Short cut for GET - if (bytes[position] == 'G' && bytes[position + 1] == 'E' && bytes[position + 2] == 'T' && bytes[position + 3] == ' ') - return GET; - // Otherwise lookup in the Trie - return LOOK_AHEAD.getBest(bytes, position, len); - } - return null; + return LOOK_AHEAD.getBest(bytes, position, limit - position); } /** @@ -187,12 +184,28 @@ public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limi * * @param buffer buffer containing ISO-8859-1 characters, it is not modified. * @return An HttpMethod if a match or null if no easy match. - * @deprecated Not used */ - @Deprecated public static HttpMethod lookAheadGet(ByteBuffer buffer) { - return LOOK_AHEAD.getBest(buffer, 0, buffer.remaining()); + 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; + default: + break; + } + } + return LOOK_AHEAD.getBest(buffer, 0, len); } /** From 3e94fae5e8c5c3c5e2c19515f8c3c61b5bcb3144 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 6 Nov 2020 09:26:58 -0600 Subject: [PATCH 8/9] Issue #5575 - simpler HttpMethod enum Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/http/HttpMethod.java | 86 +++++++++---------- .../eclipse/jetty/http/HttpParserTest.java | 12 +++ 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index fc6b27c953c6..b7e2f56ab0cb 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -31,48 +31,48 @@ public enum HttpMethod { // From https://www.iana.org/assignments/http-methods/http-methods.xhtml - ACL("ACL", Type.IDEMPOTENT), - BASELINE_CONTROL("BASELINE-CONTROL", Type.IDEMPOTENT), - BIND("BIND", Type.IDEMPOTENT), - CHECKIN("CHECKIN", Type.IDEMPOTENT), - CHECKOUT("CHECKOUT", Type.IDEMPOTENT), - CONNECT("CONNECT", Type.NORMAL), - COPY("COPY", Type.IDEMPOTENT), - DELETE("DELETE", Type.IDEMPOTENT), - GET("GET", Type.SAFE), - HEAD("HEAD", Type.SAFE), - LABEL("LABEL", Type.IDEMPOTENT), - LINK("LINK", Type.IDEMPOTENT), - LOCK("LOCK", Type.NORMAL), - MERGE("MERGE", Type.IDEMPOTENT), - MKACTIVITY("MKACTIVITY", Type.IDEMPOTENT), - MKCALENDAR("MKCALENDAR", Type.IDEMPOTENT), - MKCOL("MKCOL", Type.IDEMPOTENT), - MKREDIRECTREF("MKREDIRECTREF", Type.IDEMPOTENT), - MKWORKSPACE("MKWORKSPACE", Type.IDEMPOTENT), - MOVE("MOVE", Type.IDEMPOTENT), - OPTIONS("OPTIONS", Type.SAFE), - ORDERPATCH("ORDERPATCH", Type.IDEMPOTENT), - PATCH("PATCH", Type.NORMAL), - POST("POST", Type.NORMAL), - PRI("PRI", Type.SAFE), - PROPFIND("PROPFIND", Type.SAFE), - PROPPATCH("PROPPATCH", Type.IDEMPOTENT), - PUT("PUT", Type.IDEMPOTENT), - REBIND("REBIND", Type.IDEMPOTENT), - REPORT("REPORT", Type.SAFE), - SEARCH("SEARCH", Type.SAFE), - TRACE("TRACE", Type.SAFE), - UNBIND("UNBIND", Type.IDEMPOTENT), - UNCHECKOUT("UNCHECKOUT", Type.IDEMPOTENT), - UNLINK("UNLINK", Type.IDEMPOTENT), - UNLOCK("UNLOCK", Type.IDEMPOTENT), - UPDATE("UPDATE", Type.IDEMPOTENT), - UPDATEREDIRECTREF("UPDATEREDIRECTREF", Type.IDEMPOTENT), - VERSION_CONTROL("VERSION-CONTROL", Type.IDEMPOTENT), + ACL(Type.IDEMPOTENT), + BASELINE_CONTROL(Type.IDEMPOTENT), + BIND(Type.IDEMPOTENT), + CHECKIN(Type.IDEMPOTENT), + CHECKOUT(Type.IDEMPOTENT), + CONNECT(Type.NORMAL), + COPY(Type.IDEMPOTENT), + DELETE(Type.IDEMPOTENT), + GET(Type.SAFE), + HEAD(Type.SAFE), + LABEL(Type.IDEMPOTENT), + LINK(Type.IDEMPOTENT), + LOCK(Type.NORMAL), + MERGE(Type.IDEMPOTENT), + MKACTIVITY(Type.IDEMPOTENT), + MKCALENDAR(Type.IDEMPOTENT), + MKCOL(Type.IDEMPOTENT), + MKREDIRECTREF(Type.IDEMPOTENT), + MKWORKSPACE(Type.IDEMPOTENT), + MOVE(Type.IDEMPOTENT), + OPTIONS(Type.SAFE), + ORDERPATCH(Type.IDEMPOTENT), + PATCH(Type.NORMAL), + POST(Type.NORMAL), + PRI(Type.SAFE), + PROPFIND(Type.SAFE), + PROPPATCH(Type.IDEMPOTENT), + PUT(Type.IDEMPOTENT), + REBIND(Type.IDEMPOTENT), + REPORT(Type.SAFE), + SEARCH(Type.SAFE), + TRACE(Type.SAFE), + UNBIND(Type.IDEMPOTENT), + UNCHECKOUT(Type.IDEMPOTENT), + UNLINK(Type.IDEMPOTENT), + UNLOCK(Type.IDEMPOTENT), + UPDATE(Type.IDEMPOTENT), + UPDATEREDIRECTREF(Type.IDEMPOTENT), + VERSION_CONTROL(Type.IDEMPOTENT), // Other methods - PROXY("PROXY", Type.NORMAL); + PROXY(Type.NORMAL); // The type of the method private enum Type @@ -81,15 +81,15 @@ private enum Type IDEMPOTENT, SAFE } - + private final String _method; private final byte[] _bytes; private final ByteBuffer _buffer; private final Type _type; - HttpMethod(String method, Type type) + HttpMethod(Type type) { - _method = method; + _method = name().replace('_', '-'); _type = type; _bytes = StringUtil.getBytes(_method); _buffer = ByteBuffer.wrap(_bytes); diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index 1935ca2fd780..39d72a13be6a 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; import org.eclipse.jetty.http.HttpParser.State; import org.eclipse.jetty.toolchain.test.Net; @@ -32,6 +33,8 @@ import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.eclipse.jetty.http.HttpComplianceSection.NO_FIELD_FOLDING; import static org.hamcrest.MatcherAssert.assertThat; @@ -108,6 +111,15 @@ public void httpMethodTest() assertEquals(HttpMethod.GET, HttpMethod.lookAheadGet(b)); } + @ParameterizedTest + @ValueSource(strings = {"GET", "POST", "VERSION-CONTROL"}) + public void httpMethodNameTest(String methodName) + { + HttpMethod method = HttpMethod.fromString(methodName); + assertNotNull(method, "Method should have been found: " + methodName); + assertEquals(methodName.toUpperCase(Locale.US), method.toString()); + } + @Test public void testLineParseMockIP() { From bf98ae26e74c7b166892c24dab0f28cd7221f5cb Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 10 Nov 2020 18:31:53 +0100 Subject: [PATCH 9/9] Update from review + added benchmark + optimised POST and HEAD --- .../org/eclipse/jetty/http/HttpMethod.java | 18 ++- .../jetty/http/jmh/HttpMethodBenchmark.java | 127 ++++++++++++++++++ 2 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 jetty-jmh/src/main/java/org/eclipse/jetty/http/jmh/HttpMethodBenchmark.java diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index b7e2f56ab0cb..e0e035072dc3 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -145,10 +145,12 @@ public String toString() public static final Trie INSENSITIVE_CACHE = new ArrayTrie<>(252); public static final Trie CACHE = new ArrayTernaryTrie<>(false, 300); public static final Trie LOOK_AHEAD = new ArrayTernaryTrie<>(false, 330); - private static final int ACL_AS_INT = ('A' & 0xff) << 24 | ('C' & 0xFF) << 16 | ('L' & 0xFF) << 8 | (' ' & 0xFF); - private static final int GET_AS_INT = ('G' & 0xff) << 24 | ('E' & 0xFF) << 16 | ('T' & 0xFF) << 8 | (' ' & 0xFF); - private static final int PRI_AS_INT = ('P' & 0xff) << 24 | ('R' & 0xFF) << 16 | ('I' & 0xFF) << 8 | (' ' & 0xFF); - private static final int PUT_AS_INT = ('P' & 0xff) << 24 | ('U' & 0xFF) << 16 | ('T' & 0xFF) << 8 | (' ' & 0xFF); + public static final int ACL_AS_INT = ('A' & 0xff) << 24 | ('C' & 0xFF) << 16 | ('L' & 0xFF) << 8 | (' ' & 0xFF); + public static final int GET_AS_INT = ('G' & 0xff) << 24 | ('E' & 0xFF) << 16 | ('T' & 0xFF) << 8 | (' ' & 0xFF); + public static final int PRI_AS_INT = ('P' & 0xff) << 24 | ('R' & 0xFF) << 16 | ('I' & 0xFF) << 8 | (' ' & 0xFF); + public static final int PUT_AS_INT = ('P' & 0xff) << 24 | ('U' & 0xFF) << 16 | ('T' & 0xFF) << 8 | (' ' & 0xFF); + public static final int POST_AS_INT = ('P' & 0xff) << 24 | ('O' & 0xFF) << 16 | ('S' & 0xFF) << 8 | ('T' & 0xFF); + public static final int HEAD_AS_INT = ('H' & 0xff) << 24 | ('E' & 0xFF) << 16 | ('A' & 0xFF) << 8 | ('D' & 0xFF); static { for (HttpMethod method : HttpMethod.values()) @@ -201,6 +203,14 @@ public static HttpMethod lookAheadGet(ByteBuffer buffer) 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; } diff --git a/jetty-jmh/src/main/java/org/eclipse/jetty/http/jmh/HttpMethodBenchmark.java b/jetty-jmh/src/main/java/org/eclipse/jetty/http/jmh/HttpMethodBenchmark.java new file mode 100644 index 000000000000..283a866dab1a --- /dev/null +++ b/jetty-jmh/src/main/java/org/eclipse/jetty/http/jmh/HttpMethodBenchmark.java @@ -0,0 +1,127 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.http.jmh; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.util.BufferUtil; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.profile.GCProfiler; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@State(Scope.Benchmark) +@Threads(4) +@Warmup(iterations = 5, time = 2000, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 5, time = 2000, timeUnit = TimeUnit.MILLISECONDS) +public class HttpMethodBenchmark +{ + private static final ByteBuffer GET = BufferUtil.toBuffer("GET / HTTP/1.1\r\n\r\n"); + private static final ByteBuffer POST = BufferUtil.toBuffer("POST / HTTP/1.1\r\n\r\n"); + private static final ByteBuffer MOVE = BufferUtil.toBuffer("MOVE / HTTP/1.1\r\n\r\n"); + private static final Map MAP = new HashMap<>(); + + static + { + for (HttpMethod m : HttpMethod.values()) + MAP.put(m.asString(), m); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public HttpMethod testTrieGetBest() throws Exception + { + return HttpMethod.LOOK_AHEAD.getBest(GET, 0, GET.remaining()); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public HttpMethod testIntSwitch() throws Exception + { + switch (GET.getInt(0)) + { + case HttpMethod.ACL_AS_INT: + return HttpMethod.ACL; + case HttpMethod.GET_AS_INT: + return HttpMethod.GET; + case HttpMethod.PRI_AS_INT: + return HttpMethod.PRI; + case HttpMethod.PUT_AS_INT: + return HttpMethod.PUT; + default: + return null; + } + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public HttpMethod testMapGet() throws Exception + { + for (int i = 0; i < GET.remaining(); i++) + { + if (GET.get(i) == (byte)' ') + return MAP.get(BufferUtil.toString(GET, 0, i, StandardCharsets.US_ASCII)); + } + return null; + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public HttpMethod testHttpMethodPost() throws Exception + { + return HttpMethod.lookAheadGet(POST); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public HttpMethod testHttpMethodMove() throws Exception + { + return HttpMethod.lookAheadGet(MOVE); + } + + public static void main(String[] args) throws RunnerException + { + Options opt = new OptionsBuilder() + .include(HttpMethodBenchmark.class.getSimpleName()) + .warmupIterations(10) + .measurementIterations(10) + .addProfiler(GCProfiler.class) + .forks(1) + .threads(1) + .build(); + + new Runner(opt).run(); + } +} + +