blob: cebc0feb63ba7636f6199521f2eef03323d2be63 [file] [log] [blame]
/*
* 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 junit.framework.Assert.assertEquals;
import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_X_FORWARDED_PROTO;
import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTP;
import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTPS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.http.sslfilter.internal.SslFilter.ConfigHolder;
import org.junit.Test;
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://" + BACKEND_SERVER + "/foo";
private static final String DEFAULT_HTTP_PORT = "80";
private static final String ALT_HTTP_PORT = "8080";
private static final String DEFAULT_HTTPS_PORT = "443";
private static final String ALT_HTTPS_PORT = "8443";
private static final String LOCATION = "Location";
@Test
public void testSetHttpLocationHeaderToNullValue() throws Exception
{
TestHttpServletResponse resp = createServletResponse();
HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
ConfigHolder cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
SslFilterResponse sresp = new SslFilterResponse(resp, req, cfg);
sresp.setHeader(LOCATION, null);
assertEquals(null, resp.getHeader(LOCATION));
}
@Test
public void testSetHttpsLocationHeaderToOriginalRequestURI() throws Exception
{
String location, expected;
TestHttpServletResponse resp = createServletResponse();
HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
ConfigHolder cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
SslFilterResponse sresp = new SslFilterResponse(resp, req, cfg);
location = HTTPS + "://" + BACKEND_SERVER + "/foo";
expected = location;
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
req = createServletRequest(BACKEND_SERVER, PATH);
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + "/foo";
expected = HTTPS + "://" + BACKEND_SERVER + "/foo";
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
}
@Test
public void testSetHttpLocationHeaderToOriginalRequestURI() throws Exception
{
String location, expected;
TestHttpServletResponse resp = createServletResponse();
HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
ConfigHolder cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
SslFilterResponse sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + "/foo";
expected = location;
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + "/foo";
expected = HTTPS + "://" + BACKEND_SERVER + "/foo";
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
}
@Test
public void testSetHttpLocationHeaderToOriginalRequestWithExplicitPort() throws Exception
{
String location, expected;
TestHttpServletResponse resp = createServletResponse();
HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
ConfigHolder cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
SslFilterResponse sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + ":" + DEFAULT_HTTP_PORT + "/foo";
expected = HTTP + "://" + BACKEND_SERVER + ":" + DEFAULT_HTTP_PORT + "/foo";
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
resp = createServletResponse();
req = createServletRequest(BACKEND_SERVER, PATH);
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + ":" + DEFAULT_HTTP_PORT + "/foo";
expected = HTTPS + "://" + BACKEND_SERVER + "/foo";
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
}
@Test
public void testSetHttpLocationHeaderToOriginalRequestWithForwardedPort() throws Exception
{
String location, expected;
TestHttpServletResponse resp = createServletResponse();
HttpServletRequest req = createServletRequest(BACKEND_SERVER, DEFAULT_HTTP_PORT, HTTPS, ALT_HTTPS_PORT, PATH);
ConfigHolder cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
SslFilterResponse sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + "/foo";
expected = HTTP + "://" + BACKEND_SERVER + "/foo";
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
resp = createServletResponse();
req = createServletRequest(BACKEND_SERVER, DEFAULT_HTTP_PORT, HTTPS, ALT_HTTPS_PORT, PATH);
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + "/foo";
expected = HTTPS + "://" + BACKEND_SERVER + ":" + ALT_HTTPS_PORT + "/foo";
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
}
@Test
public void testSetHttpLocationHeaderToOriginalRequestWithDifferentPort() throws Exception
{
String location, expected;
TestHttpServletResponse resp = createServletResponse();
HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
ConfigHolder cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
SslFilterResponse sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + ":" + ALT_HTTP_PORT + "/foo";
expected = location;
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
resp = createServletResponse();
req = createServletRequest(BACKEND_SERVER, PATH);
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(resp, req, cfg);
location = HTTP + "://" + BACKEND_SERVER + ":" + ALT_HTTP_PORT + "/foo";
expected = location;
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
}
@Test
public void testSetHttpLocationHeaderToOtherRequestURI() throws Exception
{
TestHttpServletResponse resp = createServletResponse();
HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
ConfigHolder cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
SslFilterResponse sresp = new SslFilterResponse(resp, req,cfg);
String location = HTTP + "://" + OTHER_SERVER + "/foo";
String expected = location;
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
resp = createServletResponse();
req = createServletRequest(BACKEND_SERVER, PATH);
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(resp, req,cfg);
location = HTTP + "://" + OTHER_SERVER + "/foo";
expected = location;
sresp.setHeader(LOCATION, location);
assertEquals(expected, resp.getHeader(LOCATION));
}
@Test
public void testFragment() throws Exception
{
test("/foo#abc");
}
@Test
public void testQueryString() throws Exception
{
final String queryString = "?resource=%2Fen.html%3FpbOpen%3Dtrue&$$login$$=%24%24login%24%24&j_reason=errors.login.account.not.found";
test("/" + queryString);
}
@Test
public void testPathEncoding() throws Exception
{
test("/apps/test/content/%E4%B8%83%E6%9C%88%E5%8F%B7.redirect");
}
private void test(final String path) throws Exception
{
TestHttpServletResponse response = createServletResponse();
HttpServletRequest req = createServletRequest(BACKEND_SERVER, PATH);
// test - don't rewrite absolute urls / absolute http url / sendRedirect
// expected: no rewrite
ConfigHolder cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
SslFilterResponse sresp = new SslFilterResponse(response, req, cfg);
sresp.sendRedirect("http://" + BACKEND_SERVER + path);
assertEquals("http://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - don't rewrite absolute urls / absolute http url / setHeader
// expected: no rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
sresp = new SslFilterResponse(response, req, cfg);
sresp.setHeader(SslFilterConstants.HDR_LOCATION, "http://" + BACKEND_SERVER + path);
assertEquals("http://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - don't rewrite absolute urls / absolute https url / sendRedirect
// expected: no rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
sresp = new SslFilterResponse(response, req, cfg);
sresp.sendRedirect("https://" + BACKEND_SERVER + path);
assertEquals("https://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - don't rewrite absolute urls / absolute https url / setHeader
// expected: no rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
sresp = new SslFilterResponse(response, req, cfg);
sresp.setHeader(SslFilterConstants.HDR_LOCATION, "https://" + BACKEND_SERVER + path);
assertEquals("https://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - rewrite absolute urls / absolute http url / sendRedirect
// expected: rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(response, req, cfg);
sresp.sendRedirect("http://" + BACKEND_SERVER + path);
assertEquals("https://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - rewrite absolute urls / absolute http url / setHeader
// expected: rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(response, req, cfg);
sresp.setHeader(SslFilterConstants.HDR_LOCATION, "http://" + BACKEND_SERVER + path);
assertEquals("https://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - rewrite absolute urls / absolute https url / sendRedirect
// expected: no rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(response, req, cfg);
sresp.sendRedirect("https://" + BACKEND_SERVER + path);
assertEquals("https://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - rewrite absolute urls / absolute https url / setHeader
// expected: no rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(response, req, cfg);
sresp.setHeader(SslFilterConstants.HDR_LOCATION, "https://" + BACKEND_SERVER + path);
assertEquals("https://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - don't rewrite absolute urls / relative path / setHeader
// expected: rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, false);
sresp = new SslFilterResponse(response, req, cfg);
sresp.sendRedirect(path);
assertEquals("https://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
// test - rewrite absolute urls / relative path / sendRedirect
// expected: rewrite
cfg = new ConfigHolder(HDR_X_FORWARDED_PROTO, "https", null, true);
sresp = new SslFilterResponse(response, req, cfg);
sresp.setHeader(SslFilterConstants.HDR_LOCATION, path);
assertEquals("https://" + BACKEND_SERVER + path, sresp.getHeader(SslFilterConstants.HDR_LOCATION));
}
private HttpServletRequest createServletRequest(String serverName, String requestURL)
{
return createServletRequest(serverName, DEFAULT_HTTP_PORT, HTTPS, DEFAULT_HTTPS_PORT, requestURL);
}
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;
}
private TestHttpServletResponse createServletResponse()
{
return new TestHttpServletResponse();
}
private static class TestHttpServletResponse implements HttpServletResponse
{
private final Map<String, String> headers = new HashMap<String, String>();
private int status = -1;
private boolean committed = false;
@Override
public void setLocale(Locale loc)
{
throw new UnsupportedOperationException();
}
@Override
public void setContentType(String type)
{
throw new UnsupportedOperationException();
}
@Override
public void setContentLength(int len)
{
throw new UnsupportedOperationException();
}
@Override
public void setContentLengthLong(long len)
{
throw new UnsupportedOperationException();
}
@Override
public void setCharacterEncoding(String charset)
{
throw new UnsupportedOperationException();
}
@Override
public void setBufferSize(int size)
{
throw new UnsupportedOperationException();
}
@Override
public void resetBuffer()
{
}
@Override
public void reset()
{
}
@Override
public boolean isCommitted()
{
return this.committed;
}
@Override
public PrintWriter getWriter() throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public ServletOutputStream getOutputStream() throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public Locale getLocale()
{
throw new UnsupportedOperationException();
}
@Override
public String getContentType()
{
throw new UnsupportedOperationException();
}
@Override
public String getCharacterEncoding()
{
throw new UnsupportedOperationException();
}
@Override
public int getBufferSize()
{
throw new UnsupportedOperationException();
}
@Override
public void flushBuffer() throws IOException
{
committed = true;
}
@SuppressWarnings("deprecation")
@Override
public void setStatus(int sc, String sm)
{
status = sc;
committed = true;
}
@Override
public void setStatus(int sc)
{
status = sc;
committed = true;
}
@Override
public void setIntHeader(String name, int value)
{
headers.put(name, Integer.toString(value));
}
@Override
public void setHeader(String name, String value)
{
headers.put(name, value);
}
@Override
public void setDateHeader(String name, long date)
{
throw new UnsupportedOperationException();
}
@Override
public void sendRedirect(String location) throws IOException
{
this.setHeader(SslFilterConstants.HDR_LOCATION, location);
}
@Override
public void sendError(int sc, String msg) throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public void sendError(int sc) throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public int getStatus()
{
return status;
}
@Override
public Collection<String> getHeaders(String name)
{
return Collections.singleton(headers.get(name));
}
@Override
public Collection<String> getHeaderNames()
{
return headers.keySet();
}
@Override
public String getHeader(String name)
{
return headers.get(name);
}
@SuppressWarnings("deprecation")
@Override
public String encodeUrl(String url)
{
throw new UnsupportedOperationException();
}
@Override
public String encodeURL(String url)
{
throw new UnsupportedOperationException();
}
@SuppressWarnings("deprecation")
@Override
public String encodeRedirectUrl(String url)
{
throw new UnsupportedOperationException();
}
@Override
public String encodeRedirectURL(String url)
{
throw new UnsupportedOperationException();
}
@Override
public boolean containsHeader(String name)
{
return headers.containsKey(name);
}
@Override
public void addIntHeader(String name, int value)
{
throw new UnsupportedOperationException();
}
@Override
public void addHeader(String name, String value)
{
throw new UnsupportedOperationException();
}
@Override
public void addDateHeader(String name, long date)
{
throw new UnsupportedOperationException();
}
@Override
public void addCookie(Cookie cookie)
{
throw new UnsupportedOperationException();
}
}
}