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 4e5ffde4f7 Stricter URI template matching for WebSocket
4e5ffde4f7 is described below

commit 4e5ffde4f7f4241eab679f5293bbd9dcc476f29f
Author: Mark Thomas <[email protected]>
AuthorDate: Mon Jul 27 16:27:34 2026 +0100

    Stricter URI template matching for WebSocket
---
 .../websocket/server/LocalStrings.properties       |  1 -
 .../websocket/server/LocalStrings_fr.properties    |  1 -
 .../websocket/server/LocalStrings_ja.properties    |  1 -
 .../websocket/server/LocalStrings_ko.properties    |  1 -
 .../websocket/server/LocalStrings_zh_CN.properties |  1 -
 .../tomcat/websocket/server/UriTemplate.java       | 13 ++--
 .../tomcat/websocket/server/TestUriTemplate.java   | 78 ++++++++++------------
 webapps/docs/changelog.xml                         |  7 ++
 8 files changed, 48 insertions(+), 55 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/server/LocalStrings.properties 
b/java/org/apache/tomcat/websocket/server/LocalStrings.properties
index c9deb14149..cf400a7711 100644
--- a/java/org/apache/tomcat/websocket/server/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/server/LocalStrings.properties
@@ -24,7 +24,6 @@ upgradeUtil.incompatibleRsv=Extensions were specified that 
have incompatible RSV
 upgradeUtil.pojoMapFail=Unable to complete method mapping for POJO class [{0}]
 
 uriTemplate.duplicateParameter=The parameter [{0}] appears more than once in 
the path which is not permitted
-uriTemplate.emptySegment=The path [{0}] contains one or more empty segments 
which is not permitted
 uriTemplate.invalidPath=The path [{0}] is not valid.
 uriTemplate.invalidSegment=The segment [{0}] is not valid in the provided path 
[{1}]
 
diff --git a/java/org/apache/tomcat/websocket/server/LocalStrings_fr.properties 
b/java/org/apache/tomcat/websocket/server/LocalStrings_fr.properties
index b963760cbc..8d2acb71b3 100644
--- a/java/org/apache/tomcat/websocket/server/LocalStrings_fr.properties
+++ b/java/org/apache/tomcat/websocket/server/LocalStrings_fr.properties
@@ -27,7 +27,6 @@ upgradeUtil.incompatibleRsv=Des extensions qui ont été 
spécifiées ont une ut
 upgradeUtil.pojoMapFail=Impossible de compléter le mappage des méthodes pour 
la classe POJO [{0}]
 
 uriTemplate.duplicateParameter=Le paramètre [{0}] apparaît plus d''une fois 
dans le chemin ce qui n''est pas permis
-uriTemplate.emptySegment=Le chemin [{0}] contient un ou plusieurs segments 
vide ce qui n''est pas autorisé
 uriTemplate.invalidPath=Le chemin [{0}] est invalide
 uriTemplate.invalidSegment=Le segment [{0}] est invalide pour le chemin fourni 
[{1}]
 
diff --git a/java/org/apache/tomcat/websocket/server/LocalStrings_ja.properties 
b/java/org/apache/tomcat/websocket/server/LocalStrings_ja.properties
index f63b542ab9..9ea5ad4b41 100644
--- a/java/org/apache/tomcat/websocket/server/LocalStrings_ja.properties
+++ b/java/org/apache/tomcat/websocket/server/LocalStrings_ja.properties
@@ -27,7 +27,6 @@ upgradeUtil.incompatibleRsv=互換性のないRSVビットの使用法を持つ
 upgradeUtil.pojoMapFail=POJOクラス [{0}] のメソッドマッピングを完了できません
 
 uriTemplate.duplicateParameter=パス内にパラメーター [{0}] を複数回登場させることはできません
-uriTemplate.emptySegment=パス [{0}] に一つ以上の空セグメントを含めることはできません。
 uriTemplate.invalidPath=[{0}] は無効なパスです。
 uriTemplate.invalidSegment=パス [{1}] に存在しないセグメント [{0}] が指定されました。
 
diff --git a/java/org/apache/tomcat/websocket/server/LocalStrings_ko.properties 
b/java/org/apache/tomcat/websocket/server/LocalStrings_ko.properties
index 6bf78c110c..bc7f809b20 100644
--- a/java/org/apache/tomcat/websocket/server/LocalStrings_ko.properties
+++ b/java/org/apache/tomcat/websocket/server/LocalStrings_ko.properties
@@ -27,7 +27,6 @@ upgradeUtil.incompatibleRsv=호환되지 않는 RSV 비트를 사용하여, Exte
 upgradeUtil.pojoMapFail=POJO 클래스 [{0}](을)를 위한 메소드 매핑을 완료할 수 없습니다.
 
 uriTemplate.duplicateParameter=허용되지 않는 경로에서, 파라미터 [{0}]이(가) 두번 이상 나타나고 있습니다.
-uriTemplate.emptySegment=경로 [{0}]이(가), 하나 이상의 허용되지 않는 empty segment들을 포함하고 
있습니다.
 uriTemplate.invalidPath=경로 [{0}](은)는 유효하지 않습니다.
 uriTemplate.invalidSegment=세그먼트 [{0}]은(는) 제공된 경로 [{1}] 내에 유효하지 않습니다.
 
diff --git 
a/java/org/apache/tomcat/websocket/server/LocalStrings_zh_CN.properties 
b/java/org/apache/tomcat/websocket/server/LocalStrings_zh_CN.properties
index 4c5c372eaf..090b3348dc 100644
--- a/java/org/apache/tomcat/websocket/server/LocalStrings_zh_CN.properties
+++ b/java/org/apache/tomcat/websocket/server/LocalStrings_zh_CN.properties
@@ -27,7 +27,6 @@ upgradeUtil.incompatibleRsv=指定扩展名具有不兼容的RSV位使用
 upgradeUtil.pojoMapFail=无法完成 POJO 类 [{0}] 的方法映射
 
 uriTemplate.duplicateParameter=参数[{0}]在不允许的路径中出现多次
-uriTemplate.emptySegment=路径[{0}]包含一个或多个不允许的空段
 uriTemplate.invalidPath=路径 [{0}] 无效。
 uriTemplate.invalidSegment=段[{0}]在提供的路径[{1}]中无效
 
diff --git a/java/org/apache/tomcat/websocket/server/UriTemplate.java 
b/java/org/apache/tomcat/websocket/server/UriTemplate.java
index 682c8f2893..7f46e6c948 100644
--- a/java/org/apache/tomcat/websocket/server/UriTemplate.java
+++ b/java/org/apache/tomcat/websocket/server/UriTemplate.java
@@ -65,16 +65,11 @@ public class UriTemplate {
         for (int i = 0; i < segments.length; i++) {
             String segment = segments[i];
             if (segment.isEmpty()) {
-                if (i == 0 || (i == segments.length - 1 && paramCount == 0)) {
-                    // Ignore the first empty segment as the path must always
-                    // start with '/'
-                    // Ending with a '/' is also OK for instances used for
-                    // matches but not for parameterised templates.
+                if (i == 0) {
+                    // Ignore the first empty segment as the path must always 
start with '/'.
                     continue;
-                } else {
-                    // As per EG discussion, all other empty segments are
-                    // invalid
-                    throw new 
DeploymentException(sm.getString("uriTemplate.emptySegment", path));
+                } else if (i == segments.length - 1) {
+                    // Ending with a '/' is allowed.
                 }
             }
             normalized.append('/');
diff --git a/test/org/apache/tomcat/websocket/server/TestUriTemplate.java 
b/test/org/apache/tomcat/websocket/server/TestUriTemplate.java
index b2071be3d3..6cc795be93 100644
--- a/test/org/apache/tomcat/websocket/server/TestUriTemplate.java
+++ b/test/org/apache/tomcat/websocket/server/TestUriTemplate.java
@@ -90,8 +90,8 @@ public class TestUriTemplate {
 
     @Test
     public void testSpecExample1_01() throws Exception {
-        UriTemplate t = new UriTemplate("/a/b");
-        Map<String, String> result = t.match(new UriTemplate("/a/b"));
+        UriTemplate t = new UriTemplate("/a/b/");
+        Map<String, String> result = t.match(new UriTemplate("/a/b/"));
 
         Assert.assertEquals(0, result.size());
     }
@@ -99,8 +99,8 @@ public class TestUriTemplate {
 
     @Test
     public void testSpecExample1_02() throws Exception {
-        UriTemplate t = new UriTemplate("/a/b");
-        Map<String, String> result = t.match(new UriTemplate("/a"));
+        UriTemplate t = new UriTemplate("/a/b/");
+        Map<String, String> result = t.match(new UriTemplate("/a/b"));
 
         Assert.assertNull(result);
     }
@@ -108,7 +108,16 @@ public class TestUriTemplate {
 
     @Test
     public void testSpecExample1_03() throws Exception {
-        UriTemplate t = new UriTemplate("/a/b");
+        UriTemplate t = new UriTemplate("/a/b/");
+        Map<String, String> result = t.match(new UriTemplate("/a"));
+
+        Assert.assertNull(result);
+    }
+
+
+    @Test
+    public void testSpecExample1_04() throws Exception {
+        UriTemplate t = new UriTemplate("/a/b/");
         Map<String, String> result = t.match(new UriTemplate("/a/bb"));
 
         Assert.assertNull(result);
@@ -146,6 +155,15 @@ public class TestUriTemplate {
 
     @Test
     public void testSpecExample2_04() throws Exception {
+        UriTemplate t = new UriTemplate("/a/{var}");
+        Map<String, String> result = t.match(new UriTemplate("/a/b/"));
+
+        Assert.assertNull(result);
+    }
+
+
+    @Test
+    public void testSpecExample2_05() throws Exception {
         UriTemplate t = new UriTemplate("/a/{var}");
         Map<String, String> result = t.match(new UriTemplate("/a/b/c"));
 
@@ -153,6 +171,17 @@ public class TestUriTemplate {
     }
 
 
+    @Test
+    public void testSpecExample2_06() throws Exception {
+        // Extra test based on RFC 6570, 2.3
+        UriTemplate t = new UriTemplate("/a/{var}");
+        Map<String, String> result = t.match(new UriTemplate("/a/"));
+
+        Assert.assertEquals(1, result.size());
+        Assert.assertEquals("", result.get("var"));
+    }
+
+
     @Test(expected = jakarta.websocket.DeploymentException.class)
     public void testDuplicate01() throws Exception {
         @SuppressWarnings("unused")
@@ -170,45 +199,12 @@ public class TestUriTemplate {
         Assert.assertEquals("x", result.get("b"));
     }
 
-
-    public void testEgMailingList01() throws Exception {
-        UriTemplate t = new UriTemplate("/a/{var}");
-        Map<String, String> result = t.match(new UriTemplate("/a/b/"));
-
-        Assert.assertNull(result);
-    }
-
-
-    public void testEgMailingList02() throws Exception {
-        UriTemplate t = new UriTemplate("/a/{var}");
-        Map<String, String> result = t.match(new UriTemplate("/a/"));
-
-        Assert.assertNull(result);
-    }
-
-
     @Test
-    public void testEgMailingList03() throws Exception {
-        UriTemplate t = new UriTemplate("/a/{var}");
-        Map<String, String> result = t.match(new UriTemplate("/a"));
-
-        Assert.assertNull(result);
-    }
-
-
-    @Test(expected = jakarta.websocket.DeploymentException.class)
-    public void testEgMailingList04() throws Exception {
-        UriTemplate t = new UriTemplate("/a/{var1}/{var2}");
-        @SuppressWarnings("unused")
-        Map<String, String> result = t.match(new UriTemplate("/a//c"));
-    }
-
-
-    @Test(expected = jakarta.websocket.DeploymentException.class)
-    public void testEgMailingList05() throws Exception {
+    public void testEgMailingList01() throws Exception {
         UriTemplate t = new UriTemplate("/a/{var}/");
-        @SuppressWarnings("unused")
         Map<String, String> result = t.match(new UriTemplate("/a/b/"));
+        Assert.assertEquals(1, result.size());
+        Assert.assertEquals("b", result.get("var"));
     }
 
 
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 59ef41b3ef..2a366927bd 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -431,6 +431,13 @@
         <code>org.apache.tomcat.websocket.MAX_HTTP_RESPONSE_HEADER_BYTES</code>
         user property. (markt)
       </add>
+      <fix>
+        Improve URI template matching for WebSocket end points. Trailing 
slashes
+        are now significant both for template definitions and URIs considered
+        for potential matches to those URIs. Note that this means if a URI
+        template ends in a variable without a trailing slash, that variable
+        might be expanded to the empty string. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Web applications">


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

Reply via email to