This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push:
new 770a91e559 Align trailer header name filtering across HTTP versions
770a91e559 is described below
commit 770a91e559d974eff1addaf62a8ee7a3a985fb49
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 85cbbaf32d..82fa7035b9 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -46,6 +46,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;
@@ -616,8 +617,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;
}
@@ -628,17 +629,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, 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 0361e23f6d..0f968cac55 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]