This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit f715152469f8fd5678439d38068a7eb59f23c0cb Author: opencode <[email protected]> AuthorDate: Wed Oct 14 19:15:09 2026 +0200 Replace obs-fold with a single SP when parsing headers The header parser's line folding (obs-fold) handling peeked the first byte of the continuation line and copied that raw byte into the compacted header value. Since an obs-fold may start with HTAB, a TAB could end up in the value, e.g. a header sent as "X: abcd CRLF TAB efgh" was stored as "abcd\tefgh". RFC 9112 section 5.2 requires a server that receives an obs-fold to either reject the message or replace each obs-fold with one or more SP octets. The parser now always inserts a single space at the fold point instead of copying the peeked byte. Whitespace beyond the first byte of the continuation line was and is skipped by the value-start state, so the stored value for the HTAB case changes from a TAB to a space while the SP case is unchanged. This also removes a divergence between what Tomcat sees and what a spec-compliant intermediary would forward for the same message. The existing test for folded headers (bz 48839) asserted the legacy behaviour with a tab in the value and was updated to expect the single space mandated by the specification. A new test pins the SP continuation case. --- .../tomcat/util/http/parser/HttpHeaderParser.java | 7 ++- .../coyote/http11/TestHttp11InputBuffer.java | 69 +++++++++++++++++++++- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java b/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java index 2e7be33c3e..0eb653909b 100644 --- a/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java +++ b/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java @@ -255,9 +255,10 @@ public class HttpHeaderParser { headerParsePos = HeaderParsePosition.HEADER_START; break; } else { - // Copying one extra space in the buffer (since there must - // be at least one space inserted between the lines) - source.getHeaderByteBuffer().put(headerData.realPos, peek); + // Replace the line folding with a single space as required + // by RFC 9112 section 5.2. Any further whitespace at the + // start of the continuation line is skipped below. + source.getHeaderByteBuffer().put(headerData.realPos, SP); headerData.realPos++; headerParsePos = HeaderParsePosition.HEADER_VALUE_START; } diff --git a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java index a5b896e862..1f1b2ba2f7 100644 --- a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java +++ b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java @@ -103,7 +103,9 @@ public class TestHttp11InputBuffer extends TomcatBaseTest { if (getResponseBody() == null) { return false; } - if (!getResponseBody().contains("abcd\tefgh")) { + // The line folding (CRLF + HTAB) is replaced with a single SP as + // required by RFC 9110 section 5.2 + if (!getResponseBody().contains("abcd efgh")) { return false; } return true; @@ -133,6 +135,71 @@ public class TestHttp11InputBuffer extends TomcatBaseTest { } + /** + * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=48839 with a space rather than a tab starting the + * continuation line. Both fold forms must result in a single space in the parsed value (RFC 9112 section 5.2). + */ + @Test + public void testObsFoldWithSpaceContinuation() { + + ObsFoldClient client = new ObsFoldClient(); + + client.doRequest(); + Assert.assertTrue(client.isResponse200()); + Assert.assertTrue(client.getResponseBody(), client.getResponseBody().contains("abcd efgh")); + } + + + /** + * Obs-fold test client. Uses a SP continuation line. + */ + private class ObsFoldClient extends SimpleHttpClient { + + private Exception doRequest() { + + Tomcat tomcat = getTomcatInstance(); + + Context root = tomcat.addContext("", TEMP_DIR); + Tomcat.addServlet(root, "ObsFold", new Bug48839Servlet()); + root.addServletMapping("/test", "ObsFold"); + + try { + tomcat.start(); + setPort(tomcat.getConnector().getLocalPort()); + + // Open connection + connect(); + + String[] request = new String[1]; + // @formatter:off + request[0] = + "GET http://localhost:8080/test HTTP/1.1" + CRLF + + "Host: localhost:8080" + CRLF + + "X-Bug48839: abcd" + CRLF + + " efgh" + CRLF + + "Connection: close" + CRLF + + CRLF; + // @formatter:on + + setRequest(request); + processRequest(); // blocks until response has been read + + // Close the connection + disconnect(); + } catch (Exception e) { + return e; + } + return null; + } + + @Override + public boolean isResponseBodyOK() { + return getResponseBody() != null && getResponseBody().contains("abcd efgh"); + } + + } + + @Test public void testBug51557Valid() { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
