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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]