diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index 4e2fee8607b..8b91cb6f38a 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,37 +97,47 @@ public class ServletServerHttpRequest implements ServerHttpRequest { @Override public URI getURI() { if (this.uri == null) { - String urlString = null; - boolean hasQuery = false; - try { - StringBuffer url = this.servletRequest.getRequestURL(); - String query = this.servletRequest.getQueryString(); - hasQuery = StringUtils.hasText(query); - if (hasQuery) { - url.append('?').append(query); - } - urlString = url.toString(); - this.uri = new URI(urlString); - } - catch (URISyntaxException ex) { - if (!hasQuery) { - throw new IllegalStateException( - "Could not resolve HttpServletRequest as URI: " + urlString, ex); - } - // Maybe a malformed query string... try plain request URL - try { - urlString = this.servletRequest.getRequestURL().toString(); - this.uri = new URI(urlString); - } - catch (URISyntaxException ex2) { - throw new IllegalStateException( - "Could not resolve HttpServletRequest as URI: " + urlString, ex2); - } - } + this.uri = initURI(this.servletRequest); } return this.uri; } + /** + * Initialize a URI from the given Servet request. + * @param servletRequest the request + * @return the initialized URI + * @since 6.1 + */ + public static URI initURI(HttpServletRequest servletRequest) { + String urlString = null; + boolean hasQuery = false; + try { + StringBuffer url = servletRequest.getRequestURL(); + String query = servletRequest.getQueryString(); + hasQuery = StringUtils.hasText(query); + if (hasQuery) { + url.append('?').append(query); + } + urlString = url.toString(); + return new URI(urlString); + } + catch (URISyntaxException ex) { + if (!hasQuery) { + throw new IllegalStateException( + "Could not resolve HttpServletRequest as URI: " + urlString, ex); + } + // Maybe a malformed query string... try plain request URL + try { + urlString = servletRequest.getRequestURL().toString(); + return new URI(urlString); + } + catch (URISyntaxException ex2) { + throw new IllegalStateException( + "Could not resolve HttpServletRequest as URI: " + urlString, ex2); + } + } + } + @Override public HttpHeaders getHeaders() { if (this.headers == null) { 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 abb2b469b00..483722e40a9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.web.filter; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.URI; import java.util.Collections; import java.util.Enumeration; import java.util.Locale; @@ -31,12 +32,14 @@ import jakarta.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponseWrapper; +import org.springframework.http.HttpHeaders; 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.LinkedCaseInsensitiveMap; import org.springframework.util.StringUtils; +import org.springframework.web.util.ForwardedHeaderUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UrlPathHelper; @@ -236,7 +239,9 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { super(servletRequest); ServerHttpRequest request = new ServletServerHttpRequest(servletRequest); - UriComponents uriComponents = UriComponentsBuilder.fromHttpRequest(request).build(); + URI uri = request.getURI(); + HttpHeaders headers = request.getHeaders(); + UriComponents uriComponents = ForwardedHeaderUtils.adaptFromForwardedHeaders(uri, headers).build(); int port = uriComponents.getPort(); this.scheme = uriComponents.getScheme(); @@ -244,7 +249,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { this.host = uriComponents.getHost(); this.port = (port == -1 ? (this.secure ? 443 : 80) : port); - this.remoteAddress = UriComponentsBuilder.parseForwardedFor(request, request.getRemoteAddress()); + this.remoteAddress = ForwardedHeaderUtils.parseForwardedFor(uri, headers, request.getRemoteAddress()); String baseUrl = this.scheme + "://" + this.host + (port == -1 ? "" : ":" + port); Supplier delegateRequest = () -> (HttpServletRequest) getRequest(); @@ -453,8 +458,11 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { StringUtils.applyRelativePath(this.request.getRequestURI(), path)); } - String result = UriComponentsBuilder - .fromHttpRequest(new ServletServerHttpRequest(this.request)) + ServletServerHttpRequest httpRequest = new ServletServerHttpRequest(this.request); + URI uri = httpRequest.getURI(); + HttpHeaders headers = httpRequest.getHeaders(); + + String result = ForwardedHeaderUtils.adaptFromForwardedHeaders(uri, headers) .replacePath(path) .replaceQuery(uriComponents.getQuery()) .fragment(uriComponents.getFragment()) 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 fac2f1acfad..0903f38a6fc 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ 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.UriComponentsBuilder; +import org.springframework.web.util.ForwardedHeaderUtils; /** * Extract values from "Forwarded" and "X-Forwarded-*" headers to override @@ -100,7 +100,9 @@ public class ForwardedHeaderTransformer implements FunctionNote:There are security considerations surrounding the use + * of forwarded headers. Those should not be used unless the application is + * behind a trusted proxy that inserts them and also explicitly removes any such + * headers coming from an external source. + * + *

In most cases, should not use this class directly but rely on + * {@link org.springframework.web.filter.ForwardedHeaderFilter} for Spring MVC, or + * {@link org.springframework.web.server.adapter.ForwardedHeaderTransformer} in + * order to extract the information from them as early as possible, and discard + * such headers. Underlying servers such as Tomcat, Jetty, Reactor Netty, also + * provides options to handle forwarded headers even earlier. + * + * @author Rossen Stoyanchev + * @since 6.1 + */ +public abstract class ForwardedHeaderUtils { + + private static final String FORWARDED_VALUE = "\"?([^;,\"]+)\"?"; + + private static final Pattern FORWARDED_HOST_PATTERN = Pattern.compile("(?i:host)=" + FORWARDED_VALUE); + + private static final Pattern FORWARDED_PROTO_PATTERN = Pattern.compile("(?i:proto)=" + FORWARDED_VALUE); + + private static final Pattern FORWARDED_FOR_PATTERN = Pattern.compile("(?i:for)=" + FORWARDED_VALUE); + + + /** + * Adapt the scheme+host+port of the given {@link URI} from the "Forwarded" header, + * see RFC 7239, or + * "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if "Forwarded" + * is not present. + * @param headers the HTTP headers to consider + * @return a {@link UriComponentsBuilder} that reflects the request URI and + * additional updates from forwarded headers + */ + public static UriComponentsBuilder adaptFromForwardedHeaders(URI uri, HttpHeaders headers) { + UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(uri); + try { + String forwardedHeader = headers.getFirst("Forwarded"); + if (StringUtils.hasText(forwardedHeader)) { + Matcher matcher = FORWARDED_PROTO_PATTERN.matcher(forwardedHeader); + if (matcher.find()) { + uriComponentsBuilder.scheme(matcher.group(1).trim()); + uriComponentsBuilder.port(null); + } + else if (isForwardedSslOn(headers)) { + uriComponentsBuilder.scheme("https"); + uriComponentsBuilder.port(null); + } + matcher = FORWARDED_HOST_PATTERN.matcher(forwardedHeader); + if (matcher.find()) { + adaptForwardedHost(uriComponentsBuilder, matcher.group(1).trim()); + } + } + else { + String protocolHeader = headers.getFirst("X-Forwarded-Proto"); + if (StringUtils.hasText(protocolHeader)) { + uriComponentsBuilder.scheme(StringUtils.tokenizeToStringArray(protocolHeader, ",")[0]); + uriComponentsBuilder.port(null); + } + else if (isForwardedSslOn(headers)) { + uriComponentsBuilder.scheme("https"); + uriComponentsBuilder.port(null); + } + String hostHeader = headers.getFirst("X-Forwarded-Host"); + if (StringUtils.hasText(hostHeader)) { + adaptForwardedHost(uriComponentsBuilder, StringUtils.tokenizeToStringArray(hostHeader, ",")[0]); + } + String portHeader = headers.getFirst("X-Forwarded-Port"); + if (StringUtils.hasText(portHeader)) { + uriComponentsBuilder.port(Integer.parseInt(StringUtils.tokenizeToStringArray(portHeader, ",")[0])); + } + } + } + catch (NumberFormatException ex) { + throw new IllegalArgumentException("Failed to parse a port from \"forwarded\"-type headers. " + + "If not behind a trusted proxy, consider using ForwardedHeaderFilter " + + "with the removeOnly=true. Request headers: " + headers); + } + + uriComponentsBuilder.resetPortIfDefaultForScheme(); + + return uriComponentsBuilder; + } + + private static boolean isForwardedSslOn(HttpHeaders headers) { + String forwardedSsl = headers.getFirst("X-Forwarded-Ssl"); + return StringUtils.hasText(forwardedSsl) && forwardedSsl.equalsIgnoreCase("on"); + } + + private static void adaptForwardedHost(UriComponentsBuilder uriComponentsBuilder, String rawValue) { + int portSeparatorIdx = rawValue.lastIndexOf(':'); + int squareBracketIdx = rawValue.lastIndexOf(']'); + if (portSeparatorIdx > squareBracketIdx) { + if (squareBracketIdx == -1 && rawValue.indexOf(':') != portSeparatorIdx) { + throw new IllegalArgumentException("Invalid IPv4 address: " + rawValue); + } + uriComponentsBuilder.host(rawValue.substring(0, portSeparatorIdx)); + uriComponentsBuilder.port(Integer.parseInt(rawValue, portSeparatorIdx + 1, rawValue.length(), 10)); + } + else { + uriComponentsBuilder.host(rawValue); + uriComponentsBuilder.port(null); + } + } + + /** + * Parse the first "Forwarded: for=..." or "X-Forwarded-For" header value to + * an {@code InetSocketAddress} representing the address of the client. + * @param uri the request URI + * @param headers the request headers that may contain forwarded headers + * @param remoteAddress the current remoteAddress + * @return an {@code InetSocketAddress} with the extracted host and port, or + * {@code null} if the headers are not present. + * @see RFC 7239, Section 5.2 + */ + @Nullable + public static InetSocketAddress parseForwardedFor( + URI uri, HttpHeaders headers, @Nullable InetSocketAddress remoteAddress) { + + int port = (remoteAddress != null ? + remoteAddress.getPort() : "https".equals(uri.getScheme()) ? 443 : 80); + + 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()) { + String value = matcher.group(1).trim(); + String host = value; + int portSeparatorIdx = value.lastIndexOf(':'); + int squareBracketIdx = value.lastIndexOf(']'); + if (portSeparatorIdx > squareBracketIdx) { + if (squareBracketIdx == -1 && value.indexOf(':') != portSeparatorIdx) { + throw new IllegalArgumentException("Invalid IPv4 address: " + value); + } + host = value.substring(0, portSeparatorIdx); + try { + port = Integer.parseInt(value, portSeparatorIdx + 1, value.length(), 10); + } + catch (NumberFormatException ex) { + throw new IllegalArgumentException( + "Failed to parse a port from \"forwarded\"-type header value: " + value); + } + } + return InetSocketAddress.createUnresolved(host, port); + } + } + + String forHeader = headers.getFirst("X-Forwarded-For"); + if (StringUtils.hasText(forHeader)) { + String host = StringUtils.tokenizeToStringArray(forHeader, ",")[0]; + return InetSocketAddress.createUnresolved(host, port); + } + + return null; + } + +} diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 33cdc834166..cb95e2987a2 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -104,12 +104,6 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { private static final String FORWARDED_VALUE = "\"?([^;,\"]+)\"?"; - private static final Pattern FORWARDED_HOST_PATTERN = Pattern.compile("(?i:host)=" + FORWARDED_VALUE); - - private static final Pattern FORWARDED_PROTO_PATTERN = Pattern.compile("(?i:proto)=" + FORWARDED_VALUE); - - private static final Pattern FORWARDED_FOR_PATTERN = Pattern.compile("(?i:for)=" + FORWARDED_VALUE); - private static final Object[] EMPTY_VALUES = new Object[0]; @@ -326,10 +320,12 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { * @param request the source request * @return the URI components of the URI * @since 4.1.5 - * @see #parseForwardedFor(HttpRequest, InetSocketAddress) + * @deprecated in favor of {@link ForwardedHeaderUtils#adaptFromForwardedHeaders}; + * to be removed in 6.2 */ + @Deprecated(since = "6.1", forRemoval = true) public static UriComponentsBuilder fromHttpRequest(HttpRequest request) { - return fromUri(request.getURI()).adaptFromForwardedHeaders(request.getHeaders()); + return ForwardedHeaderUtils.adaptFromForwardedHeaders(request.getURI(), request.getHeaders()); } /** @@ -340,48 +336,16 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { * @return an {@code InetSocketAddress} with the extracted host and port, or * {@code null} if the headers are not present. * @since 5.3 - * @see RFC 7239, Section 5.2 + * @deprecated in favor of {@link ForwardedHeaderUtils#adaptFromForwardedHeaders}; + * to be removed in 6.2 */ + @Deprecated(since = "6.1", forRemoval = true) @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(':'); - int squareBracketIdx = value.lastIndexOf(']'); - if (portSeparatorIdx > squareBracketIdx) { - if (squareBracketIdx == -1 && value.indexOf(':') != portSeparatorIdx) { - throw new IllegalArgumentException("Invalid IPv4 address: " + value); - } - host = value.substring(0, portSeparatorIdx); - try { - port = Integer.parseInt(value, portSeparatorIdx + 1, value.length(), 10); - } - catch (NumberFormatException ex) { - throw new IllegalArgumentException( - "Failed to parse a port from \"forwarded\"-type header value: " + value); - } - } - return InetSocketAddress.createUnresolved(host, port); - } - } - - String forHeader = request.getHeaders().getFirst("X-Forwarded-For"); - if (StringUtils.hasText(forHeader)) { - String host = StringUtils.tokenizeToStringArray(forHeader, ",")[0]; - return InetSocketAddress.createUnresolved(host, port); - } - - return null; + return ForwardedHeaderUtils.parseForwardedFor( + request.getURI(), request.getHeaders(), remoteAddress); } /** @@ -824,94 +788,6 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { return this; } - /** - * Adapt this builder's scheme+host+port from the given headers, specifically - * "Forwarded" (RFC 7239, - * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if - * "Forwarded" is not found. - *

Note: this method uses values from forwarded headers, - * if present, in order to reflect the client-originated protocol and address. - * Consider using the {@code ForwardedHeaderFilter} in order to choose from a - * central place whether to extract and use, or to discard such headers. - * See the Spring Framework reference for more on this filter. - * @param headers the HTTP headers to consider - * @return this UriComponentsBuilder - * @since 4.2.7 - */ - UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { - try { - String forwardedHeader = headers.getFirst("Forwarded"); - if (StringUtils.hasText(forwardedHeader)) { - Matcher matcher = FORWARDED_PROTO_PATTERN.matcher(forwardedHeader); - if (matcher.find()) { - scheme(matcher.group(1).trim()); - port(null); - } - else if (isForwardedSslOn(headers)) { - scheme("https"); - port(null); - } - matcher = FORWARDED_HOST_PATTERN.matcher(forwardedHeader); - if (matcher.find()) { - adaptForwardedHost(matcher.group(1).trim()); - } - } - else { - String protocolHeader = headers.getFirst("X-Forwarded-Proto"); - if (StringUtils.hasText(protocolHeader)) { - scheme(StringUtils.tokenizeToStringArray(protocolHeader, ",")[0]); - port(null); - } - else if (isForwardedSslOn(headers)) { - scheme("https"); - port(null); - } - String hostHeader = headers.getFirst("X-Forwarded-Host"); - if (StringUtils.hasText(hostHeader)) { - adaptForwardedHost(StringUtils.tokenizeToStringArray(hostHeader, ",")[0]); - } - String portHeader = headers.getFirst("X-Forwarded-Port"); - if (StringUtils.hasText(portHeader)) { - port(Integer.parseInt(StringUtils.tokenizeToStringArray(portHeader, ",")[0])); - } - } - } - catch (NumberFormatException ex) { - throw new IllegalArgumentException("Failed to parse a port from \"forwarded\"-type headers. " + - "If not behind a trusted proxy, consider using ForwardedHeaderFilter " + - "with the removeOnly=true. Request headers: " + headers); - } - - if (this.scheme != null && - (((this.scheme.equals("http") || this.scheme.equals("ws")) && "80".equals(this.port)) || - ((this.scheme.equals("https") || this.scheme.equals("wss")) && "443".equals(this.port)))) { - port(null); - } - - return this; - } - - private boolean isForwardedSslOn(HttpHeaders headers) { - String forwardedSsl = headers.getFirst("X-Forwarded-Ssl"); - return StringUtils.hasText(forwardedSsl) && forwardedSsl.equalsIgnoreCase("on"); - } - - private void adaptForwardedHost(String rawValue) { - int portSeparatorIdx = rawValue.lastIndexOf(':'); - int squareBracketIdx = rawValue.lastIndexOf(']'); - if (portSeparatorIdx > squareBracketIdx) { - if (squareBracketIdx == -1 && rawValue.indexOf(':') != portSeparatorIdx) { - throw new IllegalArgumentException("Invalid IPv4 address: " + rawValue); - } - host(rawValue.substring(0, portSeparatorIdx)); - port(Integer.parseInt(rawValue, portSeparatorIdx + 1, rawValue.length(), 10)); - } - else { - host(rawValue); - port(null); - } - } - private void resetHierarchicalComponents() { this.userInfo = null; this.host = null; @@ -924,6 +800,14 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { this.ssp = null; } + void resetPortIfDefaultForScheme() { + if (this.scheme != null && + (((this.scheme.equals("http") || this.scheme.equals("ws")) && "80".equals(this.port)) || + ((this.scheme.equals("https") || this.scheme.equals("wss")) && "443".equals(this.port)))) { + port(null); + } + } + /** * Public declaration of Object's {@code clone()} method. diff --git a/spring-web/src/test/java/org/springframework/web/util/ForwardedHeaderUtilsTests.java b/spring-web/src/test/java/org/springframework/web/util/ForwardedHeaderUtilsTests.java new file mode 100644 index 00000000000..882bbb5ff33 --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/util/ForwardedHeaderUtilsTests.java @@ -0,0 +1,534 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.web.util; + +import java.net.URI; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.web.testfixture.servlet.MockHttpServletRequest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Unit tests for {@link UriComponentsBuilder}. + * + * @author Rossen Stoyanchev + */ +class ForwardedHeaderUtilsTests { + + @Test + void fromHttpRequest() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/path"); + request.setQueryString("a=1"); + + ServletServerHttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + assertThat(result.getScheme()).isEqualTo("http"); + assertThat(result.getHost()).isEqualTo("localhost"); + assertThat(result.getPort()).isEqualTo(-1); + assertThat(result.getPath()).isEqualTo("/path"); + assertThat(result.getQuery()).isEqualTo("a=1"); + } + + @ParameterizedTest // gh-17368, gh-27097 + @ValueSource(strings = {"https", "wss"}) + void fromHttpRequestResetsPort443(String protocol) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("X-Forwarded-Proto", protocol); + request.addHeader("X-Forwarded-Host", "84.198.58.199"); + request.addHeader("X-Forwarded-Port", 443); + request.setScheme("http"); + request.setServerName("example.com"); + request.setServerPort(80); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo(protocol); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPort()).isEqualTo(-1); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + } + + @ParameterizedTest // gh-27097 + @ValueSource(strings = {"http", "ws"}) + void fromHttpRequestResetsPort80(String protocol) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("X-Forwarded-Proto", protocol); + request.addHeader("X-Forwarded-Host", "84.198.58.199"); + request.addHeader("X-Forwarded-Port", 80); + request.setScheme("http"); + request.setServerName("example.com"); + request.setServerPort(80); + request.setRequestURI("/path"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo(protocol); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPort()).isEqualTo(-1); + assertThat(result.getPath()).isEqualTo("/path"); + } + + @Test // SPR-14761 + void fromHttpRequestWithForwardedIPv4Host() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("https"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("Forwarded", "host=192.168.0.1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("https://192.168.0.1/mvc-showcase"); + } + + @Test // SPR-14761 + void fromHttpRequestWithForwardedIPv6() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("Forwarded", "host=[1abc:2abc:3abc::5ABC:6abc]"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("http://[1abc:2abc:3abc::5ABC:6abc]/mvc-showcase"); + } + + @Test // SPR-14761 + void fromHttpRequestWithForwardedIPv6Host() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "[1abc:2abc:3abc::5ABC:6abc]"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("http://[1abc:2abc:3abc::5ABC:6abc]/mvc-showcase"); + } + + @Test // SPR-14761 + void fromHttpRequestWithForwardedIPv6HostAndPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "[1abc:2abc:3abc::5ABC:6abc]:8080"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("http://[1abc:2abc:3abc::5ABC:6abc]:8080/mvc-showcase"); + } + + @Test // gh-26748 + void fromHttpRequestWithForwardedInvalidIPv6Address() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "2a02:918:175:ab60:45ee:c12c:dac1:808b"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + + assertThatIllegalArgumentException().isThrownBy(() -> + ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build()); + } + + @Test + void fromHttpRequestWithForwardedHost() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("https"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "anotherHost"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("https://anotherHost/mvc-showcase"); + } + + @Test // SPR-10701 + void fromHttpRequestWithForwardedHostIncludingPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "webtest.foo.bar.com:443"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getHost()).isEqualTo("webtest.foo.bar.com"); + assertThat(result.getPort()).isEqualTo(443); + } + + @Test // SPR-11140 + void fromHttpRequestWithForwardedHostMultiValuedHeader() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.addHeader("X-Forwarded-Host", "a.example.org, b.example.org, c.example.org"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getHost()).isEqualTo("a.example.org"); + assertThat(result.getPort()).isEqualTo(-1); + } + + @Test // SPR-11855 + void fromHttpRequestWithForwardedHostAndPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(8080); + request.addHeader("X-Forwarded-Host", "foobarhost"); + request.addHeader("X-Forwarded-Port", "9090"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getHost()).isEqualTo("foobarhost"); + assertThat(result.getPort()).isEqualTo(9090); + } + + @Test // SPR-11872 + void fromHttpRequestWithForwardedHostWithDefaultPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(10080); + request.addHeader("X-Forwarded-Host", "example.org"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getHost()).isEqualTo("example.org"); + assertThat(result.getPort()).isEqualTo(-1); + } + + @Test // SPR-16262 + void fromHttpRequestWithForwardedProtoWithDefaultPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("example.org"); + request.setServerPort(10080); + request.addHeader("X-Forwarded-Proto", "https"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("example.org"); + assertThat(result.getPort()).isEqualTo(-1); + } + + @Test // SPR-16863 + void fromHttpRequestWithForwardedSsl() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("example.org"); + request.setServerPort(10080); + request.addHeader("X-Forwarded-Ssl", "on"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("example.org"); + assertThat(result.getPort()).isEqualTo(-1); + } + + @Test + void fromHttpRequestWithForwardedHostWithForwardedScheme() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(10080); + request.addHeader("X-Forwarded-Host", "example.org"); + request.addHeader("X-Forwarded-Proto", "https"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getHost()).isEqualTo("example.org"); + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getPort()).isEqualTo(-1); + } + + @Test // SPR-12771 + void fromHttpRequestWithForwardedProtoAndDefaultPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(80); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Proto", "https"); + request.addHeader("X-Forwarded-Host", "84.198.58.199"); + request.addHeader("X-Forwarded-Port", "443"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("https://84.198.58.199/mvc-showcase"); + } + + @Test // SPR-12813 + void fromHttpRequestWithForwardedPortMultiValueHeader() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(9090); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "a.example.org"); + request.addHeader("X-Forwarded-Port", "80,52022"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("http://a.example.org/mvc-showcase"); + } + + @Test // SPR-12816 + void fromHttpRequestWithForwardedProtoMultiValueHeader() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(8080); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "a.example.org"); + request.addHeader("X-Forwarded-Port", "443"); + request.addHeader("X-Forwarded-Proto", "https,https"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("https://a.example.org/mvc-showcase"); + } + + @Test // gh-19890 + void fromHttpRequestWithEmptyScheme() { + HttpRequest request = new HttpRequest() { + @Override + public HttpMethod getMethod() { + return HttpMethod.GET; + } + + @Override + public URI getURI() { + return UriComponentsBuilder.fromUriString("/").build().toUri(); + } + + @Override + public HttpHeaders getHeaders() { + return new HttpHeaders(); + } + }; + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(request.getURI(), request.getHeaders()).build(); + + assertThat(result.toString()).isEqualTo("/"); + } + + @Test // SPR-11856 + void fromHttpRequestForwardedHeader() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "proto=https; host=84.198.58.199"); + request.setScheme("http"); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + } + + @Test + void fromHttpRequestForwardedHeaderQuoted() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "proto=\"https\"; host=\"84.198.58.199\""); + request.setScheme("http"); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + } + + @Test + void fromHttpRequestMultipleForwardedHeader() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "host=84.198.58.199;proto=https"); + request.addHeader("Forwarded", "proto=ftp; host=1.2.3.4"); + request.setScheme("http"); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + } + + @Test + void fromHttpRequestMultipleForwardedHeaderComma() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "host=84.198.58.199 ;proto=https, proto=ftp; host=1.2.3.4"); + request.setScheme("http"); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + } + + @Test + void fromHttpRequestForwardedHeaderWithHostPortAndWithoutServerPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "proto=https; host=84.198.58.199:9090"); + request.setScheme("http"); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + assertThat(result.getPort()).isEqualTo(9090); + assertThat(result.toUriString()).isEqualTo("https://84.198.58.199:9090/rest/mobile/users/1"); + } + + @Test + void fromHttpRequestForwardedHeaderWithHostPortAndServerPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "proto=https; host=84.198.58.199:9090"); + request.setScheme("http"); + request.setServerPort(8080); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + assertThat(result.getPort()).isEqualTo(9090); + assertThat(result.toUriString()).isEqualTo("https://84.198.58.199:9090/rest/mobile/users/1"); + } + + @Test + void fromHttpRequestForwardedHeaderWithoutHostPortAndWithServerPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "proto=https; host=84.198.58.199"); + request.setScheme("http"); + request.setServerPort(8080); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("84.198.58.199"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + assertThat(result.getPort()).isEqualTo(-1); + assertThat(result.toUriString()).isEqualTo("https://84.198.58.199/rest/mobile/users/1"); + } + + @Test // SPR-16262 + void fromHttpRequestForwardedHeaderWithProtoAndServerPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "proto=https"); + request.setScheme("http"); + request.setServerPort(8080); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("example.com"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + assertThat(result.getPort()).isEqualTo(-1); + assertThat(result.toUriString()).isEqualTo("https://example.com/rest/mobile/users/1"); + } + + @Test // gh-25737 + void fromHttpRequestForwardedHeaderComma() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Forwarded", "for=192.0.2.0,for=192.0.2.1;proto=https;host=192.0.2.3:9090"); + request.setScheme("http"); + request.setServerPort(8080); + request.setServerName("example.com"); + request.setRequestURI("/rest/mobile/users/1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = ForwardedHeaderUtils.adaptFromForwardedHeaders(httpRequest.getURI(), httpRequest.getHeaders()).build(); + + assertThat(result.getScheme()).isEqualTo("https"); + assertThat(result.getHost()).isEqualTo("192.0.2.3"); + assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); + assertThat(result.getPort()).isEqualTo(9090); + assertThat(result.toUriString()).isEqualTo("https://192.0.2.3:9090/rest/mobile/users/1"); + } + +} diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java index 3461bbd3495..3198480eeab 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java @@ -27,16 +27,9 @@ import java.util.Optional; import java.util.function.BiConsumer; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpRequest; -import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.testfixture.servlet.MockHttpServletRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -359,316 +352,6 @@ class UriComponentsBuilderTests { assertThat(uriComponents.toUri().toString()).isEqualTo(httpUrl); } - @Test - void fromHttpRequest() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.setRequestURI("/path"); - request.setQueryString("a=1"); - - UriComponents result = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)).build(); - assertThat(result.getScheme()).isEqualTo("http"); - assertThat(result.getHost()).isEqualTo("localhost"); - assertThat(result.getPort()).isEqualTo(-1); - assertThat(result.getPath()).isEqualTo("/path"); - assertThat(result.getQuery()).isEqualTo("a=1"); - } - - @ParameterizedTest // gh-17368, gh-27097 - @ValueSource(strings = {"https", "wss"}) - void fromHttpRequestResetsPort443(String protocol) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("X-Forwarded-Proto", protocol); - request.addHeader("X-Forwarded-Host", "84.198.58.199"); - request.addHeader("X-Forwarded-Port", 443); - request.setScheme("http"); - request.setServerName("example.com"); - request.setServerPort(80); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo(protocol); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPort()).isEqualTo(-1); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - } - - @ParameterizedTest // gh-27097 - @ValueSource(strings = {"http", "ws"}) - void fromHttpRequestResetsPort80(String protocol) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("X-Forwarded-Proto", protocol); - request.addHeader("X-Forwarded-Host", "84.198.58.199"); - request.addHeader("X-Forwarded-Port", 80); - request.setScheme("http"); - request.setServerName("example.com"); - request.setServerPort(80); - request.setRequestURI("/path"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo(protocol); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPort()).isEqualTo(-1); - assertThat(result.getPath()).isEqualTo("/path"); - } - - @Test // SPR-14761 - void fromHttpRequestWithForwardedIPv4Host() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("https"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.setRequestURI("/mvc-showcase"); - request.addHeader("Forwarded", "host=192.168.0.1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.toString()).isEqualTo("https://192.168.0.1/mvc-showcase"); - } - - @Test // SPR-14761 - void fromHttpRequestWithForwardedIPv6() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.setRequestURI("/mvc-showcase"); - request.addHeader("Forwarded", "host=[1abc:2abc:3abc::5ABC:6abc]"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.toString()).isEqualTo("http://[1abc:2abc:3abc::5ABC:6abc]/mvc-showcase"); - } - - @Test // SPR-14761 - void fromHttpRequestWithForwardedIPv6Host() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.setRequestURI("/mvc-showcase"); - request.addHeader("X-Forwarded-Host", "[1abc:2abc:3abc::5ABC:6abc]"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.toString()).isEqualTo("http://[1abc:2abc:3abc::5ABC:6abc]/mvc-showcase"); - } - - @Test // SPR-14761 - void fromHttpRequestWithForwardedIPv6HostAndPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.setRequestURI("/mvc-showcase"); - request.addHeader("X-Forwarded-Host", "[1abc:2abc:3abc::5ABC:6abc]:8080"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.toString()).isEqualTo("http://[1abc:2abc:3abc::5ABC:6abc]:8080/mvc-showcase"); - } - - @Test // gh-26748 - void fromHttpRequestWithForwardedInvalidIPv6Address() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.setRequestURI("/mvc-showcase"); - request.addHeader("X-Forwarded-Host", "2a02:918:175:ab60:45ee:c12c:dac1:808b"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - - assertThatIllegalArgumentException().isThrownBy(() -> - UriComponentsBuilder.fromHttpRequest(httpRequest).build()); - } - - @Test - void fromHttpRequestWithForwardedHost() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("https"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.setRequestURI("/mvc-showcase"); - request.addHeader("X-Forwarded-Host", "anotherHost"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.toString()).isEqualTo("https://anotherHost/mvc-showcase"); - } - - @Test // SPR-10701 - void fromHttpRequestWithForwardedHostIncludingPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.setRequestURI("/mvc-showcase"); - request.addHeader("X-Forwarded-Host", "webtest.foo.bar.com:443"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getHost()).isEqualTo("webtest.foo.bar.com"); - assertThat(result.getPort()).isEqualTo(443); - } - - @Test // SPR-11140 - void fromHttpRequestWithForwardedHostMultiValuedHeader() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(-1); - request.addHeader("X-Forwarded-Host", "a.example.org, b.example.org, c.example.org"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getHost()).isEqualTo("a.example.org"); - assertThat(result.getPort()).isEqualTo(-1); - } - - @Test // SPR-11855 - void fromHttpRequestWithForwardedHostAndPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(8080); - request.addHeader("X-Forwarded-Host", "foobarhost"); - request.addHeader("X-Forwarded-Port", "9090"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getHost()).isEqualTo("foobarhost"); - assertThat(result.getPort()).isEqualTo(9090); - } - - @Test // SPR-11872 - void fromHttpRequestWithForwardedHostWithDefaultPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(10080); - request.addHeader("X-Forwarded-Host", "example.org"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getHost()).isEqualTo("example.org"); - assertThat(result.getPort()).isEqualTo(-1); - } - - @Test // SPR-16262 - void fromHttpRequestWithForwardedProtoWithDefaultPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("example.org"); - request.setServerPort(10080); - request.addHeader("X-Forwarded-Proto", "https"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("example.org"); - assertThat(result.getPort()).isEqualTo(-1); - } - - @Test // SPR-16863 - void fromHttpRequestWithForwardedSsl() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("example.org"); - request.setServerPort(10080); - request.addHeader("X-Forwarded-Ssl", "on"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("example.org"); - assertThat(result.getPort()).isEqualTo(-1); - } - - @Test - void fromHttpRequestWithForwardedHostWithForwardedScheme() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(10080); - request.addHeader("X-Forwarded-Host", "example.org"); - request.addHeader("X-Forwarded-Proto", "https"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getHost()).isEqualTo("example.org"); - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getPort()).isEqualTo(-1); - } - - @Test // SPR-12771 - void fromHttpRequestWithForwardedProtoAndDefaultPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(80); - request.setRequestURI("/mvc-showcase"); - request.addHeader("X-Forwarded-Proto", "https"); - request.addHeader("X-Forwarded-Host", "84.198.58.199"); - request.addHeader("X-Forwarded-Port", "443"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.toString()).isEqualTo("https://84.198.58.199/mvc-showcase"); - } - - @Test // SPR-12813 - void fromHttpRequestWithForwardedPortMultiValueHeader() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(9090); - request.setRequestURI("/mvc-showcase"); - request.addHeader("X-Forwarded-Host", "a.example.org"); - request.addHeader("X-Forwarded-Port", "80,52022"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.toString()).isEqualTo("http://a.example.org/mvc-showcase"); - } - - @Test // SPR-12816 - void fromHttpRequestWithForwardedProtoMultiValueHeader() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setScheme("http"); - request.setServerName("localhost"); - request.setServerPort(8080); - request.setRequestURI("/mvc-showcase"); - request.addHeader("X-Forwarded-Host", "a.example.org"); - request.addHeader("X-Forwarded-Port", "443"); - request.addHeader("X-Forwarded-Proto", "https,https"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.toString()).isEqualTo("https://a.example.org/mvc-showcase"); - } - @Test // SPR-12742 void fromHttpRequestWithTrailingSlash() { UriComponents before = UriComponentsBuilder.fromPath("/foo/").build(); @@ -676,29 +359,6 @@ class UriComponentsBuilderTests { assertThat(after.getPath()).isEqualTo("/foo/"); } - @Test // gh-19890 - void fromHttpRequestWithEmptyScheme() { - HttpRequest request = new HttpRequest() { - @Override - public HttpMethod getMethod() { - return HttpMethod.GET; - } - - @Override - public URI getURI() { - return UriComponentsBuilder.fromUriString("/").build().toUri(); - } - - @Override - public HttpHeaders getHeaders() { - return new HttpHeaders(); - } - }; - UriComponents result = UriComponentsBuilder.fromHttpRequest(request).build(); - - assertThat(result.toString()).isEqualTo("/"); - } - @Test void path() { UriComponentsBuilder builder = UriComponentsBuilder.fromPath("/foo/bar"); @@ -1107,166 +767,6 @@ class UriComponentsBuilderTests { tester.accept("/a{year:\\d{1,4}}b", "/a{year:\\d{1,4}}b"); } - @Test // SPR-11856 - void fromHttpRequestForwardedHeader() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "proto=https; host=84.198.58.199"); - request.setScheme("http"); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - } - - @Test - void fromHttpRequestForwardedHeaderQuoted() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "proto=\"https\"; host=\"84.198.58.199\""); - request.setScheme("http"); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - } - - @Test - void fromHttpRequestMultipleForwardedHeader() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "host=84.198.58.199;proto=https"); - request.addHeader("Forwarded", "proto=ftp; host=1.2.3.4"); - request.setScheme("http"); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - } - - @Test - void fromHttpRequestMultipleForwardedHeaderComma() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "host=84.198.58.199 ;proto=https, proto=ftp; host=1.2.3.4"); - request.setScheme("http"); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - } - - @Test - void fromHttpRequestForwardedHeaderWithHostPortAndWithoutServerPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "proto=https; host=84.198.58.199:9090"); - request.setScheme("http"); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - assertThat(result.getPort()).isEqualTo(9090); - assertThat(result.toUriString()).isEqualTo("https://84.198.58.199:9090/rest/mobile/users/1"); - } - - @Test - void fromHttpRequestForwardedHeaderWithHostPortAndServerPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "proto=https; host=84.198.58.199:9090"); - request.setScheme("http"); - request.setServerPort(8080); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - assertThat(result.getPort()).isEqualTo(9090); - assertThat(result.toUriString()).isEqualTo("https://84.198.58.199:9090/rest/mobile/users/1"); - } - - @Test - void fromHttpRequestForwardedHeaderWithoutHostPortAndWithServerPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "proto=https; host=84.198.58.199"); - request.setScheme("http"); - request.setServerPort(8080); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("84.198.58.199"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - assertThat(result.getPort()).isEqualTo(-1); - assertThat(result.toUriString()).isEqualTo("https://84.198.58.199/rest/mobile/users/1"); - } - - @Test // SPR-16262 - void fromHttpRequestForwardedHeaderWithProtoAndServerPort() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "proto=https"); - request.setScheme("http"); - request.setServerPort(8080); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("example.com"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - assertThat(result.getPort()).isEqualTo(-1); - assertThat(result.toUriString()).isEqualTo("https://example.com/rest/mobile/users/1"); - } - - @Test // gh-25737 - void fromHttpRequestForwardedHeaderComma() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Forwarded", "for=192.0.2.0,for=192.0.2.1;proto=https;host=192.0.2.3:9090"); - request.setScheme("http"); - request.setServerPort(8080); - request.setServerName("example.com"); - request.setRequestURI("/rest/mobile/users/1"); - - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - - assertThat(result.getScheme()).isEqualTo("https"); - assertThat(result.getHost()).isEqualTo("192.0.2.3"); - assertThat(result.getPath()).isEqualTo("/rest/mobile/users/1"); - assertThat(result.getPort()).isEqualTo(9090); - assertThat(result.toUriString()).isEqualTo("https://192.0.2.3:9090/rest/mobile/users/1"); - } - - @Test // SPR-16364 void uriComponentsNotEqualAfterNormalization() { UriComponents uri1 = UriComponentsBuilder.fromUriString("http://test.com").build().normalize();