This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push:
new b7590ef434 Optimize conversion of method from bytes to String
b7590ef434 is described below
commit b7590ef4349576b40c41a1a295b6b97be1c23ccd
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Sep 11 08:48:07 2025 +0100
Optimize conversion of method from bytes to String
The savings are small but noticeable in performance tests - particularly
with embedded that doesn't use the StringCache by default.
Because HttpServlet calls HttpServletRequest.getmethod(), nearly every
request will convert the method to a String so it is more efficient to
do the conversion early and store it as a String rather than use
MessageBytes.
Unknown methods do attract a small performance penalty but there should
be very few of those.
---
.../catalina/authenticator/FormAuthenticator.java | 6 +-
.../apache/catalina/connector/CoyoteAdapter.java | 6 +-
.../apache/catalina/connector/OutputBuffer.java | 4 +-
java/org/apache/catalina/connector/Request.java | 2 +-
java/org/apache/coyote/Request.java | 27 ++++
java/org/apache/coyote/RequestInfo.java | 2 +-
java/org/apache/coyote/ajp/AjpProcessor.java | 10 +-
.../apache/coyote/http11/Http11InputBuffer.java | 3 +-
java/org/apache/coyote/http11/Http11Processor.java | 4 +-
java/org/apache/coyote/http2/Stream.java | 8 +-
java/org/apache/coyote/http2/StreamProcessor.java | 3 +-
java/org/apache/tomcat/util/http/Method.java | 149 +++++++++++++++++++++
test/org/apache/tomcat/util/http/TestMethod.java | 43 ++++++
.../tomcat/util/http/TestMethodPerformance.java | 67 +++++++++
webapps/docs/changelog.xml | 4 +
15 files changed, 315 insertions(+), 23 deletions(-)
diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java
b/java/org/apache/catalina/authenticator/FormAuthenticator.java
index 3e5c60ff8d..ca8aebf402 100644
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -443,7 +443,7 @@ public class FormAuthenticator extends AuthenticatorBase {
// Always use GET for the login page, regardless of the method used
String oldMethod = request.getMethod();
- request.getCoyoteRequest().method().setString("GET");
+ request.getCoyoteRequest().setMethod("GET");
RequestDispatcher disp =
context.getServletContext().getRequestDispatcher(loginPage);
try {
@@ -459,7 +459,7 @@ public class FormAuthenticator extends AuthenticatorBase {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
msg);
} finally {
// Restore original method so that it is written into access log
- request.getCoyoteRequest().method().setString(oldMethod);
+ request.getCoyoteRequest().setMethod(oldMethod);
}
}
@@ -627,7 +627,7 @@ public class FormAuthenticator extends AuthenticatorBase {
request.getCoyoteRequest().setContentType(contentType);
}
- request.getCoyoteRequest().method().setString(method);
+ request.getCoyoteRequest().setMethod(method);
// The method, URI, queryString and protocol are normally stored as
// bytes in the HttpInputBuffer and converted lazily to String. At this
// point, the method has already been set as String in the line above
diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java
b/java/org/apache/catalina/connector/CoyoteAdapter.java
index 8b94d2e705..fcb4cbdc81 100644
--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
+++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -592,7 +592,7 @@ public class CoyoteAdapter implements Adapter {
// Check for ping OPTIONS * request
if (undecodedURI.equals("*")) {
- if (req.method().equals("OPTIONS")) {
+ if ("OPTIONS".equals(req.getMethod())) {
StringBuilder allow = new StringBuilder();
allow.append("GET, HEAD, POST, PUT, DELETE, OPTIONS");
// Trace if allowed
@@ -611,7 +611,7 @@ public class CoyoteAdapter implements Adapter {
MessageBytes decodedURI = req.decodedURI();
// Filter CONNECT method
- if (req.method().equals("CONNECT")) {
+ if ("CONNECT".equals(req.getMethod())) {
response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED,
sm.getString("coyoteAdapter.connect"));
} else {
// No URI for CONNECT requests
@@ -810,7 +810,7 @@ public class CoyoteAdapter implements Adapter {
}
// Filter TRACE method
- if (!connector.getAllowTrace() && req.method().equals("TRACE")) {
+ if (!connector.getAllowTrace() && "TRACE".equals(req.getMethod())) {
Wrapper wrapper = request.getWrapper();
StringBuilder header = null;
if (wrapper != null) {
diff --git a/java/org/apache/catalina/connector/OutputBuffer.java
b/java/org/apache/catalina/connector/OutputBuffer.java
index 9ae1374c3f..b44fbf4a83 100644
--- a/java/org/apache/catalina/connector/OutputBuffer.java
+++ b/java/org/apache/catalina/connector/OutputBuffer.java
@@ -226,8 +226,8 @@ public class OutputBuffer extends Writer {
// - the content length has not been explicitly set
// AND
// - some content has been written OR this is NOT a HEAD request
- if ((!coyoteResponse.isCommitted()) &&
(coyoteResponse.getContentLengthLong() == -1) &&
- ((bb.remaining() > 0 ||
!coyoteResponse.getRequest().method().equals("HEAD")))) {
+ if (!coyoteResponse.isCommitted() &&
coyoteResponse.getContentLengthLong() == -1 &&
+ (bb.remaining() > 0 ||
!"HEAD".equals(coyoteResponse.getRequest().getMethod()))) {
coyoteResponse.setContentLength(bb.remaining());
}
diff --git a/java/org/apache/catalina/connector/Request.java
b/java/org/apache/catalina/connector/Request.java
index 46302d7f80..1ac648240f 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -2068,7 +2068,7 @@ public class Request implements HttpServletRequest {
@Override
public String getMethod() {
- return coyoteRequest.method().toStringType();
+ return coyoteRequest.getMethod();
}
diff --git a/java/org/apache/coyote/Request.java
b/java/org/apache/coyote/Request.java
index 07cad56956..302589494f 100644
--- a/java/org/apache/coyote/Request.java
+++ b/java/org/apache/coyote/Request.java
@@ -34,6 +34,7 @@ import jakarta.servlet.ServletConnection;
import org.apache.tomcat.util.buf.CharsetHolder;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.buf.UDecoder;
+import org.apache.tomcat.util.http.Method;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.Parameters;
import org.apache.tomcat.util.http.ServerCookies;
@@ -314,10 +315,36 @@ public final class Request {
return schemeMB;
}
+ /**
+ * Get a MessageBytes instance that holds the current request's HTTP
method.
+ *
+ * @return a MessageBytes instance that holds the current request's HTTP
method.
+ *
+ * @deprecated Use {@link #getMethod()}, {@link Request#setMethod(String)}
and {@link #setMethod(byte[], int, int)}
+ */
+ @Deprecated
public MessageBytes method() {
return methodMB;
}
+ public void setMethod(String method) {
+ methodMB.setString(method);
+ }
+
+ public void setMethod(byte[] buf, int start, int len) {
+ String method = Method.bytesToString(buf, start, len);
+ if (method == null) {
+ methodMB.setBytes(buf, start, len);
+ method = methodMB.toStringType();
+ } else {
+ methodMB.setString(method);
+ }
+ }
+
+ public String getMethod() {
+ return methodMB.toStringType();
+ }
+
public MessageBytes requestURI() {
return uriMB;
}
diff --git a/java/org/apache/coyote/RequestInfo.java
b/java/org/apache/coyote/RequestInfo.java
index 0dc534da74..214d4ec0e9 100644
--- a/java/org/apache/coyote/RequestInfo.java
+++ b/java/org/apache/coyote/RequestInfo.java
@@ -65,7 +65,7 @@ public class RequestInfo {
// This is useful for long-running requests only
public String getMethod() {
- return req.method().toString();
+ return req.getMethod();
}
public String getCurrentUri() {
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java
b/java/org/apache/coyote/ajp/AjpProcessor.java
index 8bb55ecffb..971fadff58 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -642,7 +642,7 @@ public class AjpProcessor extends AbstractProcessor {
byte methodCode = requestHeaderMessage.getByte();
if (methodCode != Constants.SC_M_JK_STORED) {
String methodName = Constants.getMethodForCode(methodCode - 1);
- request.method().setString(methodName);
+ request.setMethod(methodName);
}
requestHeaderMessage.getBytes(request.protocol());
@@ -810,7 +810,11 @@ public class AjpProcessor extends AbstractProcessor {
}
case Constants.SC_A_SSL_KEY_SIZE ->
request.setAttribute(SSLSupport.KEY_SIZE_KEY,
Integer.valueOf(requestHeaderMessage.getInt()));
- case Constants.SC_A_STORED_METHOD ->
requestHeaderMessage.getBytes(request.method());
+ case Constants.SC_A_STORED_METHOD -> {
+ requestHeaderMessage.getBytes(tmpMB);
+ ByteChunk tmpBC = tmpMB.getByteChunk();
+ request.setMethod(tmpBC.getBytes(), tmpBC.getStart(),
tmpBC.getLength());
+ }
case Constants.SC_A_SECRET -> {
requestHeaderMessage.getBytes(tmpMB);
if (secret != null && !secret.isEmpty()) {
@@ -904,7 +908,7 @@ public class AjpProcessor extends AbstractProcessor {
// Responses with certain status codes and/or methods are not
permitted to include a response body.
int statusCode = response.getStatus();
if (statusCode < 200 || statusCode == 204 || statusCode == 205 ||
statusCode == 304 ||
- request.method().equals("HEAD")) {
+ "HEAD".equals(request.getMethod())) {
// No entity body
swallowResponse = true;
}
diff --git a/java/org/apache/coyote/http11/Http11InputBuffer.java
b/java/org/apache/coyote/http11/Http11InputBuffer.java
index c3f85487b9..977036b093 100644
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -383,8 +383,7 @@ public class Http11InputBuffer implements InputBuffer,
ApplicationBufferHandler,
chr = byteBuffer.get();
if (chr == Constants.SP || chr == Constants.HT) {
space = true;
- request.method().setBytes(byteBuffer.array(),
parsingRequestLineStart,
- pos - parsingRequestLineStart);
+ request.setMethod(byteBuffer.array(),
parsingRequestLineStart, pos - parsingRequestLineStart);
} else if (!HttpParser.isToken(chr)) {
// Avoid unknown protocol triggering an additional error
request.protocol().setString(Constants.HTTP_11);
diff --git a/java/org/apache/coyote/http11/Http11Processor.java
b/java/org/apache/coyote/http11/Http11Processor.java
index a40db16f6e..4ab60b22ab 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -501,7 +501,7 @@ public class Http11Processor extends AbstractProcessor {
// Transfer the minimal information required for the copy of the
Request
// that is passed to the HTTP upgrade process
dest.decodedURI().duplicate(source.decodedURI());
- dest.method().duplicate(source.method());
+ dest.setMethod(source.getMethod());
dest.getMimeHeaders().duplicate(source.getMimeHeaders());
dest.requestURI().duplicate(source.requestURI());
dest.queryString().duplicate(source.queryString());
@@ -890,7 +890,7 @@ public class Http11Processor extends AbstractProcessor {
}
}
- boolean head = request.method().equals("HEAD");
+ boolean head = "HEAD".equals(request.getMethod());
if (head) {
// Any entity body, if present, should not be sent
outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
diff --git a/java/org/apache/coyote/http2/Stream.java
b/java/org/apache/coyote/http2/Stream.java
index bb25d9ef1c..e79ae63e86 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -362,8 +362,8 @@ class Stream extends AbstractNonZeroStream implements
HeaderEmitter {
switch (name) {
case ":method": {
- if (coyoteRequest.method().isNull()) {
- coyoteRequest.method().setString(value);
+ if (coyoteRequest.getMethod() == null) {
+ coyoteRequest.setMethod(value);
if ("HEAD".equals(value)) {
configureVoidOutputFilter();
}
@@ -546,8 +546,8 @@ class Stream extends AbstractNonZeroStream implements
HeaderEmitter {
final boolean receivedEndOfHeaders() throws ConnectionException {
- if (coyoteRequest.method().isNull() || coyoteRequest.scheme().isNull()
||
- !coyoteRequest.method().equals("CONNECT") &&
coyoteRequest.requestURI().isNull()) {
+ if (coyoteRequest.getMethod() == null ||
coyoteRequest.scheme().isNull() ||
+ !"CONNECT".equals(coyoteRequest.getMethod()) &&
coyoteRequest.requestURI().isNull()) {
throw new
ConnectionException(sm.getString("stream.header.required", getConnectionId(),
getIdAsString()),
Http2Error.PROTOCOL_ERROR);
}
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java
b/java/org/apache/coyote/http2/StreamProcessor.java
index 6aa9b5a7f9..942ed2014c 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -505,8 +505,7 @@ class StreamProcessor extends AbstractProcessor implements
NonPipeliningProcesso
HttpParser httpParser =
handler.getProtocol().getHttp11Protocol().getHttpParser();
// Method name must be a token
- String method = request.method().toString();
- if (!HttpParser.isToken(method)) {
+ if (!HttpParser.isToken(request.getMethod())) {
return false;
}
diff --git a/java/org/apache/tomcat/util/http/Method.java
b/java/org/apache/tomcat/util/http/Method.java
new file mode 100644
index 0000000000..31411c6171
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/Method.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.http;
+
+public class Method {
+
+ // Standard HTTP methods supported by HttpServlet
+ public static final String GET = "GET";
+ public static final String POST = "POST";
+ public static final String PUT = "PUT";
+ public static final String PATCH = "PATCH";
+ public static final String HEAD = "HEAD";
+ public static final String OPTIONS = "OPTIONS";
+ public static final String DELETE = "DELETE";
+ public static final String TRACE = "TRACE";
+ // Additional WebDAV methods
+ public static final String PROPFIND = "PROPFIND";
+ public static final String PROPPATCH = "PROPPATCH";
+ public static final String MKCOL = "MKCOL";
+ public static final String COPY = "COPY";
+ public static final String MOVE = "MOVE";
+ public static final String LOCK = "LOCK";
+ public static final String UNLOCK = "UNLOCK";
+ // Other methods recognised by Tomcat
+ public static final String CONNECT = "CONNECT";
+
+
+ /**
+ * Provides optimised conversion from bytes to Strings for known HTTP
methods. The bytes are assumed to be an
+ * ISO-8859-1 encoded representation of an HTTP method. The method is not
validated as being a token, but only valid
+ * HTTP method names will be returned.
+ * <p>
+ * Doing in this way is ~10x faster than using MessageBytes.toStringType()
saving ~40ns per request which is ~1% of
+ * the processing time for a minimal "Hello World" type servlet. For
non-standard methods there is an additional
+ * overhead of ~2.5ns per request.
+ * <p>
+ * Pretty much every request ends up converting the method to a String so
it is more efficient to do this straight
+ * away and always use Strings.
+ *
+ * @param buf The byte buffer containing the HTTP method to convert
+ * @param start The first byte of the HTTP method
+ * @param len The number of bytes to convert
+ *
+ * @return The HTTP method as a String or {@code null} if the method is
not recognised.
+ */
+ public static String bytesToString(byte[] buf, int start, int len) {
+ switch (buf[start]) {
+ case 'G': {
+ if (len == 3 && buf[start + 1] == 'E' && buf[start + 2] ==
'T') {
+ return GET;
+ }
+ break;
+ }
+ case 'P': {
+ if (len == 4 && buf[start + 1] == 'O' && buf[start + 2] == 'S'
&& buf[start + 3] == 'T') {
+ return POST;
+ } else if (len == 3 && buf[start + 1] == 'U' && buf[start + 2]
== 'T') {
+ return PUT;
+ } else if (len == 5 && buf[start + 1] == 'A' && buf[start + 2]
== 'T' && buf[start + 3] == 'C' &&
+ buf[start + 4] == 'H') {
+ return PATCH;
+ } else if (len == 8 && buf[start + 1] == 'R' && buf[start + 2]
== 'O' && buf[start + 3] == 'P' &&
+ buf[start + 4] == 'F' && buf[start + 5] == 'I' &&
buf[start + 6] == 'N' &&
+ buf[start + 7] == 'D') {
+ return PROPFIND;
+ } else if (len == 9 && buf[start + 1] == 'R' && buf[start + 2]
== 'O' && buf[start + 3] == 'P' &&
+ buf[start + 4] == 'P' && buf[start + 5] == 'A' &&
buf[start + 6] == 'T' &&
+ buf[start + 7] == 'C' && buf[start + 8] == 'H') {
+ return PROPPATCH;
+ }
+ break;
+ }
+ case 'H': {
+ if (len == 4 && buf[start + 1] == 'E' && buf[start + 2] == 'A'
&& buf[start + 3] == 'D') {
+ return HEAD;
+ }
+ break;
+ }
+ case 'O': {
+ if (len == 7 && buf[start + 1] == 'P' && buf[start + 2] == 'T'
&& buf[start + 3] == 'I' &&
+ buf[start + 4] == 'O' && buf[start + 5] == 'N' &&
buf[start + 6] == 'S') {
+ return OPTIONS;
+ }
+ break;
+ }
+ case 'D': {
+ if (len == 6 && buf[start + 1] == 'E' && buf[start + 2] == 'L'
&& buf[start + 3] == 'E' &&
+ buf[start + 4] == 'T' && buf[start + 5] == 'E') {
+ return DELETE;
+ }
+ break;
+ }
+ case 'T': {
+ if (len == 5 && buf[start + 1] == 'R' && buf[start + 2] == 'A'
&& buf[start + 3] == 'C' &&
+ buf[start + 4] == 'E') {
+ return TRACE;
+ }
+ break;
+ }
+ case 'M': {
+ if (len == 5 && buf[start + 1] == 'K' && buf[start + 2] == 'C'
&& buf[start + 3] == 'O' &&
+ buf[start + 4] == 'L') {
+ return MKCOL;
+ } else if (len == 4 && buf[start + 1] == 'O' && buf[start + 2]
== 'V' && buf[start + 3] == 'E') {
+ return MOVE;
+ }
+ break;
+ }
+ case 'C': {
+ if (len == 4 && buf[start + 1] == 'O' && buf[start + 2] == 'P'
&& buf[start + 3] == 'Y') {
+ return COPY;
+ } else if (len == 7 && buf[start + 1] == 'O' && buf[start + 2]
== 'N' && buf[start + 3] == 'N' &&
+ buf[start + 4] == 'E' && buf[start + 5] == 'C' &&
buf[start + 6] == 'T') {
+ return CONNECT;
+ }
+ break;
+ }
+ case 'L': {
+ if (len == 4 && buf[start + 1] == 'O' && buf[start + 2] == 'C'
&& buf[start + 3] == 'K') {
+ return LOCK;
+ }
+ break;
+ }
+ case 'U': {
+ if (len == 6 && buf[start + 1] == 'N' && buf[start + 2] == 'L'
&& buf[start + 3] == 'O' &&
+ buf[start + 4] == 'C' && buf[start + 5] == 'K') {
+ return UNLOCK;
+ }
+ break;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/test/org/apache/tomcat/util/http/TestMethod.java
b/test/org/apache/tomcat/util/http/TestMethod.java
new file mode 100644
index 0000000000..a5fc7b7c28
--- /dev/null
+++ b/test/org/apache/tomcat/util/http/TestMethod.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.http;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestMethod {
+
+ /*
+ * Not testing performance. Just checking that there are no errors in the
parsing code.
+ */
+ @Test
+ public void testHttpMethodParsing() {
+ List<String> methods = Arrays.asList(Method.GET, Method.POST,
Method.PUT, Method.PATCH, Method.HEAD,
+ Method.OPTIONS, Method.DELETE, Method.TRACE, Method.PROPPATCH,
Method.PROPFIND, Method.MKCOL,
+ Method.COPY, Method.MOVE, Method.LOCK, Method.UNLOCK,
Method.CONNECT);
+
+ for (String method : methods) {
+ byte[] bytes = method.getBytes(StandardCharsets.ISO_8859_1);
+ String result = Method.bytesToString(bytes, 0, bytes.length);
+ Assert.assertEquals(method, result);
+ }
+ }
+}
diff --git a/test/org/apache/tomcat/util/http/TestMethodPerformance.java
b/test/org/apache/tomcat/util/http/TestMethodPerformance.java
new file mode 100644
index 0000000000..e2e3212b4e
--- /dev/null
+++ b/test/org/apache/tomcat/util/http/TestMethodPerformance.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.http;
+
+import java.nio.charset.StandardCharsets;
+
+import org.junit.Test;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+
+public class TestMethodPerformance {
+
+ private static final int LOOPS = 6;
+ private static final int ITERATIONS = 100000000;
+
+ private static final String INPUT = "GET
/context-path/servlet-path/path-info HTTP/1.1";
+ private static final byte[] INPUT_BYTES =
INPUT.getBytes(StandardCharsets.UTF_8);
+
+ private static MessageBytes mb = MessageBytes.newInstance();
+
+ @Test
+ public void testGetMethodPerformance() throws Exception {
+
+ for (int j = 0; j < LOOPS; j++) {
+ long start = System.nanoTime();
+ for (int i = 0; i < ITERATIONS; i++) {
+ mb.setBytes(INPUT_BYTES, 0, 3);
+ mb.toStringType();
+ }
+ long duration = System.nanoTime() - start;
+
+ if (j > 0) {
+ System.out.println("MessageBytes conversion took :" + duration
+ "ns");
+ }
+ }
+
+ for (int j = 0; j < LOOPS; j++) {
+ long start = System.nanoTime();
+ for (int i = 0; i < ITERATIONS; i++) {
+ String method = Method.bytesToString(INPUT_BYTES, 0, 3);
+ if (method == null) {
+ mb.setBytes(INPUT_BYTES, 0, 5);
+ mb.toStringType();
+ }
+ }
+ long duration = System.nanoTime() - start;
+
+ if (j > 0) {
+ System.out.println("Optimized conversion took :" + duration +
"ns");
+ }
+ }
+ }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index d98c4be3c7..3503a8310f 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -127,6 +127,10 @@
additional PQC certificates defined with type <code>MLDSA</code> are
added to contexts which use classic certificates. (jfclere/remm)
</update>
+ <add>
+ Optimize the conversion of HTTP method from byte form to String form.
+ (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Web applications">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]