This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/main by this push:
     new f517f00015 Ignore empty list elements when parsing the Upgrade header 
(#1143)
f517f00015 is described below

commit f517f00015a7d754495819ecd273266439e5699f
Author: Arturo Bernal <[email protected]>
AuthorDate: Thu Nov 12 18:27:41 2026 +0100

    Ignore empty list elements when parsing the Upgrade header (#1143)
    
    RFC 9110, section 5.6.1.2 requires a recipient to parse and ignore empty
    list elements, and the Upgrade header is defined as a #protocol list. A
    value such as "h2c,, websocket" or one with a leading or trailing comma
    was rejected as a whole. Ignore the empty elements.
    
    This also covers the case where the ignored elements are spread across
    separate Upgrade header lines, e.g. "Upgrade: websocket" followed by
    "Upgrade: ,,". A header line that reduces to nothing once its empty
    elements are skipped no longer forces the whole parse to fail; it simply
    contributes no protocol. Only when every header line does this, or there
    are no Upgrade headers at all, does the existing contract apply: no
    headers present yields an empty list, while one or more headers present
    that contain no protocol is still invalid and yields null.
---
 .../apache/tomcat/util/http/parser/Upgrade.java    | 22 ++++++++++++++++++++++
 .../tomcat/util/http/parser/TestUpgrade.java       | 19 +++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/java/org/apache/tomcat/util/http/parser/Upgrade.java 
b/java/org/apache/tomcat/util/http/parser/Upgrade.java
index 15d5dee633..65ea343b28 100644
--- a/java/org/apache/tomcat/util/http/parser/Upgrade.java
+++ b/java/org/apache/tomcat/util/http/parser/Upgrade.java
@@ -90,8 +90,10 @@ public class Upgrade {
     public static List<Upgrade> parse(Enumeration<String> headerValues) {
         try {
             List<Upgrade> result = new ArrayList<>();
+            boolean sawHeader = false;
 
             while (headerValues.hasMoreElements()) {
+                sawHeader = true;
                 String headerValue = headerValues.nextElement();
                 if (headerValue == null) {
                     // Invalid
@@ -101,6 +103,17 @@ public class Upgrade {
                 Reader r = new StringReader(headerValue);
                 SkipResult skipComma;
                 do {
+                    // Empty list elements must be ignored (RFC 9110, section 
5.6.1.2)
+                    SkipResult skipEmpty = HttpParser.skipConstant(r, ",");
+                    while (skipEmpty == SkipResult.FOUND) {
+                        skipEmpty = HttpParser.skipConstant(r, ",");
+                    }
+                    if (skipEmpty == SkipResult.EOF) {
+                        // Nothing left in this header value once any empty
+                        // list elements are skipped. This header contributes
+                        // no protocols, whether or not earlier headers did.
+                        break;
+                    }
                     // Skip any leading whitespace
                     HttpParser.skipWhitespace(r);
                     String protocolName = HttpParser.readToken(r);
@@ -129,6 +142,15 @@ public class Upgrade {
                 } while (skipComma == SkipResult.FOUND);
             }
 
+            if (sawHeader && result.isEmpty()) {
+                // An Upgrade header must name at least one protocol (RFC
+                // 9110, section 7.8). One or more headers that reduce to an
+                // empty list once empty list elements are discarded is
+                // invalid. No Upgrade header at all is not an error; it
+                // yields the empty list below, as before this change.
+                return null;
+            }
+
             return result;
         } catch (IOException ioe) {
             // Should never happen with Strings
diff --git a/test/org/apache/tomcat/util/http/parser/TestUpgrade.java 
b/test/org/apache/tomcat/util/http/parser/TestUpgrade.java
index 3910b21a21..33dc24defd 100644
--- a/test/org/apache/tomcat/util/http/parser/TestUpgrade.java
+++ b/test/org/apache/tomcat/util/http/parser/TestUpgrade.java
@@ -31,6 +31,9 @@ import org.junit.runners.Parameterized.Parameter;
 @RunWith(Parameterized.class)
 public class TestUpgrade {
 
+    private static final Integer ZERO = Integer.valueOf(0);
+    private static final Integer ONE = Integer.valueOf(1);
+    private static final Integer TWO = Integer.valueOf(2);
     private static final Integer FOUR = Integer.valueOf(4);
 
     @Parameterized.Parameters(name = "{index}: headers[{0}]")
@@ -57,9 +60,25 @@ public class TestUpgrade {
         // Make final version a non-token
         parameterSets.add(new Object[] { Arrays.asList(new String[] { 
"HTTP/2.0", "SHTTP/1.3", "IRC/6.9", "RTA/x}11" }), null} );
 
+        // Empty list elements must be ignored (RFC 9110, section 5.6.1.2)
+        parameterSets.add(new Object[] { Arrays.asList(new String[] { 
"HTTP/2.0,, SHTTP/1.3" }), TWO } );
+        parameterSets.add(new Object[] { Arrays.asList(new String[] { 
"HTTP/2.0, , SHTTP/1.3" }), TWO } );
+        parameterSets.add(new Object[] { Arrays.asList(new String[] { ", 
HTTP/2.0, SHTTP/1.3" }), TWO } );
+        parameterSets.add(new Object[] { Arrays.asList(new String[] { 
"HTTP/2.0, SHTTP/1.3," }), TWO } );
+        parameterSets.add(new Object[] { Arrays.asList(new String[] { 
"HTTP/2.0, SHTTP/1.3, , " }), TWO } );
+
+        // A separate Upgrade header that reduces to an empty list once empty
+        // list elements are discarded must not invalidate a header that
+        // names a real protocol (RFC 9110, section 5.6.1.2)
+        parameterSets.add(new Object[] { Arrays.asList(new String[] { 
"websocket", ",," }), ONE } );
+        parameterSets.add(new Object[] { Arrays.asList(new String[] { ",,", 
"websocket" }), ONE } );
+
         // Empty header isn't valid
         parameterSets.add(new Object[] { Arrays.asList(new String[] { "" }), 
null } );
 
+        // No Upgrade header at all is not an error; it is simply not present
+        parameterSets.add(new Object[] { Collections.<String>emptyList(), ZERO 
} );
+
         // Nulls shouldn't happen but check they are treated as invalid to be 
safe
         parameterSets.add(new Object[] { Arrays.asList(new String[] { null }), 
null } );
 


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

Reply via email to