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

asf-gitbox-commits pushed a commit to branch 2.2.X
in repository https://gitbox.apache.org/repos/asf/mina.git

commit 8b12fc781d51c533bd4e36c7bb72de3df6cc975c
Author: Emmanuel Lécharny <[email protected]>
AuthorDate: Tue Sep 8 15:12:37 2026 +0200

    Fixed an issue with HTTP text being decoded as a UTF-8 chars instead of
    byte[].
---
 .../org/apache/mina/http/HttpServerDecoder.java    | 43 ++++++++++++++++++----
 .../apache/mina/http/HttpServerDecoderTest.java    | 30 ++++++++++++++-
 2 files changed, 65 insertions(+), 8 deletions(-)

diff --git 
a/mina-http/src/main/java/org/apache/mina/http/HttpServerDecoder.java 
b/mina-http/src/main/java/org/apache/mina/http/HttpServerDecoder.java
index 3556adbb4..3dbc04ef5 100644
--- a/mina-http/src/main/java/org/apache/mina/http/HttpServerDecoder.java
+++ b/mina-http/src/main/java/org/apache/mina/http/HttpServerDecoder.java
@@ -51,6 +51,9 @@ public class HttpServerDecoder implements ProtocolDecoder {
 
     /** Key for the number of bytes remaining to read for completing the body 
*/
     private static final String BODY_REMAINING_BYTES = "http.brb";
+    
+    /** Regex to parse raw headers and body */
+    public static final byte[] RAW_VALUE_BYTES = { 0x0d, 0x0a, 0x0d, 0x0a };
 
     /** Regex to parse HttpRequest Request Line */
     public static final Pattern REQUEST_LINE_PATTERN = Pattern.compile(" ");
@@ -196,16 +199,16 @@ public class HttpServerDecoder implements ProtocolDecoder 
{
     }
 
     private HttpRequestImpl parseHttpRequestHead(ByteBuffer buffer) {
-        String raw = new String(buffer.array(), buffer.position(), 
buffer.remaining());
-        String[] headersAndBody = RAW_VALUE_PATTERN.split(raw, -1);
-
-        if (headersAndBody.length <= 1) {
+        int foundEndHeaders = ArrayIndexOf(buffer.array(), RAW_VALUE_BYTES, 
buffer.position(), buffer.limit());
+    
+        if (foundEndHeaders < 0) {
             // we didn't receive the full HTTP head
             return null;
         }
 
-        String[] headerFields = HEADERS_BODY_PATTERN.split(headersAndBody[0]);
-        headerFields = ArrayUtil.dropFromEndWhile(headerFields, "");
+        String headers = new String(buffer.array(), buffer.position(), 
foundEndHeaders);
+        
+        String[] headerFields = HEADERS_BODY_PATTERN.split(headers);
 
         String requestLine = headerFields[0];
         Map<String, String> generalHeaders = new HashMap<>();
@@ -228,8 +231,34 @@ public class HttpServerDecoder implements ProtocolDecoder {
         String queryString = pathFrags.length == 2 ? pathFrags[1] : "";
 
         // we put the buffer position where we found the beginning of the HTTP 
body
-        buffer.position(headersAndBody[0].length() + 4);
+        buffer.position(foundEndHeaders + 4);
 
         return new HttpRequestImpl(version, method, requestedPath, 
queryString, generalHeaders);
     }
+
+    /**
+     * Find the index of a byte sequence instead a larger byte sequence
+     */
+    private static int ArrayIndexOf(byte[] haystack, byte[] needle, int 
startIndex, int limit) {
+        if (needle.length == 0) {
+            return 0;
+        }
+
+        for (int i = startIndex; i <= limit - needle.length; i++) {
+            boolean match = true;
+
+            for (int j = 0; j < needle.length; j++) {
+                if (haystack[i + j] != needle[j]) {
+                    match = false;
+                    break;
+                }
+            }
+
+            if (match) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
 }
diff --git 
a/mina-http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java 
b/mina-http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java
index bf802f8ec..8c1426533 100644
--- a/mina-http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java
+++ b/mina-http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertTrue;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.Queue;
 
 import org.apache.mina.core.buffer.IoBuffer;
@@ -35,6 +36,8 @@ import 
org.apache.mina.filter.codec.AbstractProtocolDecoderOutput;
 import org.apache.mina.filter.codec.ProtocolDecoder;
 import org.apache.mina.http.api.HttpEndOfContent;
 import org.apache.mina.http.api.HttpRequest;
+import org.apache.mina.proxy.utils.StringUtilities;
+import org.junit.After;
 import org.junit.Test;
 
 public class HttpServerDecoderTest {
@@ -103,6 +106,14 @@ public class HttpServerDecoderTest {
 
         return out;
     }
+    
+    /**
+     * Be sure to clean the session to get back in NEW state
+     */
+    @After
+    public void shutdown() {
+        session.removeAttribute(DECODER_STATE_ATT);
+    }
 
     @Test
     public void testGetRequestWithoutBody() throws Exception {
@@ -325,6 +336,23 @@ public class HttpServerDecoderTest {
         HttpRequest request = (HttpRequest) out.getQueue().poll();
         assertEquals("localhost", request.getHeader("host"));
         assertTrue(out.getQueue().poll() instanceof HttpEndOfContent);
-        session.removeAttribute(DECODER_STATE_ATT); // This test leaves 
session in HEAD state, crashing following test
+        //session.removeAttribute(DECODER_STATE_ATT); // This test leaves 
session in HEAD state, crashing following test
+    }
+
+    @Test
+    public void multtiByteContentDoesNotBreakFraming() throws Exception {
+        ProtocolDecoderQueue out = new ProtocolDecoderQueue();
+        IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
+        buffer.putString("GET / HTTP/1.0\r\nMyHeader: éééééééé\r\nHost: 
localhost\r\n\r\n", 
+                Charset.forName("UTF-8").newEncoder());
+        buffer.rewind();
+        while (buffer.hasRemaining()) {
+            decoder.decode(session, buffer, out);
+        }
+        assertEquals(2, out.getQueue().size());
+        HttpRequest request = (HttpRequest) out.getQueue().poll();
+        assertEquals("localhost", request.getHeader("host"));
+        assertTrue(out.getQueue().poll() instanceof HttpEndOfContent);
+        //session.removeAttribute(DECODER_STATE_ATT); // This test leaves 
session in HEAD state, crashing following test
     }
 }

Reply via email to