This is an automated email from the ASF dual-hosted git repository. remm pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new d0b2ef9414 Add support for txt: and rnd: map types d0b2ef9414 is described below commit d0b2ef9414a8926a0fcd9a2bd0c64d2e200ce0fb Author: remm <r...@apache.org> AuthorDate: Wed Feb 22 11:48:08 2023 +0100 Add support for txt: and rnd: map types From mod_rewrite. Based on PR#591 provided by Dimitrios Soumis. --- .../valves/rewrite/LocalStrings.properties | 2 + .../valves/rewrite/RandomizedTextRewriteMap.java | 94 +++++++++++++++++++ .../catalina/valves/rewrite/RewriteValve.java | 5 +- .../conf/TesterRewriteMapB.txt | 16 ++-- .../conf/TesterRewriteMapC.txt | 16 ++-- .../catalina/valves/rewrite/TestRewriteValve.java | 104 +++++++++++++++++++++ webapps/docs/changelog.xml | 9 ++ 7 files changed, 229 insertions(+), 17 deletions(-) diff --git a/java/org/apache/catalina/valves/rewrite/LocalStrings.properties b/java/org/apache/catalina/valves/rewrite/LocalStrings.properties index c7541e7ce6..34e6248834 100644 --- a/java/org/apache/catalina/valves/rewrite/LocalStrings.properties +++ b/java/org/apache/catalina/valves/rewrite/LocalStrings.properties @@ -16,6 +16,8 @@ quotedStringTokenizer.tokenizeError=Error tokenizing text [{0}] after position [{1}] from mode [{2}] rewriteMap.tooManyParameters=Too many parameters for this map +rewriteMap.txtInvalidLine=Invalid line [{0}] in text file [{1}] +rewriteMap.txtReadError=Error reading text file [{0}] rewriteValve.closeError=Error closing configuration rewriteValve.invalidFlags=Invalid flag in [{0}] flags [{1}] diff --git a/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java b/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java new file mode 100644 index 0000000000..37a24332df --- /dev/null +++ b/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java @@ -0,0 +1,94 @@ +/* + * 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.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import org.apache.tomcat.util.file.ConfigFileLoader; +import org.apache.tomcat.util.file.ConfigurationSource.Resource; +import org.apache.tomcat.util.res.StringManager; + +/** + * Implement a map for the txt: and rnd: mod_rewrite capabilities. + */ +public class RandomizedTextRewriteMap implements RewriteMap{ + + protected static final StringManager sm = StringManager.getManager(RandomizedTextRewriteMap.class); + + private static final Random random = new Random(); + private final Map<String, String[]> map = new HashMap<>(); + + /** + * Create a map from a text file according to the mod_rewrite syntax. + * @param txtFilePath the text file path + * @param useRandom if the map should produce random results + */ + public RandomizedTextRewriteMap(String txtFilePath, boolean useRandom) { + String line; + try (Resource txtResource = ConfigFileLoader.getSource().getResource(txtFilePath); + BufferedReader reader = new BufferedReader(new InputStreamReader(txtResource.getInputStream()))) { + while ((line = reader.readLine()) != null) { + if (line.startsWith("#") || line.isEmpty()) { + //Ignore comment or empty lines + continue; + } + String[] keyValuePair = line.split(" ", 2); + if (keyValuePair.length > 1) { + String key = keyValuePair[0]; + String value = keyValuePair[1]; + String[] possibleValues = null; + if (useRandom && value.contains("|")) { + possibleValues = value.split("\\|"); + } else { + possibleValues = new String[1]; + possibleValues[0] = value; + } + map.put(key, possibleValues); + } else { + throw new IllegalArgumentException(sm.getString("rewriteMap.txtInvalidLine", line, txtFilePath)); + } + } + } catch (IOException e) { + throw new IllegalArgumentException(sm.getString("rewriteMap.txtReadError", txtFilePath), e); + } + } + + @Override + public String setParameters(String params) { + throw new IllegalArgumentException( + StringManager.getManager(RewriteMap.class).getString("rewriteMap.tooManyParameters")); + } + + @Override + public String lookup(String key) { + String[] possibleValues = map.get(key); + if (possibleValues != null) { + if (possibleValues.length > 1) { + return possibleValues[random.nextInt(possibleValues.length)]; + } else { + return possibleValues[0]; + } + } + return null; + } +} diff --git a/java/org/apache/catalina/valves/rewrite/RewriteValve.java b/java/org/apache/catalina/valves/rewrite/RewriteValve.java index f555e5391d..a5c9228c11 100644 --- a/java/org/apache/catalina/valves/rewrite/RewriteValve.java +++ b/java/org/apache/catalina/valves/rewrite/RewriteValve.java @@ -607,7 +607,6 @@ public class RewriteValve extends ValveBase { return rule; } else if (token.equals("RewriteMap")) { // RewriteMap name rewriteMapClassName whateverOptionalParameterInWhateverFormat - // FIXME: Possibly implement more special maps from https://httpd.apache.org/docs/2.4/rewrite/rewritemap.html if (tokenizer.countTokens() < 2) { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidLine", line)); } @@ -616,6 +615,10 @@ public class RewriteValve extends ValveBase { RewriteMap map = null; if (rewriteMapClassName.startsWith("int:")) { map = InternalRewriteMap.toMap(rewriteMapClassName.substring("int:".length())); + } else if (rewriteMapClassName.startsWith("txt:")) { + map = new RandomizedTextRewriteMap(rewriteMapClassName.substring("txt:".length()), false); + } else if (rewriteMapClassName.startsWith("rnd:")) { + map = new RandomizedTextRewriteMap(rewriteMapClassName.substring("rnd:".length()), true); } else if (rewriteMapClassName.startsWith("prg:")) { rewriteMapClassName = rewriteMapClassName.substring("prg:".length()); } diff --git a/java/org/apache/catalina/valves/rewrite/LocalStrings.properties b/test/conf/TesterRewriteMapB.txt similarity index 64% copy from java/org/apache/catalina/valves/rewrite/LocalStrings.properties copy to test/conf/TesterRewriteMapB.txt index c7541e7ce6..3817ad7753 100644 --- a/java/org/apache/catalina/valves/rewrite/LocalStrings.properties +++ b/test/conf/TesterRewriteMapB.txt @@ -1,3 +1,4 @@ +# ----------------------------------------------------------------------------- # 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. @@ -12,13 +13,12 @@ # 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. +# ----------------------------------------------------------------------------- +## +## map.txt -- rewriting map +## -quotedStringTokenizer.tokenizeError=Error tokenizing text [{0}] after position [{1}] from mode [{2}] +a aa -rewriteMap.tooManyParameters=Too many parameters for this map - -rewriteValve.closeError=Error closing configuration -rewriteValve.invalidFlags=Invalid flag in [{0}] flags [{1}] -rewriteValve.invalidLine=Invalid line [{0}] -rewriteValve.invalidMapClassName=Invalid map class name [{0}] -rewriteValve.readError=Error reading configuration +aa aaaa +b bb diff --git a/java/org/apache/catalina/valves/rewrite/LocalStrings.properties b/test/conf/TesterRewriteMapC.txt similarity index 64% copy from java/org/apache/catalina/valves/rewrite/LocalStrings.properties copy to test/conf/TesterRewriteMapC.txt index c7541e7ce6..85909986f9 100644 --- a/java/org/apache/catalina/valves/rewrite/LocalStrings.properties +++ b/test/conf/TesterRewriteMapC.txt @@ -1,3 +1,4 @@ +# ----------------------------------------------------------------------------- # 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. @@ -12,13 +13,12 @@ # 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. +# ----------------------------------------------------------------------------- +## +## map.txt -- rewriting map +## -quotedStringTokenizer.tokenizeError=Error tokenizing text [{0}] after position [{1}] from mode [{2}] +a aa|aa|aa|aa -rewriteMap.tooManyParameters=Too many parameters for this map - -rewriteValve.closeError=Error closing configuration -rewriteValve.invalidFlags=Invalid flag in [{0}] flags [{1}] -rewriteValve.invalidLine=Invalid line [{0}] -rewriteValve.invalidMapClassName=Invalid map class name [{0}] -rewriteValve.readError=Error reading configuration +b bb|aa|bb|aa +aa aaaa|aaaa diff --git a/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java b/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java index 0c7c7c10ff..c6a1924912 100644 --- a/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java +++ b/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java @@ -130,6 +130,109 @@ public class TestRewriteValve extends TomcatBaseTest { "RewriteRule ^(.*) ${lc:$1}", "/C/AaA", "/c/aaa"); } + @Test + public void testRewriteMap09() throws Exception { + doTestRewrite("RewriteMap lc int:toupper\n" + + "RewriteRule ^(.*) ${lc:$1}", "/w/aAa", "/W/AAA"); + } + + @Test + public void testRewriteMap10() throws Exception { + doTestRewrite("RewriteMap lc int:escape\n" + + "RewriteRule ^(.*) ${lc:$1}", "/c/a%20aa", "/c/a%2520aa"); + } + + @Test + public void testRewriteMap11() throws Exception { + doTestRewrite("RewriteMap lc int:unescape\n" + + "RewriteRule ^(.*) ${lc:$1}", "/c/a%2520aa", "/c/a%20aa"); + } + + @Test + public void testRewriteMap12() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1}", "/b/a.html", "/c/aa"); + } + + @Test + public void testRewriteMap13() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1|dd}", "/b/x.html", "/c/dd"); + } + + // BZ 62667 + @Test + public void testRewriteMap14() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1|d$1d}", "/b/x.html", "/c/dxd"); + } + + @Test + public void testRewriteMap15() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:a$1|dd}", "/b/a.html", "/c/aaaa"); + } + + @Test + public void testRewriteMap16() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt\n" + + "RewriteRule /b/.* /c/${mapb:a}", "/b/a.html", "/c/aa"); + } + + @Test + public void testRewriteMap17() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt\n" + + "RewriteRule /b/.* /c/${mapb:${mapb:a}}", "/b/a.html", "/c/aaaa"); + } + + @Test + public void testRewriteMap18() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt\n" + + "RewriteRule /b/.* /c/${mapb:${mapb:a}}", "/b/a.html", "/c/aaaa"); + } + + @Test(expected = IllegalArgumentException.class) + public void testRewriteMap19() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt first\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1}", "/b/aa.html", "/c/aaaa"); + } + + @Test(expected = IllegalArgumentException.class) + public void testRewriteMap20() throws Exception { + doTestRewrite("RewriteMap mapb txt:../../../test/conf/TesterRewriteMapB.txt first second\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1}", "/b/aa.html", "/c/aaaa"); + } + + @Test + public void testRewriteMap21() throws Exception { + doTestRewrite("RewriteMap mapb rnd:../../../test/conf/TesterRewriteMapC.txt\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1}", "/b/a.html", "/c/aa"); + } + + //This test should succeed 50% of the runs as it depends on a random choice + public void testRewriteMap22() throws Exception { + doTestRewrite("RewriteMap mapb rnd:../../../test/conf/TesterRewriteMapC.txt\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1}", "/b/b.html", "/c/bb"); + } + + @Test + public void testRewriteMap23() throws Exception { + doTestRewrite("RewriteMap mapb rnd:../../../test/conf/TesterRewriteMapC.txt\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1}", "/b/aa.html", "/c/aaaa"); + } + + @Test(expected = IllegalArgumentException.class) + public void testRewriteMap24() throws Exception { + doTestRewrite("RewriteMap mapb rnd:../../../test/conf/TesterRewriteMapC.txt first\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1}", "/b/aa.html", "/c/aaaa"); + } + + @Test(expected = IllegalArgumentException.class) + public void testRewriteMap25() throws Exception { + doTestRewrite("RewriteMap mapb rnd:../../../test/conf/TesterRewriteMapC.txt first second\n" + + "RewriteRule /b/(.*).html$ /c/${mapb:$1}", "/b/aa.html", "/c/aaaa"); + } + @Test public void testRewriteServerVar() throws Exception { doTestRewrite("RewriteRule /b/(.*).html$ /c%{SERVLET_PATH}", "/b/x.html", "/c/b/x.html"); @@ -666,6 +769,7 @@ public class TestRewriteValve extends TomcatBaseTest { Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); ctx.addServletMappingDecoded("/a/%5A", "snoop"); ctx.addServletMappingDecoded("/c/*", "snoop"); + ctx.addServletMappingDecoded("/W/*", "snoop"); Tomcat.addServlet(ctx, "default", new DefaultServlet()); ctx.addServletMappingDecoded("/", "default"); diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 3dd14ddc59..7662464b09 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -105,6 +105,15 @@ issues do not "pop up" wrt. others). --> <section name="Tomcat 9.0.73 (remm)" rtext="in development"> + <subsection name="Catalina"> + <changelog> + <update> + Add support for <code>txt:</code> and <code>rnd:</code> rewrite map + types from mod_rewrite. Based on a pull request <pr>591</pr> + provided by Dimitrios Soumis. (markt) + </update> + </changelog> + </subsection> </section> <section name="Tomcat 9.0.72 (remm)" rtext="release in progress"> <subsection name="Catalina"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org