This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/10.1.x by this push:
     new 77308f9eda Optimize conversion of method from bytes to String
77308f9eda is described below

commit 77308f9eda1d77a491967ddad0e532d3781aa442
Author: Mark Thomas <ma...@apache.org>
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       |   8 +-
 .../apache/coyote/http11/Http11InputBuffer.java    |   3 +-
 java/org/apache/coyote/http11/Http11Processor.java |   5 +-
 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, 313 insertions(+), 24 deletions(-)

diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java 
b/java/org/apache/catalina/authenticator/FormAuthenticator.java
index 1c953bd242..ab3b4a3e62 100644
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -448,7 +448,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 {
@@ -464,7 +464,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);
         }
     }
 
@@ -632,7 +632,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 e48238aae3..d2c71b4d97 100644
--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
+++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -595,7 +595,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
@@ -614,7 +614,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
@@ -813,7 +813,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 669486a317..7b21d78048 100644
--- a/java/org/apache/catalina/connector/OutputBuffer.java
+++ b/java/org/apache/catalina/connector/OutputBuffer.java
@@ -238,8 +238,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 f3debeb779..118984c5fa 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -2163,7 +2163,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 80dde650e1..12c31a1bbb 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.B2CConverter;
 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;
@@ -317,10 +318,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 50587c6a7e..88a6aacfb6 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -639,7 +639,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());
@@ -826,7 +826,9 @@ public class AjpProcessor extends AbstractProcessor {
                     break;
 
                 case Constants.SC_A_STORED_METHOD:
-                    requestHeaderMessage.getBytes(request.method());
+                    requestHeaderMessage.getBytes(tmpMB);
+                    ByteChunk tmpBC = tmpMB.getByteChunk();
+                    request.setMethod(tmpBC.getBytes(), tmpBC.getStart(), 
tmpBC.getLength());
                     break;
 
                 case Constants.SC_A_SECRET:
@@ -924,7 +926,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 832ddc520b..22b1af5f6b 100644
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -394,8 +394,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 4f52d834d6..07ce3cecb4 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -503,7 +503,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());
@@ -902,8 +902,7 @@ public class Http11Processor extends AbstractProcessor {
             }
         }
 
-        MessageBytes methodMB = request.method();
-        boolean head = methodMB.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 0ed39115c8..51afc13d1a 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -368,8 +368,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();
                     }
@@ -552,8 +552,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 d92aaffe51..cad90b64db 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -522,8 +522,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 3df1637cfd..a90bfbf55a 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -133,6 +133,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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to