diff --git a/spring-web/src/main/java/org/springframework/web/util/ForwardedHeaderUtils.java b/spring-web/src/main/java/org/springframework/web/util/ForwardedHeaderUtils.java index 84900e3374..7ccd3c1b28 100644 --- a/spring-web/src/main/java/org/springframework/web/util/ForwardedHeaderUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/ForwardedHeaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 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. @@ -181,6 +181,7 @@ public abstract class ForwardedHeaderUtils { String forHeader = headers.getFirst("X-Forwarded-For"); if (StringUtils.hasText(forHeader)) { String host = StringUtils.tokenizeToStringArray(forHeader, ",")[0]; + host = (!host.startsWith("[") && !host.endsWith("]") ? "[" + host + "]" : host); return InetSocketAddress.createUnresolved(host, port); } 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 576d891533..eba97f020e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -114,7 +114,7 @@ class ForwardedHeaderFilterTests { this.request.addHeader(X_FORWARDED_HOST, "84.198.58.199"); this.request.addHeader(X_FORWARDED_PORT, "443"); this.request.addHeader("foo", "bar"); - this.request.addHeader(X_FORWARDED_FOR, "203.0.113.195"); + this.request.addHeader(X_FORWARDED_FOR, "[203.0.113.195]"); this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain); HttpServletRequest actual = (HttpServletRequest) this.filterChain.getRequest(); @@ -125,7 +125,7 @@ class ForwardedHeaderFilterTests { assertThat(actual.getServerName()).isEqualTo("84.198.58.199"); assertThat(actual.getServerPort()).isEqualTo(443); assertThat(actual.isSecure()).isTrue(); - assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("203.0.113.195"); + assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("[203.0.113.195]"); assertThat(actual.getHeader(X_FORWARDED_PROTO)).isNull(); assertThat(actual.getHeader(X_FORWARDED_HOST)).isNull(); @@ -481,7 +481,7 @@ class ForwardedHeaderFilterTests { 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.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("[203.0.113.195]"); assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); } @@ -490,7 +490,7 @@ class ForwardedHeaderFilterTests { 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.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("[203.0.113.195]"); assertThat(actual.getRemotePort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT); } 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 82a0324613..bf78be2b93 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -230,7 +230,7 @@ class ForwardedHeaderTransformerTests { request = this.requestMutator.apply(request); assertThat(request.getRemoteAddress()).isNotNull(); - assertThat(request.getRemoteAddress().getHostName()).isEqualTo("203.0.113.195"); + assertThat(request.getRemoteAddress().getHostName()).isEqualTo("[203.0.113.195]"); } 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 index f5921e7ff2..b21861bd9f 100644 --- a/spring-web/src/test/java/org/springframework/web/util/ForwardedHeaderUtilsTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/ForwardedHeaderUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -16,6 +16,7 @@ package org.springframework.web.util; +import java.net.InetSocketAddress; import java.net.URI; import java.util.Collections; import java.util.Map; @@ -539,4 +540,15 @@ class ForwardedHeaderUtilsTests { assertThat(result.toUriString()).isEqualTo("https://192.0.2.3:9090/rest/mobile/users/1"); } + @Test // gh-34253 + void fromHttpRequestXForwardedHeaderForIpv6Formatting() { + HttpHeaders headers = new HttpHeaders(); + headers.add("X-Forwarded-For", "fd00:fefe:1::4, 192.168.0.1"); + + InetSocketAddress address = + ForwardedHeaderUtils.parseForwardedFor(URI.create("http://example.com"), headers, null); + + assertThat(address.getHostName()).isEqualTo("[fd00:fefe:1::4]"); + } + }