Skip to content

Commit fb1a647

Browse files
committed
Fix regression in handling of chain rules
Test cases written using Claude / Opus 4.8
1 parent 8f6e70c commit fb1a647

2 files changed

Lines changed: 34 additions & 8 deletions

File tree

java/org/apache/catalina/valves/rewrite/RewriteValve.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -535,15 +535,13 @@ public void invoke(Request request, Response response) throws IOException, Servl
535535

536536
// - chain (skip remaining chained rules if this one does not match)
537537
if (rule.isChain() && newtest == null) {
538-
int skip = 0;
539-
for (int j = i + 1; j < rules.length; j++) {
540-
if (!rules[j].isChain()) {
541-
break;
542-
} else {
543-
skip++;
544-
}
538+
// Skip the remaining rules in the chain, including the
539+
// terminal rule that does not have the chain flag set.
540+
int j = i + 1;
541+
while (j < rules.length && rules[j].isChain()) {
542+
j++;
545543
}
546-
i += skip;
544+
i = j;
547545
continue;
548546
}
549547
// - last (stop rewriting here)

test/org/apache/catalina/valves/rewrite/TestRewriteValve.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,34 @@ public void testNonNormalizedPathRewrite() throws Exception {
8888
doTestRewrite("RewriteRule ^/b/(.*) /b/../a/$1", "/b/%255A", "/b/../a/%255A");
8989
}
9090

91+
@Test
92+
public void testChainMatch() throws Exception {
93+
// The first rule matches so the chained (second) rule is applied to the result
94+
doTestRewrite("RewriteRule ^/a(.*) /b$1 [C]\n" +
95+
"RewriteRule ^/b(.*) /c$1", "/a/x", "/c/x");
96+
}
97+
98+
@Test
99+
public void testChainHeadNoMatch() throws Exception {
100+
// The first rule does not match so the whole chain, including the
101+
// terminal rule that would otherwise match, must be skipped. If the
102+
// terminal rule were incorrectly applied the request would be rewritten
103+
// to /W/x.
104+
doTestRewrite("RewriteRule ^/never(.*) /c$1 [C]\n" +
105+
"RewriteRule ^/c(.*) /W$1", "/c/x", "/c/x");
106+
}
107+
108+
@Test
109+
public void testChainMiddleNoMatch() throws Exception {
110+
// The first rule matches but the second (chained) rule does not, so the
111+
// rest of the chain, including the terminal rule, must be skipped and
112+
// the result of the first rule is retained. If the terminal rule were
113+
// incorrectly applied the request would be rewritten to /W/y.
114+
doTestRewrite("RewriteRule ^/a(.*) /c$1 [C]\n" +
115+
"RewriteRule ^/nomatch(.*) /x$1 [C]\n" +
116+
"RewriteRule ^/c(.*) /W$1", "/a/y", "/c/y");
117+
}
118+
91119
// BZ 57863
92120
@Test
93121
public void testRewriteMap01() throws Exception {

0 commit comments

Comments
 (0)