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) &ndash; 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 &quot;. 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

Reply via email to