This is an automated email from the ASF dual-hosted git repository. fschumacher pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/master by this push: new b978b2c Allow more quoted tokens for RewriteValve config b978b2c is described below commit b978b2c83ffe1be0670f47ccab0cbfaebdaba694 Author: Felix Schumacher <fschumac...@apache.org> AuthorDate: Fri Nov 1 16:52:52 2019 +0100 Allow more quoted tokens for RewriteValve config Along with quoted token parsing, RewriteMaps can have more than one parameter now. Bugzilla Id: 64067 Part of #221 --- .../valves/rewrite/QuotedStringTokenizer.java | 135 +++++++++++++++++++++ .../apache/catalina/valves/rewrite/RewriteMap.java | 18 +++ .../catalina/valves/rewrite/RewriteValve.java | 2 +- .../valves/rewrite/TestQuotedStringTokenizer.java | 71 +++++++++++ webapps/docs/changelog.xml | 4 + webapps/docs/rewrite.xml | 6 +- 6 files changed, 234 insertions(+), 2 deletions(-) diff --git a/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java b/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java new file mode 100644 index 0000000..101dd96 --- /dev/null +++ b/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java @@ -0,0 +1,135 @@ +/* + * 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.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +public class QuotedStringTokenizer { + + private Iterator<String> tokenIterator; + private int tokenCount; + private int returnedTokens = 0; + + enum WordMode { + SPACES, QUOTED, ESCAPED, SIMPLE, COMMENT + } + + public QuotedStringTokenizer(String text) { + List<String> tokens; + if (text != null) { + tokens = tokenizeText(text); + } else { + tokens = Collections.emptyList(); + } + this.tokenCount = tokens.size(); + this.tokenIterator = tokens.iterator(); + } + + private List<String> tokenizeText(String inputText) { + List<String> tokens = new ArrayList<>(); + int pos = 0; + int length = inputText.length(); + WordMode currentMode = WordMode.SPACES; + StringBuilder currentToken = new StringBuilder(); + while (pos < length) { + char currentChar = inputText.charAt(pos); + switch (currentMode) { + case SPACES: + currentMode = handleSpaces(currentToken, currentChar); + break; + case QUOTED: + currentMode = handleQuoted(tokens, currentToken, currentChar); + break; + case ESCAPED: + currentToken.append(currentChar); + currentMode = WordMode.QUOTED; + break; + case SIMPLE: + currentMode = handleSimple(tokens, currentToken, currentChar); + break; + case COMMENT: + if (currentChar == '\r' || currentChar == '\n') { + currentMode = WordMode.SPACES; + } + break; + default: + throw new IllegalStateException( + "Couldn't tokenize text " + inputText + " after position " + pos + "from mode " + currentMode); + } + pos++; + } + String possibleLastToken = currentToken.toString(); + if (!possibleLastToken.isEmpty()) { + tokens.add(possibleLastToken); + } + return tokens; + } + + private WordMode handleSimple(List<String> tokens, StringBuilder currentToken, char currentChar) { + if (Character.isWhitespace(currentChar)) { + tokens.add(currentToken.toString()); + currentToken.setLength(0); + return WordMode.SPACES; + } else { + currentToken.append(currentChar); + } + return WordMode.SIMPLE; + } + + private WordMode handleQuoted(List<String> tokens, StringBuilder currentToken, char currentChar) { + if (currentChar == '"') { + tokens.add(currentToken.toString()); + currentToken.setLength(0); + return WordMode.SPACES; + } else if (currentChar == '\\') { + return WordMode.ESCAPED; + } else { + currentToken.append(currentChar); + } + return WordMode.QUOTED; + } + + private WordMode handleSpaces(StringBuilder currentToken, char currentChar) { + if (!Character.isWhitespace(currentChar)) { + if (currentChar == '"') { + return WordMode.QUOTED; + } else if (currentChar == '#') { + return WordMode.COMMENT; + } else { + currentToken.append(currentChar); + return WordMode.SIMPLE; + } + } + return WordMode.SPACES; + } + + public boolean hasMoreTokens() { + return tokenIterator.hasNext(); + } + + public String nextToken() { + returnedTokens++; + return tokenIterator.next(); + } + + public int countTokens() { + return tokenCount - returnedTokens; + } +} diff --git a/java/org/apache/catalina/valves/rewrite/RewriteMap.java b/java/org/apache/catalina/valves/rewrite/RewriteMap.java index a18a5e7..43315e7 100644 --- a/java/org/apache/catalina/valves/rewrite/RewriteMap.java +++ b/java/org/apache/catalina/valves/rewrite/RewriteMap.java @@ -44,6 +44,24 @@ public interface RewriteMap { public String setParameters(String params); /** + * Optional parameters that can be defined through the {@code RewriteMap} + * directive in the {@code rewrite.config} file. + * <p> + * This method will be called, if there are more than one parameters defined. + * + * @param params the optional parameters + */ + default void setParameters(String... params) { + if (params == null) { + return; + } + if (params.length > 1) { + throw new IllegalArgumentException("Too many parameters for this map"); + } + setParameters(params[0]); + } + + /** * Maps a key to a replacement value.<br> * The method is free to return {@code null} to indicate, that the default * value from the {@code RewriteRule} directive should be used. diff --git a/java/org/apache/catalina/valves/rewrite/RewriteValve.java b/java/org/apache/catalina/valves/rewrite/RewriteValve.java index 68f0bc5..a86d1fd 100644 --- a/java/org/apache/catalina/valves/rewrite/RewriteValve.java +++ b/java/org/apache/catalina/valves/rewrite/RewriteValve.java @@ -572,7 +572,7 @@ public class RewriteValve extends ValveBase { * @return The condition, rule or map resulting from parsing the line */ public static Object parse(String line) { - StringTokenizer tokenizer = new StringTokenizer(line); + QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(line); if (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.equals("RewriteCond")) { diff --git a/test/org/apache/catalina/valves/rewrite/TestQuotedStringTokenizer.java b/test/org/apache/catalina/valves/rewrite/TestQuotedStringTokenizer.java new file mode 100644 index 0000000..59d63fb --- /dev/null +++ b/test/org/apache/catalina/valves/rewrite/TestQuotedStringTokenizer.java @@ -0,0 +1,71 @@ +/* + * 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 static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class TestQuotedStringTokenizer { + + private String inputText; + private List<String> tokens; + + @Parameters(name = "{index}: tokenize({0}) = {1}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { { null, Collections.emptyList() }, { "", Collections.emptyList() }, + { " \t\r\n", Collections.emptyList() }, { "simple", Arrays.asList("simple") }, + { "more than one word", Arrays.asList("more", "than", "one", "word") }, + { "\"quoted text\"", Arrays.asList("quoted text") }, + { " mixed \t\"words with\\\"\" escapes", Arrays.asList("mixed", "words with\"", "escapes") }, + { "# comment", Collections.emptyList() }, + { "Something # and then a comment", Arrays.asList("Something") }, + { "\"Quoted with a #\" which is not a comment", + Arrays.asList("Quoted with a #", "which", "is", "not", "a", "comment") } }); + } + + public TestQuotedStringTokenizer(String inputText, List<String> tokens) { + this.inputText = inputText; + this.tokens = tokens; + } + + @Test + public void testTokenize() { + QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(inputText); + List<String> result = new ArrayList<>(); + int count = tokens.size(); + while (tokenizer.hasMoreTokens()) { + assertThat(tokenizer.countTokens(), is(count)); + result.add(tokenizer.nextToken()); + count--; + } + assertThat(tokenizer.countTokens(), is(0)); + assertThat(tokens, is(result)); + } + +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 33d7e34..6e400de 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -96,6 +96,10 @@ <bug>58577</bug>: Respect the argument-count when searching for MBean operations to invoke via the JMXProxyServlet. (schultz) </fix> + <update> + <bug>64067</bug>: Allow more than one parameter when defining RewriteMaps. + (fschumacher) + </update> </changelog> </subsection> <subsection name="Coyote"> diff --git a/webapps/docs/rewrite.xml b/webapps/docs/rewrite.xml index 82b0b77..4869b92 100644 --- a/webapps/docs/rewrite.xml +++ b/webapps/docs/rewrite.xml @@ -401,6 +401,7 @@ RewriteRule ^/$ /homepage.std.html [L]</source> <source><![CDATA[package org.apache.catalina.valves.rewrite; public interface RewriteMap { + default String setParameters(String params...); // calls setParameters(String) with the first parameter if there is only one public String setParameters(String params); public String lookup(String key); }]]></source> @@ -410,7 +411,10 @@ public interface RewriteMap { (be careful with whitespace) – by calling <code>setParameters(String)</code>. That instance will then be registered under the name given as the first paramter of <code>RewriteMap</code> rule.</p> - <p>That instance will be given the the lookup value that is configured in the corresponding <code>RewriteRule</code> by + <p>Note: Starting with Tomcat 9 you can use more than one parameter. These have to be separated by spaces. Parameters + can be quoted with ". This enables space characters inside parameters.</p> + + <p>That map instance will be given the the lookup value that is configured in the corresponding <code>RewriteRule</code> by calling <code>lookup(String)</code>. Your implementation is free to return <code>null</code> to indicate, that the given default should be used, or to return a replacement value.</p> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org