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 02ea88cd50 Align trailer header name filtering across HTTP versions
02ea88cd50 is described below

commit 02ea88cd5004102606a8707b7b9ba15aad02b2ba
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Apr 8 09:45:39 2026 +0100

    Align trailer header name filtering across HTTP versions
---
 .../coyote/http11/filters/ChunkedOutputFilter.java | 26 ++-----------------
 java/org/apache/coyote/http2/Stream.java           | 22 ++++++++++------
 java/org/apache/tomcat/util/http/HeaderUtil.java   | 30 ++++++++++++++++++++++
 webapps/docs/changelog.xml                         |  4 +++
 4 files changed, 50 insertions(+), 32 deletions(-)

diff --git a/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java 
b/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
index 33a9741187..e03c8cf225 100644
--- a/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
+++ b/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
@@ -21,16 +21,14 @@ import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
-import java.util.HashSet;
-import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.Supplier;
 
 import org.apache.coyote.Response;
 import org.apache.coyote.http11.HttpOutputBuffer;
 import org.apache.coyote.http11.OutputFilter;
 import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.http.HeaderUtil;
 
 /**
  * Chunked output filter.
@@ -41,26 +39,6 @@ public class ChunkedOutputFilter implements OutputFilter {
     private static final byte[] CRLF_BYTES = { (byte) '\r', (byte) '\n' };
     private static final byte[] END_CHUNK_BYTES = { (byte) '0', (byte) '\r', 
(byte) '\n', (byte) '\r', (byte) '\n' };
 
-    private static final Set<String> disallowedTrailerFieldNames = new 
HashSet<>();
-
-    static {
-        // Always add these in lower case
-        disallowedTrailerFieldNames.add("age");
-        disallowedTrailerFieldNames.add("cache-control");
-        disallowedTrailerFieldNames.add("content-length");
-        disallowedTrailerFieldNames.add("content-encoding");
-        disallowedTrailerFieldNames.add("content-range");
-        disallowedTrailerFieldNames.add("content-type");
-        disallowedTrailerFieldNames.add("date");
-        disallowedTrailerFieldNames.add("expires");
-        disallowedTrailerFieldNames.add("location");
-        disallowedTrailerFieldNames.add("retry-after");
-        disallowedTrailerFieldNames.add("trailer");
-        disallowedTrailerFieldNames.add("transfer-encoding");
-        disallowedTrailerFieldNames.add("vary");
-        disallowedTrailerFieldNames.add("warning");
-    }
-
     /**
      * Next buffer in the pipeline.
      */
@@ -178,7 +156,7 @@ public class ChunkedOutputFilter implements OutputFilter {
             try (OutputStreamWriter osw = new OutputStreamWriter(baos, 
StandardCharsets.ISO_8859_1)) {
                 for (Map.Entry<String,String> trailerField : 
trailerFields.entrySet()) {
                     // Ignore disallowed headers
-                    if 
(disallowedTrailerFieldNames.contains(trailerField.getKey().toLowerCase(Locale.ENGLISH)))
 {
+                    if 
(HeaderUtil.isHeaderDisallowedInTrailers(trailerField.getKey())) {
                         continue;
                     }
                     osw.write(filterForHeaders(trailerField.getKey()));
diff --git a/java/org/apache/coyote/http2/Stream.java 
b/java/org/apache/coyote/http2/Stream.java
index f44f4721e7..b6c92edbc8 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -49,6 +49,7 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.buf.ByteChunk;
 import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HeaderUtil;
 import org.apache.tomcat.util.http.Method;
 import org.apache.tomcat.util.http.MimeHeaders;
 import org.apache.tomcat.util.http.parser.Host;
@@ -623,8 +624,8 @@ class Stream extends AbstractNonZeroStream implements 
HeaderEmitter {
 
 
     final void writeTrailers() throws IOException {
-        Supplier<Map<String,String>> supplier = 
coyoteResponse.getTrailerFields();
-        if (supplier == null) {
+        Supplier<Map<String,String>> trailerFieldsSupplier = 
coyoteResponse.getTrailerFields();
+        if (trailerFieldsSupplier == null) {
             // No supplier was set, end of stream will already have been sent
             return;
         }
@@ -635,17 +636,22 @@ class Stream extends AbstractNonZeroStream implements 
HeaderEmitter {
          */
         MimeHeaders mimeHeaders = new MimeHeaders();
 
-        Map<String,String> headerMap = supplier.get();
-        if (headerMap == null) {
-            headerMap = Collections.emptyMap();
+        Map<String,String> trailerFields = trailerFieldsSupplier.get();
+        if (trailerFields == null) {
+            trailerFields = Collections.emptyMap();
         }
 
         // Copy the contents of the Map to the MimeHeaders
         // TODO: Is there benefit in refactoring this? Is MimeHeaders too
         // heavyweight? Can we reduce the copy/conversions?
-        for (Map.Entry<String,String> headerEntry : headerMap.entrySet()) {
-            MessageBytes mb = mimeHeaders.addValue(headerEntry.getKey());
-            mb.setString(headerEntry.getValue());
+        for (Map.Entry<String,String> trailerField : trailerFields.entrySet()) 
{
+            // Ignore disallowed headers
+            if 
(HeaderUtil.isHeaderDisallowedInTrailers(trailerField.getKey())) {
+                continue;
+            }
+
+            MessageBytes mb = mimeHeaders.addValue(trailerField.getKey());
+            mb.setString(trailerField.getValue());
         }
 
         handler.writeHeaders(this, 0, mimeHeaders, true, 
Constants.DEFAULT_HEADERS_FRAME_SIZE);
diff --git a/java/org/apache/tomcat/util/http/HeaderUtil.java 
b/java/org/apache/tomcat/util/http/HeaderUtil.java
index 34d52cc261..15f139efd6 100644
--- a/java/org/apache/tomcat/util/http/HeaderUtil.java
+++ b/java/org/apache/tomcat/util/http/HeaderUtil.java
@@ -16,8 +16,38 @@
  */
 package org.apache.tomcat.util.http;
 
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
 public class HeaderUtil {
 
+    private static final Set<String> disallowedTrailerFieldNames = new 
HashSet<>();
+
+    static {
+        // Always add these in lower case
+        disallowedTrailerFieldNames.add("age");
+        disallowedTrailerFieldNames.add("cache-control");
+        disallowedTrailerFieldNames.add("content-length");
+        disallowedTrailerFieldNames.add("content-encoding");
+        disallowedTrailerFieldNames.add("content-range");
+        disallowedTrailerFieldNames.add("content-type");
+        disallowedTrailerFieldNames.add("date");
+        disallowedTrailerFieldNames.add("expires");
+        disallowedTrailerFieldNames.add("location");
+        disallowedTrailerFieldNames.add("retry-after");
+        disallowedTrailerFieldNames.add("trailer");
+        disallowedTrailerFieldNames.add("transfer-encoding");
+        disallowedTrailerFieldNames.add("vary");
+        disallowedTrailerFieldNames.add("warning");
+    }
+
+
+    public static boolean isHeaderDisallowedInTrailers(String headerName) {
+        return 
disallowedTrailerFieldNames.contains(headerName.toLowerCase(Locale.ENGLISH));
+    }
+
+
     /**
      * Converts an HTTP header line in byte form to a printable String. Bytes 
corresponding to visible ASCII characters
      * will be converted to those characters. All other bytes (0x00 to 0x1F, 
0x7F to 0xFF) will be represented in 0xNN
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 4c1c0e44ea..a96360c01f 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -151,6 +151,10 @@
         Refactor clean-up after HTTP/2 headers have been processed to aid GC
         after a stream reset. (markt)
       </fix>
+      <fix>
+        Align HTTP/2 trailer fields with HTTP/1.1 and filter out any fields
+        not permitted in trailers. (markt)
+      </fix>
     </changelog>
   </subsection>
 </section>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to