diff --git a/java/org/apache/coyote/http11/InternalAprInputBuffer.java b/java/org/apache/coyote/http11/InternalAprInputBuffer.java
index 6f153a9d6859..4e6a81aeac14 100644
--- a/java/org/apache/coyote/http11/InternalAprInputBuffer.java
+++ b/java/org/apache/coyote/http11/InternalAprInputBuffer.java
@@ -484,7 +484,7 @@ private boolean parseHeader() throws IOException {
headers.removeHeader(headers.size() - 1);
skipLine(lineStart, start);
return true;
- } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
+ } else if (HttpParser.isControl(chr) && chr != Constants.HT) {
// Invalid value
// Delete the header (it will be the most recent one)
headers.removeHeader(headers.size() - 1);
diff --git a/java/org/apache/coyote/http11/InternalInputBuffer.java b/java/org/apache/coyote/http11/InternalInputBuffer.java
index c305d3f17f28..2572e7d8dc33 100644
--- a/java/org/apache/coyote/http11/InternalInputBuffer.java
+++ b/java/org/apache/coyote/http11/InternalInputBuffer.java
@@ -435,7 +435,7 @@ private boolean parseHeader() throws IOException {
// Invalid value - also need to delete header
skipLine(lineStart, start, true);
return true;
- } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
+ } else if (HttpParser.isControl(chr) && chr != Constants.HT) {
// Invalid value - also need to delete header
skipLine(lineStart, start, true);
return true;
diff --git a/java/org/apache/coyote/http11/InternalNioInputBuffer.java b/java/org/apache/coyote/http11/InternalNioInputBuffer.java
index a0e087790ec9..3fc5c888f6d6 100644
--- a/java/org/apache/coyote/http11/InternalNioInputBuffer.java
+++ b/java/org/apache/coyote/http11/InternalNioInputBuffer.java
@@ -670,7 +670,7 @@ private HeaderParseStatus parseHeader()
} else if (prevChr == Constants.CR) {
// Invalid value - also need to delete header
return skipLine(true);
- } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
+ } else if (HttpParser.isControl(chr) && chr != Constants.HT) {
// Invalid value - also need to delete header
return skipLine(true);
} else if (chr == Constants.SP || chr == Constants.HT) {
diff --git a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
index 05e76802c1d2..7f140bc30cd8 100644
--- a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -31,6 +31,7 @@
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.http.parser.HttpParser;
/**
* Chunked input filter. Parses chunked data according to
@@ -500,6 +501,9 @@ private boolean parseHeader() throws IOException {
if (chr == Constants.COLON) {
colon = true;
+ } else if (!HttpParser.isToken(chr)) {
+ // Non-token characters are illegal in header names
+ throw new IOException(sm.getString("chunkedInputFilter.invalidTrailerHeaderName"));
} else {
trailingHeaders.append(chr);
}
@@ -561,7 +565,9 @@ private boolean parseHeader() throws IOException {
if (chr == Constants.CR || chr == Constants.LF) {
parseCRLF(true);
eol = true;
- } else if (chr == Constants.SP) {
+ } else if (HttpParser.isControl(chr) && chr != Constants.HT) {
+ throw new IOException(sm.getString("chunkedInputFilter.invalidTrailerHeaderValue"));
+ } else if (chr == Constants.SP || chr == Constants.HT) {
trailingHeaders.append(chr);
} else {
trailingHeaders.append(chr);
diff --git a/java/org/apache/coyote/http11/filters/LocalStrings.properties b/java/org/apache/coyote/http11/filters/LocalStrings.properties
index c0dc9d1b10f7..65fdbf248d38 100644
--- a/java/org/apache/coyote/http11/filters/LocalStrings.properties
+++ b/java/org/apache/coyote/http11/filters/LocalStrings.properties
@@ -23,5 +23,6 @@ chunkedInputFilter.invalidCrlfNoData=Invalid end of line sequence (no data avail
chunkedInputFilter.invalidHeader=Invalid chunk header
chunkedInputFilter.maxExtension=maxExtensionSize exceeded
chunkedInputFilter.maxTrailer=maxTrailerSize exceeded
-
+chunkedInputFilter.invalidTrailerHeaderName=Invalid trailer header name (non-token character in name)
+chunkedInputFilter.invalidTrailerHeaderValue=Invalid trailer header value (control character in value)
inputFilter.maxSwallow=maxSwallowSize exceeded
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 6c4d150c2ec2..7d374ad28bd8 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -154,6 +154,9 @@
Include the problematic data in the error message when reporting that
the provided request line contains an invalid component. (markt)
+
+ Align validation of HTTP trailer fields with standard fields. (markt)
+