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 } }
