package io.github.ibuildthecloud.gdapi.request.parser; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class DefaultApiRequestParserTest { private static final String DEFAULT_REQUEST_URL = "http://defaulturl/v1"; static DefaultApiRequestParser parser; HttpServletRequest request; @BeforeClass public static void setupClass() { parser = new DefaultApiRequestParser(); parser.setAllowClientOverrideHeaders(true); } @Before public void setup() { request = mock(HttpServletRequest.class); // host header should always be set when(request.getHeader(DefaultApiRequestParser.HOST_HEADER)).thenReturn("hostfoo"); when(request.getRequestURI()).thenReturn("/v1"); StringBuffer defaultUrl = new StringBuffer(DEFAULT_REQUEST_URL); when(request.getRequestURL()).thenReturn(defaultUrl); } @Test public void testXApiRequestUrl() { // Test X-API-Request-url basic use case when(request.getHeader(DefaultApiRequestParser.DEFAULT_OVERRIDE_URL_HEADER)).thenReturn("http://foo:8080/v1"); String url = parser.parseRequestUrl(null, request); assertEquals("http://foo:8080/v1", url); // Test longer request URI when(request.getHeader(DefaultApiRequestParser.DEFAULT_OVERRIDE_URL_HEADER)).thenReturn("http://foo:8080/v1/instances"); url = parser.parseRequestUrl(null, request); assertEquals("http://foo:8080/v1/instances", url); } @Test public void testXApiRequestUrlQueryString() { // Test X-API-Request-url with query string that needs stripped when(request.getHeader(DefaultApiRequestParser.DEFAULT_OVERRIDE_URL_HEADER)).thenReturn("http://foo/v1/instances?bar=true"); String url = parser.parseRequestUrl(null, request); assertEquals("http://foo/v1/instances", url); } @Test public void testXForwardedHost() { // Test x-forwarded-proto + x-f-host + x-f-port basic use case when(request.getHeader(DefaultApiRequestParser.FORWARDED_PROTO_HEADER)).thenReturn("https"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn("foo"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("1234"); String url = parser.parseRequestUrl(null, request); assertEquals("https://foo:1234/v1", url); // Test x-forwarded-proto + x-f-host (no x-f-port) when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn(null); url = parser.parseRequestUrl(null, request); assertEquals("https://foo/v1", url); // Test x-forwarded-proto + x-f-host + x-f-port case where x-f-host also has port and should be overridden when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn("foo:1111"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("1234"); url = parser.parseRequestUrl(null, request); assertEquals("https://foo:1234/v1", url); // Test x-forwarded-proto == https and x-forwarded-port == 443, dont include port in result // This is the typical AWS ELB use case when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn("foo"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("443"); url = parser.parseRequestUrl(null, request); assertEquals("https://foo/v1", url); // Test x-forwarded-proto == http and x-forwarded-port == 80 // This is the typical AWS ELB use case when(request.getHeader(DefaultApiRequestParser.FORWARDED_PROTO_HEADER)).thenReturn("http"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("80"); url = parser.parseRequestUrl(null, request); assertEquals("http://foo/v1", url); // Test longer request URI when(request.getRequestURI()).thenReturn("/v1/instances/"); url = parser.parseRequestUrl(null, request); assertEquals("http://foo/v1/instances/", url); } @Test public void testHost() { // Test x-forwarded-proto + Host + x-f-port basic use case when(request.getHeader(DefaultApiRequestParser.FORWARDED_PROTO_HEADER)).thenReturn("https"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("1234"); String url = parser.parseRequestUrl(null, request); assertEquals("https://hostfoo:1234/v1", url); // Test x-forwarded-proto + Host (no x-f-port) when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn(null); url = parser.parseRequestUrl(null, request); assertEquals("https://hostfoo/v1", url); // Test x-forwarded-proto + Host + x-f-port case where x-f-host also has port and should be overridden when(request.getHeader(DefaultApiRequestParser.FORWARDED_PROTO_HEADER)).thenReturn("https"); when(request.getHeader(DefaultApiRequestParser.HOST_HEADER)).thenReturn("hostfoo:1111"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("1234"); url = parser.parseRequestUrl(null, request); assertEquals("https://hostfoo:1234/v1", url); // Test longer request URI when(request.getRequestURI()).thenReturn("/v1/instances/"); url = parser.parseRequestUrl(null, request); assertEquals("https://hostfoo:1234/v1/instances/", url); } @Test public void testNoXForwardedProto() { // Test no x-forwarded-proto present but x-f-host/Host is present when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn("foo"); String url = parser.parseRequestUrl(null, request); assertEquals(DEFAULT_REQUEST_URL, url); // Test no x-forwarded-proto present but x-f-host/Host is present when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn(null); url = parser.parseRequestUrl(null, request); assertEquals(DEFAULT_REQUEST_URL, url); } @Test public void testIPv6() { // Test for x-f-host == [::1] (no port in host header) when(request.getHeader(DefaultApiRequestParser.FORWARDED_PROTO_HEADER)).thenReturn("https"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn("[::1]"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("1234"); String url = parser.parseRequestUrl(null, request); assertEquals("https://[::1]:1234/v1", url); // Same thing, but with host header when(request.getHeader(DefaultApiRequestParser.HOST_HEADER)).thenReturn("[::1]"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn(null); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("1234"); url = parser.parseRequestUrl(null, request); assertEquals("https://[::1]:1234/v1", url); // Properly override the port that's in the x-f-host header when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn("[::1]:1111"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("1234"); url = parser.parseRequestUrl(null, request); assertEquals("https://[::1]:1234/v1", url); // Same thing, but with host header when(request.getHeader(DefaultApiRequestParser.HOST_HEADER)).thenReturn("[::1]:1111"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn(null); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn("1234"); url = parser.parseRequestUrl(null, request); assertEquals("https://[::1]:1234/v1", url); // No x-f-port header when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn("[::1]"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_PORT_HEADER)).thenReturn(null); url = parser.parseRequestUrl(null, request); assertEquals("https://[::1]/v1", url); // Same thing, but with host header when(request.getHeader(DefaultApiRequestParser.HOST_HEADER)).thenReturn("[::1]"); when(request.getHeader(DefaultApiRequestParser.FORWARDED_HOST_HEADER)).thenReturn(null); url = parser.parseRequestUrl(null, request); assertEquals("https://[::1]/v1", url); } }