You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ja...@apache.org on 2014/10/30 13:07:47 UTC

svn commit: r1635470 - in /felix/trunk/http/sslfilter: ./ src/main/java/org/apache/felix/http/sslfilter/internal/ src/test/java/org/apache/felix/http/sslfilter/internal/

Author: jawi
Date: Thu Oct 30 12:07:47 2014
New Revision: 1635470

URL: http://svn.apache.org/r1635470
Log:
FELIX-4420 - Implement sendRedirect:

- implemented original idea of overriding sendRedirect with logic for rewriting
  relative and absolute URLs;
- added test case provided by [~chetanm] and extended it with some additional tests.


Added:
    felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterJettyTest.java   (with props)
Modified:
    felix/trunk/http/sslfilter/pom.xml
    felix/trunk/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterResponse.java
    felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterResponseTest.java

Modified: felix/trunk/http/sslfilter/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/http/sslfilter/pom.xml?rev=1635470&r1=1635469&r2=1635470&view=diff
==============================================================================
--- felix/trunk/http/sslfilter/pom.xml (original)
+++ felix/trunk/http/sslfilter/pom.xml Thu Oct 30 12:07:47 2014
@@ -75,9 +75,9 @@
 
     <dependencies>
         <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>servlet-api</artifactId>
-            <version>2.3</version>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.servlet-api</artifactId>
+            <version>1.0.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -115,6 +115,13 @@
             <version>3.2.1</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+          <groupId>org.eclipse.jetty</groupId>
+          <artifactId>jetty-servlet</artifactId>
+          <version>${jetty.version}</version>
+          <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>

Modified: felix/trunk/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterResponse.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterResponse.java?rev=1635470&r1=1635469&r2=1635470&view=diff
==============================================================================
--- felix/trunk/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterResponse.java (original)
+++ felix/trunk/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterResponse.java Thu Oct 30 12:07:47 2014
@@ -26,6 +26,7 @@ import static org.apache.felix.http.sslf
 import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTPS_PORT;
 import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTP_PORT;
 
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 
@@ -38,16 +39,19 @@ import javax.servlet.http.HttpServletRes
  */
 class SslFilterResponse extends HttpServletResponseWrapper
 {
+    private final URL requestURL;
     private final String serverName;
     private final String serverProto;
     private final int serverPort;
     private final String clientProto;
     private final int clientPort;
 
-    public SslFilterResponse(HttpServletResponse response, HttpServletRequest request)
+    public SslFilterResponse(HttpServletResponse response, HttpServletRequest request) throws MalformedURLException
     {
         super(response);
 
+        this.requestURL = new URL(request.getRequestURL().toString());
+
         // Only rewrite URLs for the host & port the request was sent to...
         this.serverName = request.getServerName();
         this.serverPort = request.getServerPort();
@@ -102,6 +106,17 @@ class SslFilterResponse extends HttpServ
         super.setHeader(name, value);
     }
 
+    @Override
+    public void sendRedirect(String location) throws IOException
+    {
+        URL rewritten = rewriteUrlIfNeeded(location);
+        if (rewritten != null)
+        {
+            location = rewritten.toExternalForm();
+        }
+        super.sendRedirect(location);
+    }
+
     private int normalizePort(String protocol, int port)
     {
         if (port > 0)
@@ -117,9 +132,22 @@ class SslFilterResponse extends HttpServ
 
     private URL rewriteUrlIfNeeded(String value)
     {
+        if (value == null)
+        {
+            return null;
+        }
+
         try
         {
-            URL url = new URL(value);
+            URL url;
+            if (value.startsWith(this.serverProto.concat("://")))
+            {
+                url = new URL(value);
+            }
+            else
+            {
+                url = new URL(this.requestURL, value);
+            }
 
             String actualProto = url.getProtocol();
 

Added: felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterJettyTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterJettyTest.java?rev=1635470&view=auto
==============================================================================
--- felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterJettyTest.java (added)
+++ felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterJettyTest.java Thu Oct 30 12:07:47 2014
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.http.sslfilter.internal;
+
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_LOCATION;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_X_FORWARDED_PROTO;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_X_FORWARDED_SSL;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTP;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTPS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.EnumSet;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SslFilterJettyTest
+{
+    private InetSocketAddress serverAddress;
+
+    private Server server;
+    private ServletContextHandler context;
+    private boolean originalFollowRedirects;
+
+    @Before
+    public void setupServer() throws Exception
+    {
+        this.serverAddress = new InetSocketAddress("localhost", 8080);
+
+        this.context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+        this.context.setContextPath("/");
+        this.context.addFilter(new FilterHolder(new SslFilter()), "/*", EnumSet.of(DispatcherType.REQUEST));
+
+        this.server = new Server(this.serverAddress);
+        this.server.setHandler(this.context);
+
+        this.originalFollowRedirects = HttpURLConnection.getFollowRedirects();
+        HttpURLConnection.setFollowRedirects(false);
+    }
+
+    @After
+    public void tearDown() throws Exception
+    {
+        HttpURLConnection.setFollowRedirects(this.originalFollowRedirects);
+
+        if (this.server != null)
+        {
+            this.server.stop();
+        }
+    }
+
+    @Test
+    public void testSslFilterWithRelativeRedirectURL() throws Exception
+    {
+        String servletPath = "/test";
+        String redirectPath = "/foo";
+
+        this.context.addServlet(new ServletHolder(new RedirectServlet(redirectPath)), servletPath);
+        this.server.start();
+
+        HttpURLConnection conn = openConnection(createURL(servletPath));
+
+        assertEquals(302, conn.getResponseCode());
+        String location = conn.getHeaderField(HDR_LOCATION);
+        assertTrue(location, location.startsWith(HTTPS));
+    }
+
+    @Test
+    public void testSslFilterWithAbsoluteRedirectURL() throws Exception
+    {
+        String servletPath = "/test";
+        String redirectPath = String.format("http://%s:%d/foo", this.serverAddress.getHostName(), this.serverAddress.getPort());
+
+        this.context.addServlet(new ServletHolder(new RedirectServlet(redirectPath)), servletPath);
+        this.server.start();
+
+        HttpURLConnection conn = openConnection(createURL(servletPath));
+
+        assertEquals(302, conn.getResponseCode());
+
+        String location = conn.getHeaderField(HDR_LOCATION);
+        assertTrue(location, location.startsWith(HTTPS));
+    }
+
+    @Test
+    public void testSslFilterWithAbsoluteRedirectURLWithoutScheme() throws Exception
+    {
+        String servletPath = "/test";
+        String redirectPath = String.format("//%s:%d/foo", this.serverAddress.getHostName(), this.serverAddress.getPort());
+
+        this.context.addServlet(new ServletHolder(new RedirectServlet(redirectPath)), servletPath);
+        this.server.start();
+
+        HttpURLConnection conn = openConnection(createURL(servletPath));
+
+        assertEquals(302, conn.getResponseCode());
+
+        String location = conn.getHeaderField(HDR_LOCATION);
+        assertTrue(location, location.startsWith(HTTPS));
+    }
+
+    @Test
+    public void testSslFilterWithAbsoluteRedirectURLWithHttpsScheme() throws Exception
+    {
+        String servletPath = "/test";
+        String redirectPath = String.format("https://%s:%d/foo", this.serverAddress.getHostName(), this.serverAddress.getPort());
+
+        this.context.addServlet(new ServletHolder(new RedirectServlet(redirectPath)), servletPath);
+        this.server.start();
+
+        HttpURLConnection conn = openConnection(createURL(servletPath));
+
+        assertEquals(302, conn.getResponseCode());
+
+        String location = conn.getHeaderField(HDR_LOCATION);
+        assertTrue(location, location.startsWith(HTTPS));
+    }
+
+    private HttpURLConnection openConnection(URL url) throws IOException
+    {
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestProperty(HDR_X_FORWARDED_PROTO, HTTPS);
+        conn.setRequestProperty(HDR_X_FORWARDED_SSL, "on");
+        conn.connect();
+        return conn;
+    }
+
+    private URL createURL(String path) throws MalformedURLException
+    {
+        return new URL(HTTP, this.serverAddress.getHostName(), this.serverAddress.getPort(), path);
+    }
+
+    private static class RedirectServlet extends HttpServlet
+    {
+        private final String redirectPath;
+
+        private RedirectServlet(String redirectPath)
+        {
+            this.redirectPath = redirectPath;
+        }
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            resp.sendRedirect(redirectPath);
+            assertEquals(HTTPS, req.getScheme());
+        }
+    }
+}
\ No newline at end of file

Propchange: felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterJettyTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterResponseTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterResponseTest.java?rev=1635470&r1=1635469&r2=1635470&view=diff
==============================================================================
--- felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterResponseTest.java (original)
+++ felix/trunk/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterResponseTest.java Thu Oct 30 12:07:47 2014
@@ -42,6 +42,8 @@ public class SslFilterResponseTest
 {
     private static final String BACKEND_SERVER = "backend.server";
     private static final String OTHER_SERVER = "other.server";
+    
+    private static final String PATH = "http://localhost:8080/";
 
     private static final String DEFAULT_HTTP_PORT = "80";
     private static final String ALT_HTTP_PORT = "8080";
@@ -54,7 +56,7 @@ public class SslFilterResponseTest
     public void testSetHttpLocationHeaderToNullValue() throws Exception
     {
         TestHttpServletResponse resp = createServletResponse();
-        HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+        HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
 
         SslFilterResponse sresp = new SslFilterResponse(resp, req);
 
@@ -69,7 +71,7 @@ public class SslFilterResponseTest
         String location, expected;
 
         TestHttpServletResponse resp = createServletResponse();
-        HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+        HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
 
         SslFilterResponse sresp = new SslFilterResponse(resp, req);
 
@@ -87,7 +89,7 @@ public class SslFilterResponseTest
         String location, expected;
 
         TestHttpServletResponse resp = createServletResponse();
-        HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+        HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
 
         SslFilterResponse sresp = new SslFilterResponse(resp, req);
 
@@ -105,7 +107,7 @@ public class SslFilterResponseTest
         String location, expected;
 
         TestHttpServletResponse resp = createServletResponse();
-        HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+        HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
 
         SslFilterResponse sresp = new SslFilterResponse(resp, req);
 
@@ -123,7 +125,7 @@ public class SslFilterResponseTest
         String location, expected;
 
         TestHttpServletResponse resp = createServletResponse();
-        HttpServletRequest req = createServletRequest(BACKEND_SERVER, DEFAULT_HTTP_PORT, HTTPS, ALT_HTTPS_PORT);
+        HttpServletRequest req = createServletRequest(BACKEND_SERVER, DEFAULT_HTTP_PORT, HTTPS, ALT_HTTPS_PORT, PATH);
 
         SslFilterResponse sresp = new SslFilterResponse(resp, req);
 
@@ -141,7 +143,7 @@ public class SslFilterResponseTest
         String location, expected;
 
         TestHttpServletResponse resp = createServletResponse();
-        HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+        HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
 
         SslFilterResponse sresp = new SslFilterResponse(resp, req);
 
@@ -157,7 +159,7 @@ public class SslFilterResponseTest
     public void testSetHttpLocationHeaderToOtherRequestURI() throws Exception
     {
         TestHttpServletResponse resp = createServletResponse();
-        HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+        HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
 
         SslFilterResponse sresp = new SslFilterResponse(resp, req);
 
@@ -169,16 +171,17 @@ public class SslFilterResponseTest
         assertEquals(expected, resp.getHeader(LOCATION));
     }
 
-    private HttpServletRequest createServletRequest(String serverName)
+    private HttpServletRequest createServletRequest(String serverName, String requestURL)
     {
-        return createServletRequest(serverName, DEFAULT_HTTP_PORT, HTTPS, DEFAULT_HTTPS_PORT);
+        return createServletRequest(serverName, DEFAULT_HTTP_PORT, HTTPS, DEFAULT_HTTPS_PORT, requestURL);
     }
 
-    private HttpServletRequest createServletRequest(String serverName, String serverPort, String forwardedProto, String forwardedPort)
+    private HttpServletRequest createServletRequest(String serverName, String serverPort, String forwardedProto, String forwardedPort, String requestURL)
     {
         HttpServletRequest req = mock(HttpServletRequest.class);
         when(req.getServerName()).thenReturn(serverName);
         when(req.getServerPort()).thenReturn(Integer.parseInt(serverPort));
+        when(req.getRequestURL()).thenReturn(new StringBuffer(requestURL));
         when(req.getHeader("X-Forwarded-Proto")).thenReturn(forwardedProto);
         when(req.getHeader("X-Forwarded-Port")).thenReturn(forwardedPort);
         return req;