Author: markt
Date: Fri Sep 9 13:44:46 2016
New Revision: 1760022
URL: http://svn.apache.org/viewvc?rev=1760022&view=rev
Log:
Additional review of the fix for bug 60013
After review of the httpd behaviour, update the Rewrite valve to better
align with httpd and modify the existing tests where they do not reflect
current httpd behaviour.
Add additional tests to cover various combinations of R, B, NE and QSA
flags along with UTF-8 values in original URIs, re-written URIs,
original query strings and re-written query strings.
Modified:
tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteRule.java
tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteValve.java
tomcat/trunk/java/org/apache/catalina/valves/rewrite/Substitution.java
tomcat/trunk/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
Modified: tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteRule.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteRule.java?rev=1760022&r1=1760021&r2=1760022&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteRule.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteRule.java Fri
Sep 9 13:44:46 2016
@@ -38,7 +38,6 @@ public class RewriteRule {
substitution.setSub(substitutionString);
substitution.parse(maps);
substitution.setEscapeBackReferences(isEscapeBackReferences());
- substitution.setNoEscape(isNoescape());
}
// Parse the pattern
int flags = 0;
Modified: tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteValve.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteValve.java?rev=1760022&r1=1760021&r2=1760022&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteValve.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteValve.java Fri
Sep 9 13:44:46 2016
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
+import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Hashtable;
@@ -44,15 +45,54 @@ import org.apache.catalina.Pipeline;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.URLEncoder;
import org.apache.catalina.valves.ValveBase;
import org.apache.tomcat.util.buf.CharChunk;
import org.apache.tomcat.util.buf.MessageBytes;
-import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.buf.UriUtil;
import org.apache.tomcat.util.http.RequestUtil;
public class RewriteValve extends ValveBase {
+ static URLEncoder ENCODER = new URLEncoder();
+ static {
+ /*
+ * Replicates httpd's encoding
+ * Primarily aimed at encoding URI paths, so from the spec:
+ *
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+ *
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ *
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ * / "*" / "+" / "," / ";" / "="
+ */
+ // ALPHA and DIGIT are always treated as safe characters
+ // Add the remaining unreserved characters
+ ENCODER.addSafeCharacter('-');
+ ENCODER.addSafeCharacter('.');
+ ENCODER.addSafeCharacter('_');
+ ENCODER.addSafeCharacter('~');
+ // Add the sub-delims
+ ENCODER.addSafeCharacter('!');
+ ENCODER.addSafeCharacter('$');
+ ENCODER.addSafeCharacter('&');
+ ENCODER.addSafeCharacter('\'');
+ ENCODER.addSafeCharacter('(');
+ ENCODER.addSafeCharacter(')');
+ ENCODER.addSafeCharacter('*');
+ ENCODER.addSafeCharacter('+');
+ ENCODER.addSafeCharacter(',');
+ ENCODER.addSafeCharacter(';');
+ ENCODER.addSafeCharacter('=');
+ // Add the remaining literals
+ ENCODER.addSafeCharacter(':');
+ ENCODER.addSafeCharacter('@');
+ // Add '/' so it isn't encoded when we encode a path
+ ENCODER.addSafeCharacter('/');
+ }
+
+
/**
* The rewrite rules that the valve will use.
*/
@@ -285,13 +325,13 @@ public class RewriteValve extends ValveB
// converted to a string
MessageBytes urlMB = context ? request.getRequestPathMB() :
request.getDecodedRequestURIMB();
urlMB.toChars();
- CharSequence url = urlMB.getCharChunk();
+ CharSequence urlDecoded = urlMB.getCharChunk();
CharSequence host = request.getServerName();
boolean rewritten = false;
boolean done = false;
for (int i = 0; i < rules.length; i++) {
RewriteRule rule = rules[i];
- CharSequence test = (rule.isHost()) ? host : url;
+ CharSequence test = (rule.isHost()) ? host : urlDecoded;
CharSequence newtest = rule.evaluate(test, resolver);
if (newtest != null && !test.equals(newtest.toString())) {
if (container.getLogger().isDebugEnabled()) {
@@ -301,7 +341,7 @@ public class RewriteValve extends ValveB
if (rule.isHost()) {
host = newtest;
} else {
- url = newtest;
+ urlDecoded = newtest;
}
rewritten = true;
}
@@ -323,35 +363,55 @@ public class RewriteValve extends ValveB
// - redirect (code)
if (rule.isRedirect() && newtest != null) {
// append the query string to the url if there is one and
it hasn't been rewritten
- String queryString = request.getQueryString();
- StringBuffer urlString = new StringBuffer(url);
- if (queryString != null && queryString.length() > 0) {
- int index = urlString.indexOf("?");
- if (index != -1) {
- // if qsa is specified append the query
+ String originalQueryStringEncoded =
request.getQueryString();
+ String urlStringDecoded = urlDecoded.toString();
+ int index = urlStringDecoded.indexOf("?");
+ String rewrittenQueryStringDecoded;
+ if (index == -1) {
+ rewrittenQueryStringDecoded = null;
+ } else {
+ rewrittenQueryStringDecoded =
urlStringDecoded.substring(index + 1);
+ urlStringDecoded = urlStringDecoded.substring(0,
index);
+ }
+
+ StringBuffer urlStringEncoded = new
StringBuffer(ENCODER.encode(urlStringDecoded,
request.getConnector().getURIEncoding()));
+ if (originalQueryStringEncoded != null &&
originalQueryStringEncoded.length() > 0) {
+ if (rewrittenQueryStringDecoded == null) {
+ urlStringEncoded.append('?');
+
urlStringEncoded.append(originalQueryStringEncoded);
+ } else {
if (rule.isQsappend()) {
- urlString.append('&');
- urlString.append(queryString);
- }
- // if the ? is the last character delete it, its
only purpose was to
- // prevent the rewrite module from appending the
query string
- else if (index == urlString.length() - 1) {
- urlString.deleteCharAt(index);
+ // if qsa is specified append the query
+ urlStringEncoded.append('?');
+
urlStringEncoded.append(ENCODER.encode(rewrittenQueryStringDecoded,
request.getConnector().getURIEncoding()));
+ urlStringEncoded.append('&');
+
urlStringEncoded.append(originalQueryStringEncoded);
+ } else if (index == urlStringEncoded.length() - 1)
{
+ // if the ? is the last character delete it,
its only purpose was to
+ // prevent the rewrite module from appending
the query string
+ urlStringEncoded.deleteCharAt(index);
+ } else {
+ urlStringEncoded.append('?');
+
urlStringEncoded.append(ENCODER.encode(rewrittenQueryStringDecoded,
request.getConnector().getURIEncoding()));
}
- } else {
- urlString.append('?');
- urlString.append(queryString);
}
+ } else if (rewrittenQueryStringDecoded != null) {
+ urlStringEncoded.append('?');
+
urlStringEncoded.append(ENCODER.encode(rewrittenQueryStringDecoded,
request.getConnector().getURIEncoding()));
}
// Insert the context if
// 1. this valve is associated with a context
// 2. the url starts with a leading slash
// 3. the url isn't absolute
- if (context && urlString.charAt(0) == '/' &&
!UriUtil.hasScheme(urlString)) {
- urlString.insert(0,
request.getContext().getEncodedPath());
+ if (context && urlStringEncoded.charAt(0) == '/' &&
!UriUtil.hasScheme(urlStringEncoded)) {
+ urlStringEncoded.insert(0,
request.getContext().getEncodedPath());
+ }
+ if (rule.isNoescape()) {
+
response.sendRedirect(URLDecoder.decode(urlStringEncoded.toString(),
request.getConnector().getURIEncoding()));
+ } else {
+ response.sendRedirect(urlStringEncoded.toString());
}
- response.sendRedirect(urlString.toString());
response.setStatus(rule.getRedirectCode());
done = true;
break;
@@ -384,9 +444,9 @@ public class RewriteValve extends ValveB
// - qsappend
if (rule.isQsappend() && newtest != null) {
String queryString = request.getQueryString();
- String urlString = url.toString();
+ String urlString = urlDecoded.toString();
if (urlString.indexOf('?') != -1 && queryString != null) {
- url = urlString + "&" + queryString;
+ urlDecoded = urlString + "&" + queryString;
}
}
@@ -421,42 +481,46 @@ public class RewriteValve extends ValveB
if (rewritten) {
if (!done) {
// See if we need to replace the query string
- String urlString = url.toString();
- String queryString = null;
- int queryIndex = urlString.indexOf('?');
+ String urlStringDecoded = urlDecoded.toString();
+ String queryStringDecoded = null;
+ int queryIndex = urlStringDecoded.indexOf('?');
if (queryIndex != -1) {
- queryString = urlString.substring(queryIndex+1);
- urlString = urlString.substring(0, queryIndex);
+ queryStringDecoded =
urlStringDecoded.substring(queryIndex+1);
+ urlStringDecoded = urlStringDecoded.substring(0,
queryIndex);
}
- // Set the new 'original' URI
+ // Save the current context path before re-writing starts
String contextPath = null;
if (context) {
contextPath = request.getContextPath();
}
+ // Populated the encoded (i.e. undecoded) requestURI
request.getCoyoteRequest().requestURI().setString(null);
CharChunk chunk =
request.getCoyoteRequest().requestURI().getCharChunk();
chunk.recycle();
if (context) {
+ // This is neither decoded nor normalized
chunk.append(contextPath);
}
- chunk.append(urlString);
+ chunk.append(ENCODER.encode(urlStringDecoded,
request.getConnector().getURIEncoding()));
request.getCoyoteRequest().requestURI().toChars();
// Decoded and normalized URI
+ // Rewriting may have denormalized the URL
+ urlStringDecoded = RequestUtil.normalize(urlStringDecoded);
request.getCoyoteRequest().decodedURI().setString(null);
chunk =
request.getCoyoteRequest().decodedURI().getCharChunk();
chunk.recycle();
if (context) {
- chunk.append(contextPath);
+ // This is decoded and normalized
+
chunk.append(request.getServletContext().getContextPath());
}
- chunk.append(RequestUtil.normalize(UDecoder.URLDecode(
- urlString,
request.getConnector().getURIEncoding())));
+ chunk.append(urlStringDecoded);
request.getCoyoteRequest().decodedURI().toChars();
// Set the new Query if there is one
- if (queryString != null) {
+ if (queryStringDecoded != null) {
request.getCoyoteRequest().queryString().setString(null);
chunk =
request.getCoyoteRequest().queryString().getCharChunk();
chunk.recycle();
- chunk.append(queryString);
+ chunk.append(ENCODER.encode(queryStringDecoded,
request.getConnector().getURIEncoding()));
request.getCoyoteRequest().queryString().toChars();
}
// Set the new host if it changed
Modified: tomcat/trunk/java/org/apache/catalina/valves/rewrite/Substitution.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/rewrite/Substitution.java?rev=1760022&r1=1760021&r2=1760022&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/valves/rewrite/Substitution.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/valves/rewrite/Substitution.java Fri
Sep 9 13:44:46 2016
@@ -20,25 +20,8 @@ import java.util.ArrayList;
import java.util.Map;
import java.util.regex.Matcher;
-import org.apache.catalina.util.URLEncoder;
-
public class Substitution {
- private static URLEncoder STATIC_ENCODER = new URLEncoder();
- static {
- // Defaults
- STATIC_ENCODER.addSafeCharacter('~');
- STATIC_ENCODER.addSafeCharacter('-');
- STATIC_ENCODER.addSafeCharacter('_');
- STATIC_ENCODER.addSafeCharacter('.');
- STATIC_ENCODER.addSafeCharacter('*');
- STATIC_ENCODER.addSafeCharacter('/');
- // httpd doesn't encode these either
- STATIC_ENCODER.addSafeCharacter('?');
- STATIC_ENCODER.addSafeCharacter('=');
- }
-
-
public abstract class SubstitutionElement {
public abstract String evaluate(Matcher rule, Matcher cond, Resolver
resolver);
}
@@ -48,11 +31,7 @@ public class Substitution {
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
- if (noEscape) {
- return value;
- } else {
- return STATIC_ENCODER.encode(value, resolver.getUriEncoding());
- }
+ return value;
}
}
@@ -66,7 +45,7 @@ public class Substitution {
// We might want to consider providing a dedicated
decoder
// with an option to add additional safe characters to
// provide users with more flexibility
- return URLEncoder.DEFAULT.encode(rule.group(n),
resolver.getUriEncoding());
+ return RewriteValve.ENCODER.encode(rule.group(n),
resolver.getUriEncoding());
} else {
return rule.group(n);
}
@@ -139,11 +118,6 @@ public class Substitution {
this.escapeBackReferences = escapeBackReferences;
}
- private boolean noEscape;
- void setNoEscape(boolean noEscape) {
- this.noEscape = noEscape;
- }
-
public void parse(Map<String, RewriteMap> maps) {
ArrayList<SubstitutionElement> elements = new ArrayList<>();
Modified:
tomcat/trunk/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java?rev=1760022&r1=1760021&r2=1760022&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
(original)
+++ tomcat/trunk/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
Fri Sep 9 13:44:46 2016
@@ -41,17 +41,17 @@ public class TestRewriteValve extends To
@Test
public void testNoopRewrite() throws Exception {
- doTestRewrite("RewriteRule ^(.*) $1 [B]", "/a/%255A", "/a/%255A");
+ doTestRewrite("RewriteRule ^(.*) $1", "/a/%255A", "/a/%255A");
}
@Test
public void testPathRewrite() throws Exception {
- doTestRewrite("RewriteRule ^/b(.*) /a$1 [B]", "/b/%255A", "/a/%255A");
+ doTestRewrite("RewriteRule ^/b(.*) /a$1", "/b/%255A", "/a/%255A");
}
@Test
public void testNonNormalizedPathRewrite() throws Exception {
- doTestRewrite("RewriteRule ^/b/(.*) /b/../a/$1 [B]", "/b/%255A",
"/b/../a/%255A");
+ doTestRewrite("RewriteRule ^/b/(.*) /b/../a/$1", "/b/%255A",
"/b/../a/%255A");
}
// BZ 57863
@@ -75,13 +75,15 @@ public class TestRewriteValve extends To
@Test
public void testRewriteEnvVarAndServerVar() throws Exception {
System.setProperty("some_variable", "something");
- doTestRewrite("RewriteRule /b/(.*).html$
/c/%{ENV:some_variable}%{SERVLET_PATH}", "/b/x.html", "/c/something/b/x.html");
+ doTestRewrite("RewriteRule /b/(.*).html$
/c/%{ENV:some_variable}%{SERVLET_PATH}",
+ "/b/x.html", "/c/something/b/x.html");
}
@Test
public void testRewriteServerVarAndEnvVar() throws Exception {
System.setProperty("some_variable", "something");
- doTestRewrite("RewriteRule /b/(.*).html$
/c%{SERVLET_PATH}/%{ENV:some_variable}", "/b/x.html", "/c/b/x.html/something");
+ doTestRewrite("RewriteRule /b/(.*).html$
/c%{SERVLET_PATH}/%{ENV:some_variable}",
+ "/b/x.html", "/c/b/x.html/something");
}
@Test
@@ -90,7 +92,7 @@ public class TestRewriteValve extends To
doTestRewrite("RewriteRule /b/(.*).html$ /c%_{SERVLET_PATH}",
"/b/x.html", "/c");
Assert.fail("IAE expected.");
} catch (java.lang.IllegalArgumentException e) {
- // excpected as %_{ is invalid
+ // expected as %_{ is invalid
}
}
@@ -100,7 +102,7 @@ public class TestRewriteValve extends To
doTestRewrite("RewriteRule /b/(.*).html$ /c$_{SERVLET_PATH}",
"/b/x.html", "/c");
Assert.fail("IAE expected.");
} catch (java.lang.IllegalArgumentException e) {
- // excpected as $_{ is invalid
+ // expected as $_{ is invalid
}
}
@@ -110,19 +112,15 @@ public class TestRewriteValve extends To
"/b/%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95", "/c/",
"param=\u5728\u7EBF\u6D4B\u8BD5");
}
- private void doTestRewrite(String config, String request, String
expectedURI) throws Exception {
- doTestRewrite(config, request, expectedURI, null);
- }
-
@Test
public void testNonAsciiPath() throws Exception {
- doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [B]",
"/b/%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95",
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1",
"/b/%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95",
"/c/%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95");
}
@Test
public void testNonAsciiPathRedirect() throws Exception {
- doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [R,B]",
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [R]",
"/b/%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95",
"/c/%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95");
}
@@ -134,7 +132,7 @@ public class TestRewriteValve extends To
@Test
public void testNonAsciiQueryString() throws Exception {
- doTestRewrite("RewriteRule ^/b/id=(.*) /c?id=$1 [B]",
+ doTestRewrite("RewriteRule ^/b/(.*) /c?$1",
"/b/id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95",
"/c", "id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95");
}
@@ -142,28 +140,304 @@ public class TestRewriteValve extends To
@Test
public void testNonAsciiQueryStringAndPath() throws Exception {
- doTestRewrite("RewriteRule ^/b/(.*)/id=(.*) /c/$1?id=$2 [B]",
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/$1?$2",
"/b/%E5%9C%A8%E7%BA%BF/id=%E6%B5%8B%E8%AF%95",
"/c/%E5%9C%A8%E7%BA%BF", "id=%E6%B5%8B%E8%AF%95");
}
@Test
- public void testNonAsciiQueryStringRedirect() throws Exception {
- doTestRewrite("RewriteRule ^/b/id=(.*) /c?id=$1 [R,B]",
+ public void testNonAsciiQueryStringAndRedirect() throws Exception {
+ doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [R]",
"/b/id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95",
"/c", "id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95");
}
@Test
- public void testNonAsciiQueryStringAndRedirectPath() throws Exception {
- doTestRewrite("RewriteRule ^/b/(.*)/id=(.*) /c/$1?id=$2 [R,B]",
+ public void testNonAsciiQueryStringAndPathAndRedirect() throws Exception {
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/$1?$2 [R]",
"/b/%E5%9C%A8%E7%BA%BF/id=%E6%B5%8B%E8%AF%95",
"/c/%E5%9C%A8%E7%BA%BF", "id=%E6%B5%8B%E8%AF%95");
}
+ @Test
+ public void testNonAsciiQueryStringWithB() throws Exception {
+ doTestRewrite("RewriteRule ^/b/(.*)/id=(.*) /c?filename=$1&id=$2 [B]",
+ "/b/file01/id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95", "/c",
+
"filename=file01&id=%25E5%259C%25A8%25E7%25BA%25BF%25E6%25B5%258B%25E8%25AF%2595");
+ }
+
+
+ @Test
+ public void testNonAsciiQueryStringAndPathAndRedirectWithB() throws
Exception {
+ // Note the double encoding of the result (httpd produces the same
result)
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*)/id=(.*)
/c/$1?filename=$2&id=$3 [B,R]",
+ "/b/%E5%9C%A8%E7%BA%BF/file01/id=%E6%B5%8B%E8%AF%95",
+ "/c/%25E5%259C%25A8%25E7%25BA%25BF",
+ "filename=file01&id=%25E6%25B5%258B%25E8%25AF%2595");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsNone() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%C2%A1",
"id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsB() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%25C2%25A1",
"id=%25C2%25A1");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsR() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%C2%A1",
"id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsRB() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%25C2%25A1",
"id=%25C2%25A1");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsRNE() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,NE]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE",
"/c/\u00C2\u00A1\u00C2\u00A1", "id=\u00C2\u00A1");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsRBNE() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B,NE]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/\u00C2\u00A1%C2%A1",
"id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsBQSA() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B,QSA]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%25C2%25A1",
+ "id=%25C2%25A1&di=%25C2%25AE");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsRQSA() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,QSA]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%C2%A1",
+ "id=%C2%A1&di=%C2%AE");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsRBQSA() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B,QSA]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%25C2%25A1",
+ "id=%25C2%25A1&di=%C2%AE");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsRNEQSA() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,NE,QSA]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/\u00C2\u00A1\u00C2\u00A1",
+ "id=\u00C2\u00A1&di=\u00C2\u00AE");
+ }
+
+
+ @Test
+ public void testUtf8WithBothQsFlagsRBNEQSA() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B,NE,QSA]",
+ "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/\u00C2\u00A1%C2%A1",
+ "id=%C2%A1&di=\u00C2\u00AE");
+ }
+
+
+ @Test
+ public void testUtf8WithOriginalQsFlagsNone() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1",
+ "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%C2%A1", "id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithOriginalQsFlagsB() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]",
+ "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%25C2%25A1", "id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithOriginalQsFlagsR() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R]",
+ "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%C2%A1", "id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithOriginalQsFlagsRB() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]",
+ "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%25C2%25A1", "id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithOriginalQsFlagsRNE() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,NE]",
+ "/b/%C2%A1?id=%C2%A1", "/c/\u00C2\u00A1\u00C2\u00A1",
"id=\u00C2\u00A1");
+ }
+
+
+ @Test
+ public void testUtf8WithOriginalQsFlagsRBNE() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B,NE]",
+ "/b/%C2%A1?id=%C2%A1", "/c/\u00C2\u00A1%C2%A1",
"id=\u00C2\u00A1");
+ }
+
+
+ @Test
+ public void testUtf8WithRewriteQsFlagsNone() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2",
+ "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%C2%A1", "id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithRewriteQsFlagsB() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]",
+ "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1");
+ }
+
+
+ @Test
+ public void testUtf8WithRewriteQsFlagsR() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R]",
+ "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%C2%A1", "id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8WithRewriteQsFlagsRB() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]",
+ "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1");
+ }
+
+
+ @Test
+ public void testUtf8WithRewriteQsFlagsRNE() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,NE]",
+ "/b/%C2%A1/id=%C2%A1", "/c/\u00C2\u00A1\u00C2\u00A1",
"id=\u00C2\u00A1");
+ }
+
+
+ @Test
+ public void testUtf8WithRewriteQsFlagsRBNE() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B,NE]",
+ "/b/%C2%A1/id=%C2%A1", "/c/\u00C2\u00A1%C2%A1", "id=%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8FlagsNone() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1", "/b/%C2%A1",
"/c/%C2%A1%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8FlagsB() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]", "/b/%C2%A1",
"/c/%C2%A1%25C2%25A1");
+ }
+
+
+ @Test
+ public void testUtf8FlagsR() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R]", "/b/%C2%A1",
"/c/%C2%A1%C2%A1");
+ }
+
+
+ @Test
+ public void testUtf8FlagsRB() throws Exception {
+ // Note %C2%A1 == \u00A1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]", "/b/%C2%A1",
"/c/%C2%A1%25C2%25A1");
+ }
+
+
+ @Test
+ public void testUtf8FlagsRNE() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,NE]",
+ "/b/%C2%A1", "/c/\u00C2\u00A1\u00C2\u00A1");
+ }
+
+
+ @Test
+ public void testUtf8FlagsRBNE() throws Exception {
+ // Note %C2%A1 == \u00A1
+ // Failing to escape the redirect means UTF-8 bytes in the Location
+ // header which will be treated as if they are ISO-8859-1
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B,NE]",
+ "/b/%C2%A1", "/c/\u00C2\u00A1%C2%A1");
+ }
+
+
+ private void doTestRewrite(String config, String request, String
expectedURI) throws Exception {
+ doTestRewrite(config, request, expectedURI, null);
+ }
+
+
private void doTestRewrite(String config, String request, String
expectedURI,
String expectedQueryString) throws Exception {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]