diff --git a/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java index ddafbe2cf6a..dac3ab709f1 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java @@ -17,6 +17,7 @@ package org.springframework.web.filter; import java.io.IOException; +import java.net.InetSocketAddress; import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -24,7 +25,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.Supplier; -import java.util.regex.Pattern; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -33,9 +33,8 @@ import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; @@ -220,10 +219,6 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { */ private static class ForwardedHeaderExtractingRequest extends ForwardedHeaderRemovingRequest { - private static final String X_FORWARDED_FOR_HEADER = "X-Forwarded-For"; - private static final String FORWARDED_HEADER = "Forwarded"; - private static final Pattern FORWARDED_FOR_PATTERN = Pattern.compile("(?i:^[^,]*for=.+)"); - @Nullable private final String scheme; @@ -235,21 +230,16 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { private final int port; @Nullable - private final String remoteHost; - - @Nullable - private final String remoteAddr; - - private final int remotePort; + private final InetSocketAddress remoteAddress; private final ForwardedPrefixExtractor forwardedPrefixExtractor; - ForwardedHeaderExtractingRequest(HttpServletRequest request, UrlPathHelper pathHelper) { - super(request); + ForwardedHeaderExtractingRequest(HttpServletRequest servletRequest, UrlPathHelper pathHelper) { + super(servletRequest); - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents uriComponents = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); + ServerHttpRequest request = new ServletServerHttpRequest(servletRequest); + UriComponents uriComponents = UriComponentsBuilder.fromHttpRequest(request).build(); int port = uriComponents.getPort(); this.scheme = uriComponents.getScheme(); @@ -257,24 +247,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { this.host = uriComponents.getHost(); this.port = (port == -1 ? (this.secure ? 443 : 80) : port); - HttpHeaders headers = httpRequest.getHeaders(); - boolean hasForwardedFor = StringUtils.hasText(headers.getFirst(X_FORWARDED_FOR_HEADER)) || - (StringUtils.hasText(headers.getFirst(FORWARDED_HEADER)) && - FORWARDED_FOR_PATTERN.matcher(headers.getFirst(FORWARDED_HEADER)).matches()); - if (hasForwardedFor) { - UriComponents remoteUriComponents = UriComponentsBuilder.newInstance() - .host(request.getRemoteHost()) - .port(request.getRemotePort()) - .adaptFromForwardedForHeader(headers) - .build(); - this.remoteHost = remoteUriComponents.getHost(); - this.remoteAddr = this.remoteHost; - this.remotePort = remoteUriComponents.getPort(); - } else { - this.remoteHost = request.getRemoteHost(); - this.remoteAddr = request.getRemoteAddr(); - this.remotePort = request.getRemotePort(); - } + this.remoteAddress = UriComponentsBuilder.parseForwardedFor(request, request.getRemoteAddress()); String baseUrl = this.scheme + "://" + this.host + (port == -1 ? "" : ":" + port); Supplier delegateRequest = () -> (HttpServletRequest) getRequest(); @@ -322,18 +295,18 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { @Override @Nullable public String getRemoteHost() { - return this.remoteHost; + return (this.remoteAddress != null ? this.remoteAddress.getHostString() : super.getRemoteHost()); } @Override @Nullable public String getRemoteAddr() { - return this.remoteAddr; + return (this.remoteAddress != null ? this.remoteAddress.getHostString() : super.getRemoteAddr()); } @Override public int getRemotePort() { - return remotePort; + return (this.remoteAddress != null ? this.remoteAddress.getPort() : super.getRemotePort()); } } diff --git a/spring-web/src/main/java/org/springframework/web/server/adapter/ForwardedHeaderTransformer.java b/spring-web/src/main/java/org/springframework/web/server/adapter/ForwardedHeaderTransformer.java index 34ac8ef8bc5..a34cef914eb 100644 --- a/spring-web/src/main/java/org/springframework/web/server/adapter/ForwardedHeaderTransformer.java +++ b/spring-web/src/main/java/org/springframework/web/server/adapter/ForwardedHeaderTransformer.java @@ -22,7 +22,6 @@ import java.util.Collections; import java.util.Locale; import java.util.Set; import java.util.function.Function; -import java.util.regex.Pattern; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; @@ -30,7 +29,6 @@ import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.Nullable; import org.springframework.util.LinkedCaseInsensitiveMap; import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** @@ -53,9 +51,6 @@ import org.springframework.web.util.UriComponentsBuilder; */ public class ForwardedHeaderTransformer implements Function { - private static final String X_FORWARDED_FOR_HEADER = "X-Forwarded-For"; - private static final String FORWARDED_HEADER = "Forwarded"; - private static final Pattern FORWARDED_FOR_PATTERN = Pattern.compile("(?i:^[^,]*for=.+)"); static final Set FORWARDED_HEADER_NAMES = Collections.newSetFromMap(new LinkedCaseInsensitiveMap<>(10, Locale.ENGLISH)); @@ -108,24 +103,8 @@ public class ForwardedHeaderTransformer implements FunctionRFC 7239, Section 5.2 + */ + @Nullable + public static InetSocketAddress parseForwardedFor( + HttpRequest request, @Nullable InetSocketAddress remoteAddress) { + + int port = (remoteAddress != null ? + remoteAddress.getPort() : "https".equals(request.getURI().getScheme()) ? 443 : 80); + + String forwardedHeader = request.getHeaders().getFirst("Forwarded"); + if (StringUtils.hasText(forwardedHeader)) { + String forwardedToUse = StringUtils.tokenizeToStringArray(forwardedHeader, ",")[0]; + Matcher matcher = FORWARDED_FOR_PATTERN.matcher(forwardedToUse); + if (matcher.find()) { + String value = matcher.group(1).trim(); + String host = value; + int portSeparatorIdx = value.lastIndexOf(':'); + if (portSeparatorIdx > value.lastIndexOf(']')) { + host = value.substring(0, portSeparatorIdx); + port = Integer.parseInt(value.substring(portSeparatorIdx + 1)); + } + return new InetSocketAddress(host, port); + } + } + + String forHeader = request.getHeaders().getFirst("X-Forwarded-For"); + if (StringUtils.hasText(forHeader)) { + String host = StringUtils.tokenizeToStringArray(forHeader, ",")[0]; + return new InetSocketAddress(host, port); + } + + return null; + } + /** * Create an instance by parsing the "Origin" header of an HTTP request. * @see RFC 6454 @@ -727,33 +771,6 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { return this; } - /** - * Adapt this builders's host+port from the "for" parameter of the "Forwarded" - * header or from "X-Forwarded-For" if "Forwarded" is not found. If neither - * "Forwarded" nor "X-Forwarded-For" is found no changes are made to the - * builder. - * @param headers the HTTP headers to consider - * @return this UriComponentsBuilder - */ - public UriComponentsBuilder adaptFromForwardedForHeader(HttpHeaders headers) { - String forwardedHeader = headers.getFirst("Forwarded"); - if (StringUtils.hasText(forwardedHeader)) { - String forwardedToUse = StringUtils.tokenizeToStringArray(forwardedHeader, ",")[0]; - Matcher matcher = FORWARDED_FOR_PATTERN.matcher(forwardedToUse); - if (matcher.find()) { - adaptForwardedForHost(matcher.group(1).trim()); - } - } - else { - String forHeader = headers.getFirst("X-Forwarded-For"); - if (StringUtils.hasText(forHeader)) { - String forwardedForToUse = StringUtils.tokenizeToStringArray(forHeader, ",")[0]; - host(forwardedForToUse); - } - } - return this; - } - /** * Adapt this builder's scheme+host+port from the given headers, specifically * "Forwarded" (RFC 7239, @@ -828,36 +845,18 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { return StringUtils.hasText(forwardedSsl) && forwardedSsl.equalsIgnoreCase("on"); } - private void adaptForwardedHost(String hostToUse) { - int portSeparatorIdx = hostToUse.lastIndexOf(':'); - if (portSeparatorIdx > hostToUse.lastIndexOf(']')) { - host(hostToUse.substring(0, portSeparatorIdx)); - port(Integer.parseInt(hostToUse.substring(portSeparatorIdx + 1))); + private void adaptForwardedHost(String rawValue) { + int portSeparatorIdx = rawValue.lastIndexOf(':'); + if (portSeparatorIdx > rawValue.lastIndexOf(']')) { + host(rawValue.substring(0, portSeparatorIdx)); + port(Integer.parseInt(rawValue.substring(portSeparatorIdx + 1))); } else { - host(hostToUse); + host(rawValue); port(null); } } - private void adaptForwardedForHost(String hostToUse) { - String hostName = hostToUse; - int portSeparatorIdx = hostToUse.lastIndexOf(':'); - if (portSeparatorIdx > hostToUse.lastIndexOf(']')) { - String hostPort = hostToUse.substring(portSeparatorIdx + 1); - // check if port is not obfuscated - if (hostPort.matches(FORWARDED_FOR_NUMERIC_PORT_PATTERN)) { - port(Integer.parseInt(hostPort)); - } - hostName = hostToUse.substring(0, portSeparatorIdx); - } - if (hostName.matches(HOST_IPV6_PATTERN)) { - host(hostName.substring(hostName.indexOf('[') + 1, hostName.indexOf(']'))); - } else { - host(hostName); - } - } - private void resetHierarchicalComponents() { this.userInfo = null; this.host = null; diff --git a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java index d5afe96f30e..1c42f0ea8b9 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java @@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.web.testfixture.servlet.MockFilterChain; @@ -46,290 +47,35 @@ import static org.mockito.Mockito.mock; */ public class ForwardedHeaderFilterTests { + private static final String FORWARDED = "forwarded"; + private static final String X_FORWARDED_PROTO = "x-forwarded-proto"; // SPR-14372 (case insensitive) private static final String X_FORWARDED_HOST = "x-forwarded-host"; private static final String X_FORWARDED_PORT = "x-forwarded-port"; + private static final String X_FORWARDED_SSL = "x-forwarded-ssl"; + private static final String X_FORWARDED_PREFIX = "x-forwarded-prefix"; - private static final String X_FORWARDED_SSL = "x-forwarded-ssl"; private static final String X_FORWARDED_FOR = "x-forwarded-for"; - private static final String FORWARDED = "forwarded"; private final ForwardedHeaderFilter filter = new ForwardedHeaderFilter(); private MockHttpServletRequest request; - private MockFilterChain filterChain; + private final MockFilterChain filterChain = new MockFilterChain(new HttpServlet() {}); @BeforeEach @SuppressWarnings("serial") - public void setup() throws Exception { + public void setup() { this.request = new MockHttpServletRequest(); this.request.setScheme("http"); this.request.setServerName("localhost"); this.request.setServerPort(80); - this.filterChain = new MockFilterChain(new HttpServlet() {}); - } - - @Test - public void forwardedForEmpty() throws Exception { - this.request.addHeader(X_FORWARDED_FOR, ""); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(MockHttpServletRequest.DEFAULT_REMOTE_ADDR); - assertThat(actual.getRemoteHost()).isEqualTo(MockHttpServletRequest.DEFAULT_REMOTE_HOST); - assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); - } - - @Test - public void forwardedForSingleIdentifier() throws Exception { - this.request.addHeader(X_FORWARDED_FOR, "203.0.113.195"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); - assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); - } - - @Test - public void forwardedForMultipleIdentifiers() throws Exception { - this.request.addHeader(X_FORWARDED_FOR, "203.0.113.195, 70.41.3.18, 150.172.238.178"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); - assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); - } - - @Test - public void standardizedForwardedForIpV4Identifier() throws Exception { - this.request.addHeader(FORWARDED, "for=203.0.113.195"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); - assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); - } - - @Test - public void standardizedForwardedForIpV6Identifier() throws Exception { - this.request.addHeader(FORWARDED, "for=\"[2001:db8:cafe::17]\""); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("2001:db8:cafe::17"); - assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); - } - - @Test - public void standardizedForwardedForUnknownIdentifier() throws Exception { - this.request.addHeader(FORWARDED, "for=unknown"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("unknown"); - assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); - } - - @Test - public void standardizedForwardedForObfuscatedIdentifier() throws Exception { - this.request.addHeader(FORWARDED, "for=_abc-12_d.e"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("_abc-12_d.e"); - assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); - } - - @Test - public void standardizedForwardedForIpV4IdentifierWithPort() throws Exception { - this.request.addHeader(FORWARDED, "for=\"203.0.113.195:47011\""); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); - assertThat(actual.getRemotePort()).isEqualTo(47011); - } - - @Test - public void standardizedForwardedForIpV6IdentifierWithPort() throws Exception { - this.request.addHeader(FORWARDED, "For=\"[2001:db8:cafe::17]:47011\""); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("2001:db8:cafe::17"); - assertThat(actual.getRemotePort()).isEqualTo(47011); - } - - @Test - public void standardizedForwardedForUnknownIdentifierWithPort() throws Exception { - this.request.addHeader(FORWARDED, "for=\"unknown:47011\""); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("unknown"); - assertThat(actual.getRemotePort()).isEqualTo(47011); - } - - @Test - public void standardizedForwardedForObfuscatedIdentifierWithPort() throws Exception { - this.request.addHeader(FORWARDED, "for=\"_abc-12_d.e:47011\""); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("_abc-12_d.e"); - assertThat(actual.getRemotePort()).isEqualTo(47011); - } - - @Test - public void standardizedForwardedForMultipleIdentifiers() throws Exception { - this.request.addHeader(FORWARDED, "for=203.0.113.195;proto=http, for=\"[2001:db8:cafe::17]\", for=unknown"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); - assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); - } - - @Test - public void contextPathEmpty() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, ""); - assertThat(filterAndGetContextPath()).isEqualTo(""); - } - - @Test - public void contextPathWithTrailingSlash() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/foo/bar/"); - assertThat(filterAndGetContextPath()).isEqualTo("/foo/bar"); - } - - @Test - public void contextPathWithTrailingSlashes() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/foo/bar/baz///"); - assertThat(filterAndGetContextPath()).isEqualTo("/foo/bar/baz"); - } - - @Test - public void contextPathWithForwardedPrefix() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/prefix"); - this.request.setContextPath("/mvc-showcase"); - - String actual = filterAndGetContextPath(); - assertThat(actual).isEqualTo("/prefix"); - } - - @Test - public void contextPathWithForwardedPrefixTrailingSlash() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/prefix/"); - this.request.setContextPath("/mvc-showcase"); - - String actual = filterAndGetContextPath(); - assertThat(actual).isEqualTo("/prefix"); - } - - private String filterAndGetContextPath() throws ServletException, IOException { - return filterAndGetWrappedRequest().getContextPath(); - } - - private HttpServletRequest filterAndGetWrappedRequest() throws ServletException, IOException { - MockHttpServletResponse response = new MockHttpServletResponse(); - this.filter.doFilterInternal(this.request, response, this.filterChain); - return (HttpServletRequest) this.filterChain.getRequest(); - } - - - @Test - public void contextPathPreserveEncoding() throws Exception { - this.request.setContextPath("/app%20"); - this.request.setRequestURI("/app%20/path/"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getContextPath()).isEqualTo("/app%20"); - assertThat(actual.getRequestURI()).isEqualTo("/app%20/path/"); - assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/app%20/path/"); - } - - @Test - public void requestUri() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/"); - this.request.setContextPath("/app"); - this.request.setRequestURI("/app/path"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getContextPath()).isEqualTo(""); - assertThat(actual.getRequestURI()).isEqualTo("/path"); - } - - @Test - public void requestUriWithTrailingSlash() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/"); - this.request.setContextPath("/app"); - this.request.setRequestURI("/app/path/"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getContextPath()).isEqualTo(""); - assertThat(actual.getRequestURI()).isEqualTo("/path/"); - } - - @Test - public void requestUriPreserveEncoding() throws Exception { - this.request.setContextPath("/app"); - this.request.setRequestURI("/app/path%20with%20spaces/"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getContextPath()).isEqualTo("/app"); - assertThat(actual.getRequestURI()).isEqualTo("/app/path%20with%20spaces/"); - assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/app/path%20with%20spaces/"); - } - - @Test - public void requestUriEqualsContextPath() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/"); - this.request.setContextPath("/app"); - this.request.setRequestURI("/app"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getContextPath()).isEqualTo(""); - assertThat(actual.getRequestURI()).isEqualTo("/"); - } - - @Test - public void requestUriRootUrl() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/"); - this.request.setContextPath("/app"); - this.request.setRequestURI("/app/"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getContextPath()).isEqualTo(""); - assertThat(actual.getRequestURI()).isEqualTo("/"); - } - - @Test - public void requestUriPreserveSemicolonContent() throws Exception { - this.request.setContextPath(""); - this.request.setRequestURI("/path;a=b/with/semicolon"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getContextPath()).isEqualTo(""); - assertThat(actual.getRequestURI()).isEqualTo("/path;a=b/with/semicolon"); - assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/path;a=b/with/semicolon"); - } - - @Test - public void caseInsensitiveForwardedPrefix() throws Exception { - this.request = new MockHttpServletRequest() { - - @Override // SPR-14372: make it case-sensitive - public String getHeader(String header) { - Enumeration names = getHeaderNames(); - while (names.hasMoreElements()) { - String name = names.nextElement(); - if (name.equals(header)) { - return super.getHeader(header); - } - } - return null; - } - }; - this.request.addHeader(X_FORWARDED_PREFIX, "/prefix"); - this.request.setRequestURI("/path"); - HttpServletRequest actual = filterAndGetWrappedRequest(); - - assertThat(actual.getRequestURI()).isEqualTo("/prefix/path"); } @Test @@ -339,6 +85,7 @@ public class ForwardedHeaderFilterTests { testShouldFilter(X_FORWARDED_PORT); testShouldFilter(X_FORWARDED_PROTO); testShouldFilter(X_FORWARDED_SSL); + testShouldFilter(X_FORWARDED_PREFIX); testShouldFilter(X_FORWARDED_FOR); } @@ -365,6 +112,7 @@ public class ForwardedHeaderFilterTests { this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain); HttpServletRequest actual = (HttpServletRequest) this.filterChain.getRequest(); + assertThat(actual).isNotNull(); assertThat(actual.getRequestURL().toString()).isEqualTo("https://84.198.58.199/mvc-showcase"); assertThat(actual.getScheme()).isEqualTo("https"); assertThat(actual.getServerName()).isEqualTo("84.198.58.199"); @@ -393,6 +141,7 @@ public class ForwardedHeaderFilterTests { this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain); HttpServletRequest actual = (HttpServletRequest) this.filterChain.getRequest(); + assertThat(actual).isNotNull(); assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/mvc-showcase"); assertThat(actual.getScheme()).isEqualTo("http"); assertThat(actual.getServerName()).isEqualTo("localhost"); @@ -420,6 +169,7 @@ public class ForwardedHeaderFilterTests { this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain); HttpServletRequest actual = (HttpServletRequest) this.filterChain.getRequest(); + assertThat(actual).isNotNull(); assertThat(actual.getRequestURL().toString()).isEqualTo("https://84.198.58.199/mvc-showcase"); assertThat(actual.getScheme()).isEqualTo("https"); assertThat(actual.getServerName()).isEqualTo("84.198.58.199"); @@ -454,206 +204,435 @@ public class ForwardedHeaderFilterTests { assertThat(actual.getRequestURL().toString()).isEqualTo("https://www.mycompany.example/bar"); } - @Test - public void requestUriWithForwardedPrefix() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/prefix"); - this.request.setRequestURI("/mvc-showcase"); + @Nested + class ForwardedPrefix { - HttpServletRequest actual = filterAndGetWrappedRequest(); - assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/prefix/mvc-showcase"); + @Test + public void contextPathEmpty() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, ""); + assertThat(filterAndGetContextPath()).isEqualTo(""); + } + + @Test + public void contextPathWithTrailingSlash() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/foo/bar/"); + assertThat(filterAndGetContextPath()).isEqualTo("/foo/bar"); + } + + @Test + public void contextPathWithTrailingSlashes() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/foo/bar/baz///"); + assertThat(filterAndGetContextPath()).isEqualTo("/foo/bar/baz"); + } + + @Test + public void contextPathWithForwardedPrefix() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/prefix"); + request.setContextPath("/mvc-showcase"); + + String actual = filterAndGetContextPath(); + assertThat(actual).isEqualTo("/prefix"); + } + + @Test + public void contextPathWithForwardedPrefixTrailingSlash() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/prefix/"); + request.setContextPath("/mvc-showcase"); + + String actual = filterAndGetContextPath(); + assertThat(actual).isEqualTo("/prefix"); + } + + private String filterAndGetContextPath() throws ServletException, IOException { + return filterAndGetWrappedRequest().getContextPath(); + } + + @Test + public void contextPathPreserveEncoding() throws Exception { + request.setContextPath("/app%20"); + request.setRequestURI("/app%20/path/"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getContextPath()).isEqualTo("/app%20"); + assertThat(actual.getRequestURI()).isEqualTo("/app%20/path/"); + assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/app%20/path/"); + } + + @Test + public void requestUri() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/"); + request.setContextPath("/app"); + request.setRequestURI("/app/path"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getContextPath()).isEqualTo(""); + assertThat(actual.getRequestURI()).isEqualTo("/path"); + } + + @Test + public void requestUriWithTrailingSlash() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/"); + request.setContextPath("/app"); + request.setRequestURI("/app/path/"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getContextPath()).isEqualTo(""); + assertThat(actual.getRequestURI()).isEqualTo("/path/"); + } + + @Test + public void requestUriPreserveEncoding() throws Exception { + request.setContextPath("/app"); + request.setRequestURI("/app/path%20with%20spaces/"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getContextPath()).isEqualTo("/app"); + assertThat(actual.getRequestURI()).isEqualTo("/app/path%20with%20spaces/"); + assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/app/path%20with%20spaces/"); + } + + @Test + public void requestUriEqualsContextPath() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/"); + request.setContextPath("/app"); + request.setRequestURI("/app"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getContextPath()).isEqualTo(""); + assertThat(actual.getRequestURI()).isEqualTo("/"); + } + + @Test + public void requestUriRootUrl() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/"); + request.setContextPath("/app"); + request.setRequestURI("/app/"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getContextPath()).isEqualTo(""); + assertThat(actual.getRequestURI()).isEqualTo("/"); + } + + @Test + public void requestUriPreserveSemicolonContent() throws Exception { + request.setContextPath(""); + request.setRequestURI("/path;a=b/with/semicolon"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getContextPath()).isEqualTo(""); + assertThat(actual.getRequestURI()).isEqualTo("/path;a=b/with/semicolon"); + assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/path;a=b/with/semicolon"); + } + + @Test + public void caseInsensitiveForwardedPrefix() throws Exception { + request = new MockHttpServletRequest() { + + @Override // SPR-14372: make it case-sensitive + public String getHeader(String header) { + Enumeration names = getHeaderNames(); + while (names.hasMoreElements()) { + String name = names.nextElement(); + if (name.equals(header)) { + return super.getHeader(header); + } + } + return null; + } + }; + request.addHeader(X_FORWARDED_PREFIX, "/prefix"); + request.setRequestURI("/path"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRequestURI()).isEqualTo("/prefix/path"); + } + + @Test + public void requestUriWithForwardedPrefix() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/prefix"); + request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/prefix/mvc-showcase"); + } + + @Test + public void requestUriWithForwardedPrefixTrailingSlash() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/prefix/"); + request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/prefix/mvc-showcase"); + } + + @Test + void shouldConcatenatePrefixes() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/first,/second"); + request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/first/second/mvc-showcase"); + } + + @Test + void shouldConcatenatePrefixesWithTrailingSlashes() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/first/,/second//"); + request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/first/second/mvc-showcase"); + } + + @Test + public void requestURLNewStringBuffer() throws Exception { + request.addHeader(X_FORWARDED_PREFIX, "/prefix/"); + request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + actual.getRequestURL().append("?key=value"); + assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/prefix/mvc-showcase"); + } } - @Test - public void requestUriWithForwardedPrefixTrailingSlash() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/prefix/"); - this.request.setRequestURI("/mvc-showcase"); + @Nested + class ForwardedFor { - HttpServletRequest actual = filterAndGetWrappedRequest(); - assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/prefix/mvc-showcase"); + @Test + public void xForwardedForEmpty() throws Exception { + request.addHeader(X_FORWARDED_FOR, ""); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRemoteAddr()).isEqualTo(MockHttpServletRequest.DEFAULT_REMOTE_ADDR); + assertThat(actual.getRemoteHost()).isEqualTo(MockHttpServletRequest.DEFAULT_REMOTE_HOST); + assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); + } + + @Test + public void xForwardedForSingleIdentifier() throws Exception { + request.addHeader(X_FORWARDED_FOR, "203.0.113.195"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); + assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); + } + + @Test + public void xForwardedForMultipleIdentifiers() throws Exception { + request.addHeader(X_FORWARDED_FOR, "203.0.113.195, 70.41.3.18, 150.172.238.178"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); + assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); + } + + @Test + public void forwardedForIpV4Identifier() throws Exception { + request.addHeader(FORWARDED, "for=203.0.113.195"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); + assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); + } + + @Test + public void forwardedForIpV6Identifier() throws Exception { + request.addHeader(FORWARDED, "for=\"[2001:db8:cafe::17]\""); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("2001:db8:cafe:0:0:0:0:17"); + assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); + } + + @Test + public void forwardedForIpV4IdentifierWithPort() throws Exception { + request.addHeader(FORWARDED, "for=\"203.0.113.195:47011\""); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); + assertThat(actual.getRemotePort()).isEqualTo(47011); + } + + @Test + public void forwardedForIpV6IdentifierWithPort() throws Exception { + request.addHeader(FORWARDED, "For=\"[2001:db8:cafe::17]:47011\""); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("2001:db8:cafe:0:0:0:0:17"); + assertThat(actual.getRemotePort()).isEqualTo(47011); + } + + @Test + public void forwardedForMultipleIdentifiers() throws Exception { + request.addHeader(FORWARDED, "for=203.0.113.195;proto=http, for=\"[2001:db8:cafe::17]\", for=unknown"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); + assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); + } } - @Test - void shouldConcatenatePrefixes() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/first,/second"); - this.request.setRequestURI("/mvc-showcase"); + @Nested + class SendRedirect { - HttpServletRequest actual = filterAndGetWrappedRequest(); - assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/first/second/mvc-showcase"); + @Test + public void sendRedirectWithAbsolutePath() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + + String redirectedUrl = sendRedirect("/foo/bar"); + assertThat(redirectedUrl).isEqualTo("https://example.com/foo/bar"); + } + + @Test // SPR-16506 + public void sendRedirectWithAbsolutePathQueryParamAndFragment() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + request.setQueryString("oldqp=1"); + + String redirectedUrl = sendRedirect("/foo/bar?newqp=2#fragment"); + assertThat(redirectedUrl).isEqualTo("https://example.com/foo/bar?newqp=2#fragment"); + } + + @Test + public void sendRedirectWithContextPath() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + request.setContextPath("/context"); + + String redirectedUrl = sendRedirect("/context/foo/bar"); + assertThat(redirectedUrl).isEqualTo("https://example.com/context/foo/bar"); + } + + @Test + public void sendRedirectWithRelativePath() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + request.setRequestURI("/parent/"); + + String redirectedUrl = sendRedirect("foo/bar"); + assertThat(redirectedUrl).isEqualTo("https://example.com/parent/foo/bar"); + } + + @Test + public void sendRedirectWithFileInPathAndRelativeRedirect() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + request.setRequestURI("/context/a"); + + String redirectedUrl = sendRedirect("foo/bar"); + assertThat(redirectedUrl).isEqualTo("https://example.com/context/foo/bar"); + } + + @Test + public void sendRedirectWithRelativePathIgnoresFile() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + request.setRequestURI("/parent"); + + String redirectedUrl = sendRedirect("foo/bar"); + assertThat(redirectedUrl).isEqualTo("https://example.com/foo/bar"); + } + + @Test + public void sendRedirectWithLocationDotDotPath() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + + String redirectedUrl = sendRedirect("parent/../foo/bar"); + assertThat(redirectedUrl).isEqualTo("https://example.com/foo/bar"); + } + + @Test + public void sendRedirectWithLocationHasScheme() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + + String location = "http://company.example/foo/bar"; + String redirectedUrl = sendRedirect(location); + assertThat(redirectedUrl).isEqualTo(location); + } + + @Test + public void sendRedirectWithLocationSlashSlash() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + + String location = "//other.info/foo/bar"; + String redirectedUrl = sendRedirect(location); + assertThat(redirectedUrl).isEqualTo(("https:" + location)); + } + + @Test + public void sendRedirectWithLocationSlashSlashParentDotDot() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + + String location = "//other.info/parent/../foo/bar"; + String redirectedUrl = sendRedirect(location); + assertThat(redirectedUrl).isEqualTo(("https:" + location)); + } + + @Test + public void sendRedirectWithNoXForwardedAndAbsolutePath() throws Exception { + String redirectedUrl = sendRedirect("/foo/bar"); + assertThat(redirectedUrl).isEqualTo("/foo/bar"); + } + + @Test + public void sendRedirectWithNoXForwardedAndDotDotPath() throws Exception { + String redirectedUrl = sendRedirect("../foo/bar"); + assertThat(redirectedUrl).isEqualTo("../foo/bar"); + } + + @Test + public void sendRedirectWhenRequestOnlyAndXForwardedThenUsesRelativeRedirects() throws Exception { + request.addHeader(X_FORWARDED_PROTO, "https"); + request.addHeader(X_FORWARDED_HOST, "example.com"); + request.addHeader(X_FORWARDED_PORT, "443"); + filter.setRelativeRedirects(true); + String location = sendRedirect("/a"); + + assertThat(location).isEqualTo("/a"); + } + + @Test + public void sendRedirectWhenRequestOnlyAndNoXForwardedThenUsesRelativeRedirects() throws Exception { + filter.setRelativeRedirects(true); + String location = sendRedirect("/a"); + + assertThat(location).isEqualTo("/a"); + } + + private String sendRedirect(final String location) throws ServletException, IOException { + Filter redirectFilter = new OncePerRequestFilter() { + @Override + protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, + FilterChain chain) throws IOException { + + res.sendRedirect(location); + } + }; + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain filterChain = new MockFilterChain(mock(HttpServlet.class), filter, redirectFilter); + filterChain.doFilter(request, response); + return response.getRedirectedUrl(); + } } - @Test - void shouldConcatenatePrefixesWithTrailingSlashes() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/first/,/second//"); - this.request.setRequestURI("/mvc-showcase"); - - HttpServletRequest actual = filterAndGetWrappedRequest(); - assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/first/second/mvc-showcase"); - } - - @Test - public void requestURLNewStringBuffer() throws Exception { - this.request.addHeader(X_FORWARDED_PREFIX, "/prefix/"); - this.request.setRequestURI("/mvc-showcase"); - - HttpServletRequest actual = filterAndGetWrappedRequest(); - actual.getRequestURL().append("?key=value"); - assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/prefix/mvc-showcase"); - } - - @Test - public void sendRedirectWithAbsolutePath() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - - String redirectedUrl = sendRedirect("/foo/bar"); - assertThat(redirectedUrl).isEqualTo("https://example.com/foo/bar"); - } - - @Test // SPR-16506 - public void sendRedirectWithAbsolutePathQueryParamAndFragment() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - this.request.setQueryString("oldqp=1"); - - String redirectedUrl = sendRedirect("/foo/bar?newqp=2#fragment"); - assertThat(redirectedUrl).isEqualTo("https://example.com/foo/bar?newqp=2#fragment"); - } - - @Test - public void sendRedirectWithContextPath() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - this.request.setContextPath("/context"); - - String redirectedUrl = sendRedirect("/context/foo/bar"); - assertThat(redirectedUrl).isEqualTo("https://example.com/context/foo/bar"); - } - - @Test - public void sendRedirectWithRelativePath() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - this.request.setRequestURI("/parent/"); - - String redirectedUrl = sendRedirect("foo/bar"); - assertThat(redirectedUrl).isEqualTo("https://example.com/parent/foo/bar"); - } - - @Test - public void sendRedirectWithFileInPathAndRelativeRedirect() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - this.request.setRequestURI("/context/a"); - - String redirectedUrl = sendRedirect("foo/bar"); - assertThat(redirectedUrl).isEqualTo("https://example.com/context/foo/bar"); - } - - @Test - public void sendRedirectWithRelativePathIgnoresFile() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - this.request.setRequestURI("/parent"); - - String redirectedUrl = sendRedirect("foo/bar"); - assertThat(redirectedUrl).isEqualTo("https://example.com/foo/bar"); - } - - @Test - public void sendRedirectWithLocationDotDotPath() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - - String redirectedUrl = sendRedirect("parent/../foo/bar"); - assertThat(redirectedUrl).isEqualTo("https://example.com/foo/bar"); - } - - @Test - public void sendRedirectWithLocationHasScheme() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - - String location = "http://company.example/foo/bar"; - String redirectedUrl = sendRedirect(location); - assertThat(redirectedUrl).isEqualTo(location); - } - - @Test - public void sendRedirectWithLocationSlashSlash() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - - String location = "//other.info/foo/bar"; - String redirectedUrl = sendRedirect(location); - assertThat(redirectedUrl).isEqualTo(("https:" + location)); - } - - @Test - public void sendRedirectWithLocationSlashSlashParentDotDot() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - - String location = "//other.info/parent/../foo/bar"; - String redirectedUrl = sendRedirect(location); - assertThat(redirectedUrl).isEqualTo(("https:" + location)); - } - - @Test - public void sendRedirectWithNoXForwardedAndAbsolutePath() throws Exception { - String redirectedUrl = sendRedirect("/foo/bar"); - assertThat(redirectedUrl).isEqualTo("/foo/bar"); - } - - @Test - public void sendRedirectWithNoXForwardedAndDotDotPath() throws Exception { - String redirectedUrl = sendRedirect("../foo/bar"); - assertThat(redirectedUrl).isEqualTo("../foo/bar"); - } - - @Test - public void sendRedirectWhenRequestOnlyAndXForwardedThenUsesRelativeRedirects() throws Exception { - this.request.addHeader(X_FORWARDED_PROTO, "https"); - this.request.addHeader(X_FORWARDED_HOST, "example.com"); - this.request.addHeader(X_FORWARDED_PORT, "443"); - this.filter.setRelativeRedirects(true); - String location = sendRedirect("/a"); - - assertThat(location).isEqualTo("/a"); - } - - @Test - public void sendRedirectWhenRequestOnlyAndNoXForwardedThenUsesRelativeRedirects() throws Exception { - this.filter.setRelativeRedirects(true); - String location = sendRedirect("/a"); - - assertThat(location).isEqualTo("/a"); - } - - private String sendRedirect(final String location) throws ServletException, IOException { - Filter filter = new OncePerRequestFilter() { - @Override - protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, - FilterChain chain) throws IOException { - - res.sendRedirect(location); - } - }; - + private HttpServletRequest filterAndGetWrappedRequest() throws ServletException, IOException { MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain filterChain = new MockFilterChain(mock(HttpServlet.class), this.filter, filter); - filterChain.doFilter(request, response); - - return response.getRedirectedUrl(); + this.filter.doFilterInternal(this.request, response, this.filterChain); + return (HttpServletRequest) this.filterChain.getRequest(); } } diff --git a/spring-web/src/test/java/org/springframework/web/server/adapter/ForwardedHeaderTransformerTests.java b/spring-web/src/test/java/org/springframework/web/server/adapter/ForwardedHeaderTransformerTests.java index 5149db28b9d..d4897b8291d 100644 --- a/spring-web/src/test/java/org/springframework/web/server/adapter/ForwardedHeaderTransformerTests.java +++ b/spring-web/src/test/java/org/springframework/web/server/adapter/ForwardedHeaderTransformerTests.java @@ -156,7 +156,7 @@ public class ForwardedHeaderTransformerTests { } @Test - public void noForwardedFor() throws URISyntaxException { + public void forwardedForNotPresent() throws URISyntaxException { HttpHeaders headers = new HttpHeaders(); headers.add("Forwarded", "host=84.198.58.199;proto=https"); @@ -186,6 +186,7 @@ public class ForwardedHeaderTransformerTests { .build(); request = this.requestMutator.apply(request); + assertThat(request.getRemoteAddress()).isNotNull(); assertThat(request.getRemoteAddress().getHostName()).isEqualTo("203.0.113.195"); assertThat(request.getRemoteAddress().getPort()).isEqualTo(4711); } @@ -201,6 +202,7 @@ public class ForwardedHeaderTransformerTests { .build(); request = this.requestMutator.apply(request); + assertThat(request.getRemoteAddress()).isNotNull(); assertThat(request.getRemoteAddress().getHostName()).isEqualTo("203.0.113.195"); }