259 lines
12 KiB
Diff
259 lines
12 KiB
Diff
--- java/org/apache/catalina/valves/rewrite/RewriteValve.java
|
|
+++ java/org/apache/catalina/valves/rewrite/RewriteValve.java
|
|
@@ -326,9 +326,8 @@
|
|
|
|
// As long as MB isn't a char sequence or affiliated, this has to be converted to a string
|
|
Charset uriCharset = request.getConnector().getURICharset();
|
|
- String originalQueryStringEncoded = request.getQueryString();
|
|
- MessageBytes urlMB =
|
|
- context ? request.getRequestPathMB() : request.getDecodedRequestURIMB();
|
|
+ String queryStringOriginalEncoded = request.getQueryString();
|
|
+ MessageBytes urlMB = context ? request.getRequestPathMB() : request.getDecodedRequestURIMB();
|
|
urlMB.toChars();
|
|
CharSequence urlDecoded = urlMB.getCharChunk();
|
|
|
|
@@ -425,10 +424,10 @@
|
|
StringBuilder urlStringEncoded =
|
|
new StringBuilder(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset));
|
|
|
|
- if (!qsd && originalQueryStringEncoded != null && !originalQueryStringEncoded.isEmpty()) {
|
|
+ if (!qsd && queryStringOriginalEncoded != null && !queryStringOriginalEncoded.isEmpty()) {
|
|
if (rewrittenQueryStringRewriteEncoded == null) {
|
|
urlStringEncoded.append('?');
|
|
- urlStringEncoded.append(originalQueryStringEncoded);
|
|
+ urlStringEncoded.append(queryStringOriginalEncoded);
|
|
} else {
|
|
if (qsa) {
|
|
// if qsa is specified append the query
|
|
@@ -436,7 +435,7 @@
|
|
urlStringEncoded.append(
|
|
REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset));
|
|
urlStringEncoded.append('&');
|
|
- urlStringEncoded.append(originalQueryStringEncoded);
|
|
+ urlStringEncoded.append(queryStringOriginalEncoded);
|
|
} 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
|
|
@@ -550,24 +549,31 @@
|
|
|
|
// Step 3. Complete the 2nd stage to encoding.
|
|
chunk.append(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset));
|
|
- // Decoded and normalized URI
|
|
- // Rewriting may have denormalized the URL
|
|
- urlStringRewriteEncoded = RequestUtil.normalize(urlStringRewriteEncoded);
|
|
+ // Rewriting may have denormalized the URL and added encoded characters
|
|
+ // Decode then normalize
|
|
+ String urlStringRewriteDecoded = URLDecoder.decode(urlStringRewriteEncoded, String.valueOf(uriCharset));
|
|
+ urlStringRewriteDecoded = RequestUtil.normalize(urlStringRewriteDecoded);
|
|
request.getCoyoteRequest().decodedURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
|
|
chunk = request.getCoyoteRequest().decodedURI().getCharChunk();
|
|
if (context) {
|
|
// This is decoded and normalized
|
|
chunk.append(request.getServletContext().getContextPath());
|
|
}
|
|
- chunk.append(URLDecoder.decode(urlStringRewriteEncoded, String.valueOf(uriCharset)));
|
|
- // Set the new Query if there is one
|
|
- if (queryStringRewriteEncoded != null) {
|
|
+ chunk.append(urlStringRewriteDecoded);
|
|
+ // Set the new Query String
|
|
+ if (queryStringRewriteEncoded == null) {
|
|
+ // No new query string. Therefore the original is retained unless QSD is defined.
|
|
+ if (qsd) {
|
|
+ request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
|
|
+ }
|
|
+ } else {
|
|
+ // New query string. Therefore the original is dropped unless QSA is defined (and QSD is not).
|
|
request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
|
|
chunk = request.getCoyoteRequest().queryString().getCharChunk();
|
|
chunk.append(REWRITE_QUERY_ENCODER.encode(queryStringRewriteEncoded, uriCharset));
|
|
- if (qsa && originalQueryStringEncoded != null && !originalQueryStringEncoded.isEmpty()) {
|
|
+ if (qsa && queryStringOriginalEncoded != null && !queryStringOriginalEncoded.isEmpty()) {
|
|
chunk.append('&');
|
|
- chunk.append(originalQueryStringEncoded);
|
|
+ chunk.append(queryStringOriginalEncoded);
|
|
}
|
|
}
|
|
// Set the new host if it changed
|
|
@@ -652,6 +658,10 @@
|
|
StringTokenizer flagsTokenizer = new StringTokenizer(flags, ",");
|
|
while (flagsTokenizer.hasMoreElements()) {
|
|
parseRuleFlag(line, rule, flagsTokenizer.nextToken());
|
|
+ }
|
|
+ // If QSD and QSA are present, QSD always takes precedence
|
|
+ if (rule.isQsdiscard()) {
|
|
+ rule.setQsappend(false);
|
|
}
|
|
}
|
|
return rule;
|
|
--- test/org/apache/catalina/startup/TomcatBaseTest.java
|
|
+++ test/org/apache/catalina/startup/TomcatBaseTest.java
|
|
@@ -551,7 +551,7 @@
|
|
value.append(";");
|
|
}
|
|
}
|
|
- out.println("PARAM/" + name + ": " + value);
|
|
+ out.println("PARAM:" + name + ": " + value);
|
|
}
|
|
|
|
out.println("SESSION-REQUESTED-ID: " +
|
|
--- test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
|
|
+++ test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
|
|
@@ -180,17 +180,112 @@
|
|
}
|
|
|
|
@Test
|
|
- public void testQueryString() throws Exception {
|
|
+ public void testQueryStringTargetOnly() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2", "/b/id=1", "/c/id=1", "je=2");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringTargetOnlyQSA() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSA]", "/b/id=1", "/c/id=1", "je=2");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringTargetOnlyQSD() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSD]", "/b/id=1", "/c/id=1", "je=2");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringTargetOnlyQSAQSD() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSA,QSD]", "/b/id=1", "/c/id=1", "je=2");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringTargetOnlyQS() throws Exception {
|
|
doTestRewrite("RewriteRule ^/b/(.*) /c?$1", "/b/id=1", "/c", "id=1");
|
|
}
|
|
|
|
@Test
|
|
+ public void testQueryStringTargetOnlyQSAQS() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSA]", "/b/id=1", "/c", "id=1");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringTargetOnlyQSDQS() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSD]", "/b/id=1", "/c", "id=1");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringTargetOnlyQSAQSDQS() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSA,QSD]", "/b/id=1", "/c", "id=1");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringSourceOnly() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1", "/b/d?id=1", "/c/d", "id=1");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringSourceOnlyQSA() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSA]", "/b/d?id=1", "/c/d", "id=1");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringSourceOnlyQSD() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?id=1", "/c/d", null);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringSourceOnlyQSAQSD() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSA,QSD]", "/b/d?id=1", "/c/d", null);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringSourceAndTarget() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1", "/b/d?je=2", "/c/d", "id=1");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringSourceAndTargetQSA() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSA]", "/b/d?je=2", "/c/d", "id=1&je=2");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringSourceAndTargetQSD() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSD]", "/b/d?je=2", "/c/d", "id=1");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringSourceAndTargetQSAQSD() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSA,QSD]", "/b/d?je=2", "/c/d", "id=1");
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringEncoded01() throws Exception {
|
|
+ doTestRewrite("RewriteCond %{QUERY_STRING} a=(.*)\nRewriteRule ^/b.*$ /%1 [QSD]", "/b?a=c", "/c", null);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringEncoded02() throws Exception {
|
|
+ doTestRewrite("RewriteCond %{QUERY_STRING} a=(.*)\nRewriteRule ^/b.*$ /z/%1 [QSD]", "/b?a=%2e%2e%2fc%2faAbB", "/z/%2e%2e%2fc%2faAbB", null);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
public void testQueryStringRemove() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?", "/b/d?id=1", "/c/d", null);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringRemove02() throws Exception {
|
|
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?id=1", "/c/d", null);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testQueryStringRemoveInvalid() throws Exception {
|
|
doTestRewrite("RewriteRule ^/b/(.*) /c/$1?", "/b/d?=1", "/c/d", null);
|
|
}
|
|
|
|
@Test
|
|
- public void testQueryStringRemove02() throws Exception {
|
|
+ public void testQueryStringRemoveInvalid02() throws Exception {
|
|
doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?=1", "/c/d", null);
|
|
}
|
|
|
|
@@ -511,9 +606,8 @@
|
|
@Test
|
|
public void testFlagsNC() throws Exception {
|
|
// https://bz.apache.org/bugzilla/show_bug.cgi?id=60116
|
|
- doTestRewrite("RewriteCond %{QUERY_STRING} a=([a-z]*) [NC]\n"
|
|
- + "RewriteRule .* - [E=X-Test:%1]",
|
|
- "/c?a=aAa", "/c", null, "aAa");
|
|
+ doTestRewrite("RewriteCond %{QUERY_STRING} a=([a-z]*) [NC]\n" + "RewriteRule .* - [E=X-Test:%1]", "/c?a=aAa",
|
|
+ "/c", "a=aAa", "aAa");
|
|
}
|
|
|
|
|
|
@@ -669,12 +763,16 @@
|
|
// were written into the request target
|
|
Assert.assertEquals(400, rc);
|
|
} else {
|
|
+ // If there is an expected URI, the request should be successful
|
|
+ Assert.assertEquals(200, rc);
|
|
String body = res.toString();
|
|
RequestDescriptor requestDesc = SnoopResult.parse(body);
|
|
String requestURI = requestDesc.getRequestInfo("REQUEST-URI");
|
|
Assert.assertEquals(expectedURI, requestURI);
|
|
|
|
- if (expectedQueryString != null) {
|
|
+ if (expectedQueryString == null) {
|
|
+ Assert.assertTrue(requestDesc.getParams().isEmpty());
|
|
+ } else {
|
|
String queryString = requestDesc.getRequestInfo("REQUEST-QUERY-STRING");
|
|
Assert.assertEquals(expectedQueryString, queryString);
|
|
}
|
|
--- webapps/docs/changelog.xml
|
|
+++ webapps/docs/changelog.xml
|
|
@@ -113,6 +113,10 @@
|
|
<code>/WEB-INF/lib</code> when running a web application from a packed
|
|
WAR file. (markt)
|
|
</fix>
|
|
+ <fix>
|
|
+ Fix handling of <code>QSA</code> and <code>QSD</code> flags in
|
|
+ <code>RewriteValve</code>. (markt)
|
|
+ </fix>
|
|
</changelog>
|
|
</subsection>
|
|
<subsection name="Coyote">
|