From 0476d4d28d6350616bb41525d0b6cbe1067d57aa Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 23 Jul 2015 13:27:43 -0700 Subject: [PATCH] Supplementing redirect rules with RedirectUtil + Redirect rules should produce full URI for "Location" header + Redirect rules should produce no response body content --- .../rewrite/handler/RedirectPatternRule.java | 7 +- .../rewrite/handler/RedirectRegexRule.java | 9 ++- .../jetty/rewrite/handler/RedirectUtil.java | 71 +++++++++++++++++++ .../handler/RedirectRegexRuleTest.java | 12 ++++ .../jetty/test/rfcs/RFC2616BaseTest.java | 16 ++--- 5 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.java index 57350128145..81da7ed8163 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.java @@ -73,8 +73,11 @@ public class RedirectPatternRule extends PatternRule @Override public String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException { - response.setHeader("Location",response.encodeRedirectURL(_location)); - response.sendError(_statusCode); + String location = response.encodeRedirectURL(_location); + response.setHeader("Location",RedirectUtil.toRedirectURL(request,location)); + response.setStatus(_statusCode); + response.getOutputStream().flush(); // no output / content + response.getOutputStream().close(); return target; } diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java index aca659eab15..436a1aa819b 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java @@ -83,9 +83,12 @@ public class RedirectRegexRule extends RegexRule String group = matcher.group(g); target=target.replaceAll("\\$"+g,group); } - - response.setHeader("Location",response.encodeRedirectURL(target)); - response.sendError(_statusCode); + + target = response.encodeRedirectURL(target); + response.setHeader("Location",RedirectUtil.toRedirectURL(request,target)); + response.setStatus(_statusCode); + response.getOutputStream().flush(); // no output / content + response.getOutputStream().close(); return target; } diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java new file mode 100644 index 00000000000..caabb7e055d --- /dev/null +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.rewrite.handler; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jetty.util.URIUtil; + +/** + * Utility for managing redirect based rules + */ +public final class RedirectUtil +{ + /** + * Common point to generate a proper "Location" header for redirects. + * + * @param request + * the request the redirect should be based on (needed when relative locations are provided, so that + * server name, scheme, port can be built out properly) + * @param location + * the location URL to redirect to (can be a relative path) + * @return the full redirect "Location" URL (including scheme, host, port, path, etc...) + */ + public static String toRedirectURL(final HttpServletRequest request, String location) + { + if (!URIUtil.hasScheme(location)) + { + StringBuilder url = new StringBuilder(128); + URIUtil.appendSchemeHostPort(url,request.getScheme(),request.getServerName(),request.getServerPort()); + + if (location.startsWith("/")) + { + // absolute in context + location = URIUtil.canonicalPath(location); + } + else + { + // relative to request + String path = request.getRequestURI(); + String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path); + location = URIUtil.canonicalPath(URIUtil.addPaths(parent,location)); + if (!location.startsWith("/")) + url.append('/'); + } + + if (location == null) + throw new IllegalStateException("path cannot be above root"); + url.append(location); + + location = url.toString(); + } + + return location; + } +} diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRuleTest.java index 68b3ea9f656..dea0ce25f39 100644 --- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRuleTest.java +++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRuleTest.java @@ -53,6 +53,18 @@ public class RedirectRegexRuleTest extends AbstractRuleTestCase rule.matchAndApply("/my/dir/file/", _request, _response); assertRedirectResponse(HttpStatus.FOUND_302,"http://www.mortbay.org/"); } + + @Test + public void testLocationWithPathReplacement() throws IOException + { + RedirectRegexRule rule = new RedirectRegexRule(); + rule.setRegex("/documentation/(.*)$"); + rule.setReplacement("/docs/$1"); + + // Resource is dir + rule.matchAndApply("/documentation/top.html", _request, _response); + assertRedirectResponse(HttpStatus.FOUND_302,"http://0.0.0.0/docs/top.html"); + } @Test public void testLocationWithReplacmentGroupSimple() throws IOException diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java index 6125236f26c..af309d7fc6f 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java @@ -18,10 +18,8 @@ package org.eclipse.jetty.test.rfcs; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.matchers.JUnitMatchers.containsString; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; import java.io.File; import java.io.IOException; @@ -1159,12 +1157,12 @@ public abstract class RFC2616BaseTest req4.append("\n"); HttpTester.Response response = http.request(req4); - + String specId = "10.3 Redirection HTTP/1.1 w/content"; - assertEquals(specId,HttpStatus.FOUND_302, response.getStatus()); - assertEquals(specId,server.getScheme() + "://localhost/tests/R2.txt", response.get("Location")); - assertEquals(specId,"close", response.get("Connection")); - assertTrue(specId,response.get("Content-Length") == null); + assertThat(specId + " [status]",response.getStatus(),is(HttpStatus.FOUND_302)); + assertThat(specId + " [location]",response.get("Location"),is(server.getScheme() + "://localhost/tests/R2.txt")); + assertThat(specId + " [connection]",response.get("Connection"),is("close")); + assertThat(specId + " [content-length]",response.get("Content-Length"), nullValue()); } /**