This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 4a2611f18421a22f502f8eac88ae4395aa1957b9 Author: opencode <[email protected]> AuthorDate: Mon Oct 12 14:43:25 2026 +0200 Accept any whitespace as key/value separator in txt and rnd rewrite maps RandomizedTextRewriteMap parsed mod_rewrite map files with a hard coded single space as the separator between key and value. Tab separated lines were rejected with an IllegalArgumentException at start up, lines with several whitespace characters silently kept the extra whitespace at the start of the value, and comment or empty lines indented with whitespace were mis-parsed, the former failing start up and the latter creating a garbage entry under an empty key. Trim each line and treat any run of whitespace as the separator, as mod_rewrite documents and as the httpd parser implements it. The value may still contain whitespace, as before. Add a unit test class covering the separator variants, comment handling, random pipe separated values and the invalid key-only line. --- .../valves/rewrite/RandomizedTextRewriteMap.java | 5 +- .../rewrite/TestRandomizedTextRewriteMap.java | 126 +++++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java b/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java index b90181625d..d809a77f9a 100644 --- a/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java +++ b/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java @@ -52,6 +52,7 @@ public class RandomizedTextRewriteMap implements RewriteMap { try (Resource txtResource = ConfigFileLoader.getSource().getResource(txtFilePath); BufferedReader reader = new BufferedReader(new InputStreamReader(txtResource.getInputStream()))) { while ((line = reader.readLine()) != null) { + line = line.trim(); if (line.startsWith("#") || line.isEmpty()) { // Ignore comment line or empty lines continue; @@ -59,7 +60,9 @@ public class RandomizedTextRewriteMap implements RewriteMap { // Ignore comment characters after '#' line = line.substring(0, line.indexOf('#')).trim(); } - String[] keyValuePair = line.split(" ", 2); + // Like mod_rewrite, any whitespace separates the key from the + // value. The value may itself contain whitespace. + String[] keyValuePair = line.split("\\s+", 2); if (keyValuePair.length > 1) { String key = keyValuePair[0]; String value = keyValuePair[1]; diff --git a/test/org/apache/catalina/valves/rewrite/TestRandomizedTextRewriteMap.java b/test/org/apache/catalina/valves/rewrite/TestRandomizedTextRewriteMap.java new file mode 100644 index 0000000000..68aa20b1e1 --- /dev/null +++ b/test/org/apache/catalina/valves/rewrite/TestRandomizedTextRewriteMap.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.valves.rewrite; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +public class TestRandomizedTextRewriteMap { + + private String createMapFile(String content) throws IOException { + File file = File.createTempFile("rewrite-map", ".txt"); + file.deleteOnExit(); + Files.writeString(file.toPath(), content, StandardCharsets.UTF_8); + return file.getAbsolutePath(); + } + + @Test + public void testSingleSpaceSeparation() throws Exception { + RandomizedTextRewriteMap map = + new RandomizedTextRewriteMap(createMapFile("key value\n"), false); + Assert.assertEquals("value", map.lookup("key")); + } + + @Test + public void testTabSeparation() throws Exception { + // mod_rewrite separates key and value by any whitespace + RandomizedTextRewriteMap map = + new RandomizedTextRewriteMap(createMapFile("key\tvalue\n"), false); + Assert.assertEquals("value", map.lookup("key")); + } + + @Test + public void testMultipleWhitespaceSeparation() throws Exception { + // A single space was required before, which silently added the extra + // whitespace to the start of the value + RandomizedTextRewriteMap map = + new RandomizedTextRewriteMap(createMapFile("key \tvalue\n"), false); + Assert.assertEquals("value", map.lookup("key")); + } + + @Test + public void testWhitespaceAroundEntry() throws Exception { + RandomizedTextRewriteMap map = new RandomizedTextRewriteMap( + createMapFile(" key value \n"), false); + Assert.assertEquals("value", map.lookup("key")); + } + + @Test + public void testIndentedCommentLine() throws Exception { + // An indentation before the comment marker used to make the line + // look like an entry without a value and fail the map start up + RandomizedTextRewriteMap map = new RandomizedTextRewriteMap( + createMapFile(" # comment\nkey value\n"), false); + Assert.assertEquals("value", map.lookup("key")); + } + + @Test + public void testCommentAfterValue() throws Exception { + RandomizedTextRewriteMap map = new RandomizedTextRewriteMap( + createMapFile("key value # comment\n"), false); + Assert.assertEquals("value", map.lookup("key")); + } + + @Test + public void testValueMayContainWhitespace() throws Exception { + RandomizedTextRewriteMap map = new RandomizedTextRewriteMap( + createMapFile("key one two\n"), false); + Assert.assertEquals("one two", map.lookup("key")); + } + + @Test + public void testRandomValues() throws Exception { + RandomizedTextRewriteMap map = new RandomizedTextRewriteMap( + createMapFile("key one|two\n"), true); + Set<String> possible = new HashSet<>(); + Set<String> seen = new HashSet<>(); + possible.add("one"); + possible.add("two"); + for (int i = 0; i < 100; i++) { + String value = map.lookup("key"); + Assert.assertTrue(possible.contains(value)); + seen.add(value); + } + // Both values must show up. The probability of one of the two values + // never being picked in 100 draws is below 10^-30. + Assert.assertEquals(possible, seen); + } + + @Test + public void testUnknownKeyResolvesToNull() throws Exception { + RandomizedTextRewriteMap map = + new RandomizedTextRewriteMap(createMapFile("key value\n"), false); + Assert.assertNull(map.lookup("other")); + } + + @Test + public void testLineWithoutValueThrows() throws Exception { + try { + new RandomizedTextRewriteMap(createMapFile("key\n"), false); + Assert.fail("IAE expected."); + } catch (IllegalArgumentException e) { + // expected, an entry without a value is invalid + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
