Author: markt Date: Mon Nov 28 11:54:23 2016 New Revision: 1771718 URL: http://svn.apache.org/viewvc?rev=1771718&view=rev Log: Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=60411 Implement support in the RewriteValve for symbolic names to specify the redirect code to use when returning a redirect response to the user agent. Patch provided by Michael Osipov.
Modified: tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteRule.java tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteValve.java tomcat/trunk/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java tomcat/trunk/webapps/docs/changelog.xml tomcat/trunk/webapps/docs/rewrite.xml 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=1771718&r1=1771717&r2=1771718&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteRule.java (original) +++ tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteRule.java Mon Nov 28 11:54:23 2016 @@ -294,18 +294,18 @@ public class RewriteRule { /** * Prefix Substitution with http://thishost[:thisport]/ (which makes the * new URL a URI) to force a external redirection. If no code is given - * a HTTP response of 302 (MOVED TEMPORARILY) is used. If you want to - * use other response codes in the range 300-400 just specify them as - * a number or use one of the following symbolic names: temp (default), - * permanent, seeother. Use it for rules which should canonicalize the - * URL and give it back to the client, e.g., translate ``/~'' into ``/u/'' - * or always append a slash to /u/user, etc. Note: When you use this flag, - * make sure that the substitution field is a valid URL! If not, you are - * redirecting to an invalid location! And remember that this flag itself - * only prefixes the URL with http://thishost[:thisport]/, rewriting - * continues. Usually you also want to stop and do the redirection - * immediately. To stop the rewriting you also have to provide the - * 'L' flag. + * an HTTP response of 302 (FOUND, previously MOVED TEMPORARILY) is used. + * If you want to use other response codes in the range 300-399 just + * specify them as a number or use one of the following symbolic names: + * temp (default), permanent, seeother. Use it for rules which should + * canonicalize the URL and give it back to the client, e.g., translate + * ``/~'' into ``/u/'' or always append a slash to /u/user, etc. Note: + * When you use this flag, make sure that the substitution field is a + * valid URL! If not, you are redirecting to an invalid location! + * And remember that this flag itself only prefixes the URL with + * http://thishost[:thisport]/, rewriting continues. Usually you also + * want to stop and do the redirection immediately. To stop the + * rewriting you also have to provide the 'L' flag. */ protected boolean redirect = false; protected int redirectCode = 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=1771718&r1=1771717&r2=1771718&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteValve.java (original) +++ tomcat/trunk/java/org/apache/catalina/valves/rewrite/RewriteValve.java Mon Nov 28 11:54:23 2016 @@ -804,18 +804,30 @@ public class RewriteValve extends ValveB } else if (flag.startsWith("qsappend") || flag.startsWith("QSA")) { rule.setQsappend(true); } else if (flag.startsWith("redirect") || flag.startsWith("R")) { - if (flag.startsWith("redirect=")) { - flag = flag.substring("redirect=".length()); - rule.setRedirect(true); - rule.setRedirectCode(Integer.parseInt(flag)); - } else if (flag.startsWith("R=")) { - flag = flag.substring("R=".length()); - rule.setRedirect(true); - rule.setRedirectCode(Integer.parseInt(flag)); - } else { - rule.setRedirect(true); - rule.setRedirectCode(HttpServletResponse.SC_FOUND); + rule.setRedirect(true); + int redirectCode = HttpServletResponse.SC_FOUND; + if (flag.startsWith("redirect=") || flag.startsWith("R=")) { + if (flag.startsWith("redirect=")) { + flag = flag.substring("redirect=".length()); + } else if (flag.startsWith("R=")) { + flag = flag.substring("R=".length()); + } + switch(flag) { + case "temp": + redirectCode = HttpServletResponse.SC_FOUND; + break; + case "permanent": + redirectCode = HttpServletResponse.SC_MOVED_PERMANENTLY; + break; + case "seeother": + redirectCode = HttpServletResponse.SC_SEE_OTHER; + break; + default: + redirectCode = Integer.parseInt(flag); + break; + } } + rule.setRedirectCode(redirectCode); } else if (flag.startsWith("skip") || flag.startsWith("S")) { if (flag.startsWith("skip=")) { flag = flag.substring("skip=".length()); 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=1771718&r1=1771717&r2=1771718&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java (original) +++ tomcat/trunk/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java Mon Nov 28 11:54:23 2016 @@ -16,13 +16,18 @@ */ package org.apache.catalina.valves.rewrite; +import java.net.HttpURLConnection; import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.Assert; import org.junit.Test; import org.apache.catalina.Context; import org.apache.catalina.servlets.DefaultServlet; +import org.apache.catalina.startup.TesterServlet; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; @@ -479,6 +484,76 @@ public class TestRewriteValve extends To } + @Test + public void testDefaultRedirect() throws Exception { + // Disable the following of redirects for this test only + boolean originalValue = HttpURLConnection.getFollowRedirects(); + HttpURLConnection.setFollowRedirects(false); + try { + doTestRedirect("RewriteRule ^/from/a$ /to/b [R]", "/redirect/from/a", "/redirect/to/b", + 302); + } finally { + HttpURLConnection.setFollowRedirects(originalValue); + } + } + + + @Test + public void testTempRedirect() throws Exception { + // Disable the following of redirects for this test only + boolean originalValue = HttpURLConnection.getFollowRedirects(); + HttpURLConnection.setFollowRedirects(false); + try { + doTestRedirect("RewriteRule ^/from/a$ /to/b [R=temp]", "/redirect/from/a", "/redirect/to/b", + 302); + } finally { + HttpURLConnection.setFollowRedirects(originalValue); + } + } + + + @Test + public void testPermanentRedirect() throws Exception { + // Disable the following of redirects for this test only + boolean originalValue = HttpURLConnection.getFollowRedirects(); + HttpURLConnection.setFollowRedirects(false); + try { + doTestRedirect("RewriteRule ^/from/a$ /to/b [R=permanent]", "/redirect/from/a", "/redirect/to/b", + 301); + } finally { + HttpURLConnection.setFollowRedirects(originalValue); + } + } + + + @Test + public void testSeeotherRedirect() throws Exception { + // Disable the following of redirects for this test only + boolean originalValue = HttpURLConnection.getFollowRedirects(); + HttpURLConnection.setFollowRedirects(false); + try { + doTestRedirect("RewriteRule ^/from/a$ /to/b [R=seeother]", "/redirect/from/a", "/redirect/to/b", + 303); + } finally { + HttpURLConnection.setFollowRedirects(originalValue); + } + } + + + @Test + public void test307Redirect() throws Exception { + // Disable the following of redirects for this test only + boolean originalValue = HttpURLConnection.getFollowRedirects(); + HttpURLConnection.setFollowRedirects(false); + try { + doTestRedirect("RewriteRule ^/from/a$ /to/b [R=307]", "/redirect/from/a", "/redirect/to/b", + 307); + } finally { + HttpURLConnection.setFollowRedirects(originalValue); + } + } + + private void doTestRewrite(String config, String request, String expectedURI) throws Exception { doTestRewrite(config, request, expectedURI, null); } @@ -536,4 +611,41 @@ public class TestRewriteValve extends To } } } + + private void doTestRedirect(String config, String request, String expectedURI, + int expectedStatusCode) throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("redirect", null); + + RewriteValve rewriteValve = new RewriteValve(); + ctx.getPipeline().addValve(rewriteValve); + + rewriteValve.setConfiguration(config); + + Tomcat.addServlet(ctx, "tester", new TesterServlet()); + ctx.addServletMappingDecoded("/from/a", "tester"); + ctx.addServletMappingDecoded("/to/b", "tester"); + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + Map<String, List<String>> resHead = new HashMap<>(); + int rc = getUrl("http://localhost:" + getPort() + request, res, null, resHead); + res.setCharset(StandardCharsets.UTF_8); + + if (expectedURI == null) { + // Rewrite is expected to fail. Probably because invalid characters + // were written into the request target + Assert.assertEquals(400, rc); + } else { + List<String> locations = resHead.get("Location"); + Assert.assertFalse(locations.isEmpty()); + String redirectURI = locations.get(0); + Assert.assertEquals(expectedURI, redirectURI); + Assert.assertEquals(expectedStatusCode, rc); + } + } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1771718&r1=1771717&r2=1771718&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Mon Nov 28 11:54:23 2016 @@ -137,6 +137,12 @@ <code>JarInputStreamWrapper#close()</code> do not incorrectly trigger the closure of the underlying JAR or WAR file. (markt) </fix> + <fix> + <bug>60411</bug>: Implement support in the <code>RewriteValve</code> for + symbolic names to specify the redirect code to use when returning a + redirect response to the user agent. Patch provided by Michael Osipov. + (markt) + </fix> </changelog> </subsection> <subsection name="Coyote"> Modified: tomcat/trunk/webapps/docs/rewrite.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/rewrite.xml?rev=1771718&r1=1771717&r2=1771718&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/rewrite.xml (original) +++ tomcat/trunk/webapps/docs/rewrite.xml Mon Nov 28 11:54:23 2016 @@ -680,9 +680,9 @@ cannot use <code>$N</code> in the substi Prefix <em>Substitution</em> with <code>http://thishost[:thisport]/</code> (which makes the new URL a URI) to force a external redirection. If no - <em>code</em> is given, a HTTP response of 302 (MOVED + <em>code</em> is given, a HTTP response of 302 (FOUND, previously MOVED TEMPORARILY) will be returned. If you want to use other response - codes in the range 300-400, simply specify the appropriate number + codes in the range 300-399, simply specify the appropriate number or use one of the following symbolic names: <code>temp</code> (default), <code>permanent</code>, <code>seeother</code>. Use this for rules to --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org