You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2010/01/26 12:23:56 UTC

svn commit: r903175 - in /sling/trunk/bundles/servlets/get/src: main/java/org/apache/sling/servlets/get/impl/ test/java/org/apache/sling/servlets/get/impl/

Author: fmeschbe
Date: Tue Jan 26 11:23:55 2010
New Revision: 903175

URL: http://svn.apache.org/viewvc?rev=903175&view=rev
Log:
SLING-1324 Add support to define the redirect status in the RedirectResource's
value map and some unit tests to validate helper methods.

Modified:
    sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/RedirectServlet.java
    sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockRequestPathInfo.java
    sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockSlingHttpServletRequest.java
    sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/RedirectServletTest.java

Modified: sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/RedirectServlet.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/RedirectServlet.java?rev=903175&r1=903174&r2=903175&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/RedirectServlet.java (original)
+++ sling/trunk/bundles/servlets/get/src/main/java/org/apache/sling/servlets/get/impl/RedirectServlet.java Tue Jan 26 11:23:55 2010
@@ -69,6 +69,9 @@
     /** The name of the target property */
     public static final String TARGET_PROP = "sling:target";
 
+    /** The name of the redirect status property */
+    public static final String STATUS_PROP = "sling:status";
+
     /** default log */
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -76,20 +79,20 @@
 
     /** @scr.property valueRef="DEFAULT_JSON_RENDERER_MAXIMUM_RESULTS" type="Integer" */
     public static final String JSON_RENDERER_MAXIMUM_RESULTS_PROPERTY = "json.maximumresults";
-    
+
     /** Default value for the maximum amount of results that should be returned by the jsonResourceWriter */
     public static final int DEFAULT_JSON_RENDERER_MAXIMUM_RESULTS = 200;
-    
+
     private int jsonMaximumResults;
-    
+
     protected void activate(ComponentContext ctx) {
       Dictionary<?, ?> props = ctx.getProperties();
-      this.jsonMaximumResults = OsgiUtil.toInteger(props.get(JSON_RENDERER_MAXIMUM_RESULTS_PROPERTY), 
+      this.jsonMaximumResults = OsgiUtil.toInteger(props.get(JSON_RENDERER_MAXIMUM_RESULTS_PROPERTY),
           DEFAULT_JSON_RENDERER_MAXIMUM_RESULTS);
       // When the maximumResults get updated, we force a reset for the jsonRendererServlet.
       jsonRendererServlet = getJsonRendererServlet();
     }
-    
+
     @Override
     protected void doGet(SlingHttpServletRequest request,
             SlingHttpServletResponse response) throws ServletException,
@@ -136,18 +139,25 @@
 
             // if the target resource is a path (string), redirect there
             targetPath = targetResource.adaptTo(String.class);
-
         }
 
         // if we got a target path, make it external and redirect to it
         if (targetPath != null) {
             if (!isUrl(targetPath)) {
                 // make path relative and append selectors, extension etc.
+                // this is an absolute URI suitable for the Location header
                 targetPath = toRedirectPath(targetPath, request);
             }
 
-            // and redirect there ...
-            response.sendRedirect(targetPath);
+            final int status = getStatus(valueMap);
+
+            // redirect the client, use our own setup since we might have a
+            // custom response status and we already have converted the target
+            // into an absolute URI.
+            response.reset();
+            response.setStatus(status);
+            response.setHeader("Location", targetPath);
+            response.flushBuffer();
 
             return;
         }
@@ -158,11 +168,38 @@
     }
 
     /**
-     * Create a relative redirect URL for the targetPath relative to the given
-     * request. The URL is relative to the request's resource and will include
-     * the selectors, extension, suffix and query string of the request.
+     * Returns the response status from the {@link #STATUS_PROP} property in the
+     * value map. If <code>valueMap</code> is <code>null</code>, the property is
+     * not contained in the map or if the value is outside of the value HTTP
+     * response status range of [ 100 .. 999 ], the default status 302/FOUND is
+     * returned.
+     *
+     * @param valueMap The <code>valueMap</code> providing the optional status
+     *            property.
+     * @return The status value as defined above.
+     */
+    static int getStatus(final ValueMap valueMap) {
+        if (valueMap != null) {
+            final Integer statusInt = valueMap.get(STATUS_PROP, Integer.class);
+            if (statusInt != null) {
+                int status = statusInt.intValue();
+                if (status >= 100 && status <= 999) {
+                    return status;
+                }
+            }
+        }
+
+        // fall back to default value
+        return HttpServletResponse.SC_FOUND;
+
+    }
+
+    /**
+     * Create an absolute URI suitable for the "Location" response header
+     * including any selectors, extension, suffix and query from the current
+     * request.
      */
-    protected static String toRedirectPath(String targetPath,
+    static String toRedirectPath(String targetPath,
             SlingHttpServletRequest request) {
 
         // if the target path is an URL, do nothing and return it unmodified
@@ -205,8 +242,47 @@
             target.append('?').append(request.getQueryString());
         }
 
-        // return the mapped full path
-        return request.getResourceResolver().map(request, target.toString());
+        // return the mapped full path and return if already an absolute URI
+        final String finalTarget = request.getResourceResolver().map(request, target.toString());
+        if (isUrl(finalTarget)) {
+            return finalTarget;
+        }
+
+        // otherwise prepend the current request's information
+        return toAbsoluteUri(request.getScheme(), request.getServerName(),
+            request.getServerPort(), finalTarget);
+    }
+
+    /**
+     * Returns an absolute URI built from the given parameters.
+     *
+     * @param scheme The scheme for the URI to be built.
+     * @param host The name of the host.
+     * @param port The port or -1 to not add a port number to the URI. For
+     *            <code>http</code> and <code>https</code> schemes the port is
+     *            not added if it is the default port.
+     * @param targetPath The path of the resulting URI. This path is expected to
+     *            not be an absolute URI.
+     * @return The absolute URI built from the components.
+     */
+    static String toAbsoluteUri(final String scheme, final String host,
+            final int port, final String targetPath) {
+
+        // 1. scheme and host
+        final StringBuilder absUriBuilder = new StringBuilder();
+        absUriBuilder.append(scheme).append("://").append(host);
+
+        // 2. append the port depending on the scheme and whether the port is
+        // the default or not
+        if (port > 0) {
+            if (!(("http".equals(scheme) && port == 80) || ("https".equals(scheme) && port == 443))) {
+                absUriBuilder.append(':').append(port);
+            }
+        }
+
+        // 3. the actual target path
+        absUriBuilder.append(targetPath);
+        return absUriBuilder.toString();
     }
 
     private Servlet getJsonRendererServlet() {
@@ -224,15 +300,31 @@
 
     /**
      * Returns <code>true</code> if the path is potentially an URL. This
-     * simplistic check looks for a ":/" string in the path assuming that this
-     * is a separator to separate the scheme from the scheme-specific part. If
-     * the separator occurs after a query separator ("?"), though, it is not
-     * assumed to be a scheme-separator.
+     * checks whether the path starts with a scheme followed by a colon
+     * according to <a href="http://www.faqs.org/rfcs/rfc2396.html">RFC-2396</a>:
+     * <pre>
+     *     scheme = alpha *( alpha | digit | "+" | "-" | "." )
+     *     alpha  = [ "A" .. "Z", "a" .. "z" ]
+     *     digit  = [ "0" .. "9" ]
+     * </pre>
      */
     private static boolean isUrl(final String path) {
-        final int protocolIndex = path.indexOf(":/");
-        final int queryIndex = path.indexOf('?');
-        return protocolIndex > -1
-            && (queryIndex == -1 || queryIndex > protocolIndex);
+        for (int i = 0; i < path.length(); i++) {
+            char c = path.charAt(i);
+            if (c == ':') {
+                return true;
+            }
+            if (!((c >= 'a' && c <= 'z')
+                    || (c >= 'A' && c <= 'Z')
+                    || (i > 0
+                            && ((c >= '0' && c <= '9')
+                                    || c == '.'
+                                    || c == '+'
+                                    || c == '-')))) {
+                break;
+            }
+        }
+        return false;
     }
+
 }

Modified: sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockRequestPathInfo.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockRequestPathInfo.java?rev=903175&r1=903174&r2=903175&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockRequestPathInfo.java (original)
+++ sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockRequestPathInfo.java Tue Jan 26 11:23:55 2010
@@ -30,10 +30,6 @@
 
     private final String path;
 
-    public MockRequestPathInfo(String selectors, String extension, String suffix) {
-        this(selectors, extension ,suffix, null);
-    }
-
     public MockRequestPathInfo(String selectors, String extension, String suffix, String path) {
         this.selectors = selectors;
         this.extension = extension;

Modified: sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockSlingHttpServletRequest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockSlingHttpServletRequest.java?rev=903175&r1=903174&r2=903175&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockSlingHttpServletRequest.java (original)
+++ sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/MockSlingHttpServletRequest.java Tue Jan 26 11:23:55 2010
@@ -323,15 +323,15 @@
     }
 
     public String getScheme() {
-        return null;
+        return RedirectServletTest.TEST_SCHEME;
     }
 
     public String getServerName() {
-        return null;
+        return RedirectServletTest.TEST_HOST;
     }
 
     public int getServerPort() {
-        return 0;
+        return RedirectServletTest.TEST_PORT;
     }
 
     public boolean isSecure() {

Modified: sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/RedirectServletTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/RedirectServletTest.java?rev=903175&r1=903174&r2=903175&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/RedirectServletTest.java (original)
+++ sling/trunk/bundles/servlets/get/src/test/java/org/apache/sling/servlets/get/impl/RedirectServletTest.java Tue Jan 26 11:23:55 2010
@@ -18,142 +18,211 @@
  */
 package org.apache.sling.servlets.get.impl;
 
+import java.util.Collections;
+import java.util.HashMap;
+
+import javax.servlet.http.HttpServletResponse;
+
 import junit.framework.TestCase;
 
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
 
 public class RedirectServletTest extends TestCase {
 
+    static final String TEST_SCHEME = "http";
+
+    static final String TEST_HOST = "the.host.any";
+
+    static final int TEST_PORT = 80;
+
+    static final String TEST_PREFIX = TEST_SCHEME + "://" + TEST_HOST;
+
+    public void testToAbsoluteURI() {
+        final String http = "http";
+        final String https = "https";
+        final String scheme = "test";
+        final String host = TEST_HOST;
+        final int port80 = 80;
+        final int port443 = 443;
+        final int portAny = 9999;
+        final int portNone = -1;
+        final String target = "/target";
+
+        // regular building without default ports
+        assertEquals(http + "://" + host + ":" + portAny + target,
+            RedirectServlet.toAbsoluteUri(http, host, portAny, target));
+        assertEquals(https + "://" + host + ":" + portAny + target,
+            RedirectServlet.toAbsoluteUri(https, host, portAny, target));
+        assertEquals(scheme + "://" + host + ":" + portAny + target,
+            RedirectServlet.toAbsoluteUri(scheme, host, portAny, target));
+
+        // building with default ports
+        assertEquals(http + "://" + host + target,
+            RedirectServlet.toAbsoluteUri(http, host, port80, target));
+        assertEquals(https + "://" + host + target,
+            RedirectServlet.toAbsoluteUri(https, host, port443, target));
+        assertEquals(scheme + "://" + host + ":" + port80 + target,
+            RedirectServlet.toAbsoluteUri(scheme, host, port80, target));
+
+        // building without ports
+        assertEquals(http + "://" + host + target,
+            RedirectServlet.toAbsoluteUri(http, host, portNone, target));
+        assertEquals(https + "://" + host + target,
+            RedirectServlet.toAbsoluteUri(https, host, portNone, target));
+        assertEquals(scheme + "://" + host + target,
+            RedirectServlet.toAbsoluteUri(scheme, host, portNone, target));
+    }
+
+    public void testGetStatus() {
+        final int found = HttpServletResponse.SC_FOUND;
+        final int valid = 768;
+        final int invalidLow = 77;
+        final int invalidHigh = 1234;
+        final int min = 100;
+        final int max = 999;
+
+        assertStatus(found, -2);
+        assertStatus(found, -1);
+        assertStatus(found, invalidLow);
+        assertStatus(found, invalidHigh);
+
+        assertStatus(valid, valid);
+        assertStatus(min, min);
+        assertStatus(max, max);
+    }
+
     public void testSameParent() {
         String base = "/a";
         String target = "/b";
-        assertEquals("/b", toRedirect(base, target));
+        assertEqualsUri("/b", toRedirect(base, target));
 
         base = "/";
         target = "/a";
-        assertEquals("/a", toRedirect(base, target));
+        assertEqualsUri("/a", toRedirect(base, target));
 
         base = "/a/b/c";
         target = "/a/b/d";
-        assertEquals("/a/b/d", toRedirect(base, target));
+        assertEqualsUri("/a/b/d", toRedirect(base, target));
     }
 
     public void testTrailingSlash() {
         String base = "/a/b/c/";
         String target = "/a/b/c.html";
-        assertEquals("/a/b/c.html", toRedirect(base, target));
+        assertEqualsUri("/a/b/c.html", toRedirect(base, target));
     }
 
     public void testCommonAncestor() {
         String base = "/a/b/c/d";
         String target = "/a/b/x/y";
-        assertEquals("/a/b/x/y", toRedirect(base, target));
+        assertEqualsUri("/a/b/x/y", toRedirect(base, target));
     }
 
     public void testChild() {
         String base = "/a.html";
         String target = "/a/b.html";
-        assertEquals("/a/b.html", toRedirect(base, target));
+        assertEqualsUri("/a/b.html", toRedirect(base, target));
 
         base = "/a";
         target = "/a/b.html";
-        assertEquals("/a/b.html", toRedirect(base, target));
+        assertEqualsUri("/a/b.html", toRedirect(base, target));
 
         base = "/a";
         target = "/a/b";
-        assertEquals("/a/b", toRedirect(base, target));
+        assertEqualsUri("/a/b", toRedirect(base, target));
 
         base = "/a.html";
         target = "/a/b/c.html";
-        assertEquals("/a/b/c.html", toRedirect(base, target));
+        assertEqualsUri("/a/b/c.html", toRedirect(base, target));
 
         base = "/a";
         target = "/a/b/c.html";
-        assertEquals("/a/b/c.html", toRedirect(base, target));
+        assertEqualsUri("/a/b/c.html", toRedirect(base, target));
 
         base = "/a";
         target = "/a/b/c";
-        assertEquals("/a/b/c", toRedirect(base, target));
+        assertEqualsUri("/a/b/c", toRedirect(base, target));
     }
 
     public void testChildNonRoot() {
         String base = "/x/a.html";
         String target = "/x/a/b.html";
-        assertEquals("/x/a/b.html", toRedirect(base, target));
+        assertEqualsUri("/x/a/b.html", toRedirect(base, target));
 
         base = "/x/a";
         target = "/x/a/b.html";
-        assertEquals("/x/a/b.html", toRedirect(base, target));
+        assertEqualsUri("/x/a/b.html", toRedirect(base, target));
 
         base = "/x/a";
         target = "/x/a/b";
-        assertEquals("/x/a/b", toRedirect(base, target));
+        assertEqualsUri("/x/a/b", toRedirect(base, target));
 
         base = "/x/a.html";
         target = "/x/a/b/c.html";
-        assertEquals("/x/a/b/c.html", toRedirect(base, target));
+        assertEqualsUri("/x/a/b/c.html", toRedirect(base, target));
 
         base = "/x/a";
         target = "/x/a/b/c.html";
-        assertEquals("/x/a/b/c.html", toRedirect(base, target));
+        assertEqualsUri("/x/a/b/c.html", toRedirect(base, target));
 
         base = "/x/a";
         target = "/x/a/b/c";
-        assertEquals("/x/a/b/c", toRedirect(base, target));
+        assertEqualsUri("/x/a/b/c", toRedirect(base, target));
     }
 
     public void testChildRelative() {
         String base = "/a";
         String target = "b.html";
-        assertEquals("/a/b.html", toRedirect(base, target));
+        assertEqualsUri("/a/b.html", toRedirect(base, target));
 
         base = "/a";
         target = "b";
-        assertEquals("/a/b", toRedirect(base, target));
+        assertEqualsUri("/a/b", toRedirect(base, target));
 
         base = "/a";
         target = "b/c.html";
-        assertEquals("/a/b/c.html", toRedirect(base, target));
+        assertEqualsUri("/a/b/c.html", toRedirect(base, target));
 
         base = "/a";
         target = "b/c";
-        assertEquals("/a/b/c", toRedirect(base, target));
+        assertEqualsUri("/a/b/c", toRedirect(base, target));
     }
 
     public void testChildNonRootRelative() {
         String base = "/x/a";
         String target = "b.html";
-        assertEquals("/x/a/b.html", toRedirect(base, target));
+        assertEqualsUri("/x/a/b.html", toRedirect(base, target));
 
         base = "/x/a";
         target = "b";
-        assertEquals("/x/a/b", toRedirect(base, target));
+        assertEqualsUri("/x/a/b", toRedirect(base, target));
 
         base = "/x/a";
         target = "b/c.html";
-        assertEquals("/x/a/b/c.html", toRedirect(base, target));
+        assertEqualsUri("/x/a/b/c.html", toRedirect(base, target));
 
         base = "/x/a";
         target = "b/c";
-        assertEquals("/x/a/b/c", toRedirect(base, target));
+        assertEqualsUri("/x/a/b/c", toRedirect(base, target));
     }
 
     public void testUnCommon() {
         String base = "/a/b/c/d";
         String target = "/w/x/y/z";
-        assertEquals("/w/x/y/z", toRedirect(base, target));
+        assertEqualsUri("/w/x/y/z", toRedirect(base, target));
     }
 
     public void testSibbling() {
         String base = "/a/b";
         String target0 = "../y/z";
-        assertEquals("/a/y/z", toRedirect(base, target0));
+        assertEqualsUri("/a/y/z", toRedirect(base, target0));
 
         String target1 = "../../y/z";
-        assertEquals("/y/z", toRedirect(base, target1));
+        assertEqualsUri("/y/z", toRedirect(base, target1));
 
         String target2 = "../../../y/z";
-        assertEquals(base + "/" + target2, toRedirect(base, target2));
+        assertEqualsUri(base + "/" + target2, toRedirect(base, target2));
     }
 
     public void testSelectorsEtc() {
@@ -218,7 +287,20 @@
             target);
     }
 
-    private void assertEquals(String expected, String basePath,
+    public void testEmptyPath() {
+        SlingHttpServletRequest request = new MockSlingHttpServletRequest("/",
+            null, null, null, null, "", "/webapp");
+        String path = RedirectServlet.toRedirectPath("/index.html", request);
+        assertEqualsUri("/webapp/index.html", path);
+        request = new MockSlingHttpServletRequest("/", null, null, null, null,
+            "/", "/webapp");
+        path = RedirectServlet.toRedirectPath("/index.html", request);
+        assertEqualsUri("/webapp/index.html", path);
+    }
+
+    //---------- Helper
+
+    private static void assertEquals(String expected, String basePath,
             String selectors, String extension, String suffix,
             String queryString, String targetPath) {
 
@@ -238,14 +320,35 @@
         String actual = toRedirect(basePath, selectors, extension, suffix,
             queryString, targetPath);
 
-        assertEquals(expected, actual);
+        assertEqualsUri(expected, actual);
+    }
+
+    private static void assertEqualsUri(String expected, String actual) {
+        assertEquals(TEST_PREFIX + expected, actual);
+    }
+
+    private static void assertStatus(final int expectedStatus,
+            final int testStatus) {
+        final ValueMap valueMap;
+        if (testStatus == -2) {
+            valueMap = null;
+        } else if (testStatus == -1) {
+            valueMap = new ValueMapDecorator(new HashMap<String, Object>());
+        } else {
+            valueMap = new ValueMapDecorator(Collections.singletonMap(
+                RedirectServlet.STATUS_PROP, (Object) testStatus));
+        }
+
+        final int actualStatus = RedirectServlet.getStatus(valueMap);
+
+        assertEquals(expectedStatus, actualStatus);
     }
 
-    private String toRedirect(String basePath, String targetPath) {
+    private static String toRedirect(String basePath, String targetPath) {
         return toRedirect(basePath, null, null, null, null, targetPath);
     }
 
-    private String toRedirect(String basePath, String selectors,
+    private static String toRedirect(String basePath, String selectors,
             String extension, String suffix, String queryString,
             String targetPath) {
         SlingHttpServletRequest request = new MockSlingHttpServletRequest(
@@ -253,14 +356,4 @@
         return RedirectServlet.toRedirectPath(targetPath, request);
     }
 
-    public void testEmptyPath() {
-        SlingHttpServletRequest request = new MockSlingHttpServletRequest("/",
-            null, null, null, null, "", "/webapp");
-        String path = RedirectServlet.toRedirectPath("/index.html", request);
-        assertEquals("/webapp/index.html", path);
-        request = new MockSlingHttpServletRequest("/", null, null, null, null,
-            "/", "/webapp");
-        path = RedirectServlet.toRedirectPath("/index.html", request);
-        assertEquals("/webapp/index.html", path);
-    }
 }