Skip to content

Commit 8751c99

Browse files
committed
Fix NumberFormatException with X-Forwarded-Host
This commit fixes `NumberFormatException`s that were thrown when parsing IPv6 host values in `X-Forwarded-Host` request headers. Issue: SPR-14761 (cherry picked from ea5ff87)
1 parent adbf992 commit 8751c99

File tree

2 files changed

+92
-62
lines changed

2 files changed

+92
-62
lines changed

spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
* @author Rossen Stoyanchev
5252
* @author Phillip Webb
5353
* @author Oliver Gierke
54+
* @author Brian Clozel
5455
* @since 3.1
5556
* @see #newInstance()
5657
* @see #fromPath(String)
@@ -687,10 +688,10 @@ UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) {
687688
String hostHeader = headers.getFirst("X-Forwarded-Host");
688689
if (StringUtils.hasText(hostHeader)) {
689690
String hostToUse = StringUtils.tokenizeToStringArray(hostHeader, ",")[0];
690-
String[] hostAndPort = StringUtils.split(hostToUse, ":");
691-
if (hostAndPort != null) {
692-
host(hostAndPort[0]);
693-
port(Integer.parseInt(hostAndPort[1]));
691+
int portSeparatorIdx = hostToUse.lastIndexOf(":");
692+
if (portSeparatorIdx > hostToUse.lastIndexOf("]")) {
693+
host(hostToUse.substring(0, portSeparatorIdx));
694+
port(Integer.parseInt(hostToUse.substring(portSeparatorIdx + 1)));
694695
}
695696
else {
696697
host(hostToUse);

spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java

+87-58
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ public void fromOpaqueUri() throws URISyntaxException {
125125
assertEquals("Invalid result URI", uri, result.toUri());
126126
}
127127

128-
// SPR-9317
129-
130-
@Test
128+
@Test // SPR-9317
131129
public void fromUriEncodedQuery() throws URISyntaxException {
132130
URI uri = new URI("http://www.example.org/?param=aGVsbG9Xb3JsZA%3D%3D");
133131
String fromUri = UriComponentsBuilder.fromUri(uri).build().getQueryParams().get("param").get(0);
@@ -182,9 +180,7 @@ public void fromUriString() {
182180
assertEquals("28", result.getFragment());
183181
}
184182

185-
// SPR-9832
186-
187-
@Test
183+
@Test // SPR-9832
188184
public void fromUriStringQueryParamWithReservedCharInValue() throws URISyntaxException {
189185
String uri = "http://www.google.com/ig/calculator?q=1USD=?EUR";
190186
UriComponents result = UriComponentsBuilder.fromUriString(uri).build();
@@ -193,41 +189,35 @@ public void fromUriStringQueryParamWithReservedCharInValue() throws URISyntaxExc
193189
assertEquals("1USD=?EUR", result.getQueryParams().getFirst("q"));
194190
}
195191

196-
// SPR-10779
197-
198-
@Test
192+
@Test // SPR-10779
199193
public void fromHttpUrlStringCaseInsesitiveScheme() {
200194
assertEquals("http", UriComponentsBuilder.fromHttpUrl("HTTP://www.google.com").build().getScheme());
201195
assertEquals("https", UriComponentsBuilder.fromHttpUrl("HTTPS://www.google.com").build().getScheme());
202196
}
203197

204-
// SPR-10539
205198

206-
@Test(expected = IllegalArgumentException.class)
199+
200+
@Test(expected = IllegalArgumentException.class) // SPR-10539
207201
public void fromHttpUrlStringInvalidIPv6Host() throws URISyntaxException {
208202
UriComponentsBuilder.fromHttpUrl("http://[1abc:2abc:3abc::5ABC:6abc:8080/resource").build().encode();
209203
}
210204

211-
// SPR-10539
212-
213-
@Test
205+
@Test // SPR-10539
214206
public void fromUriStringIPv6Host() throws URISyntaxException {
215-
UriComponents result = UriComponentsBuilder
216-
.fromUriString("http://[1abc:2abc:3abc::5ABC:6abc]:8080/resource").build().encode();
217-
assertEquals("[1abc:2abc:3abc::5ABC:6abc]", result.getHost());
207+
UriComponents result = UriComponentsBuilder
208+
.fromUriString("http://[1abc:2abc:3abc::5ABC:6abc]:8080/resource").build().encode();
209+
assertEquals("[1abc:2abc:3abc::5ABC:6abc]", result.getHost());
218210

219-
UriComponents resultWithScopeId = UriComponentsBuilder
220-
.fromUriString("http://[1abc:2abc:3abc::5ABC:6abc%eth0]:8080/resource").build().encode();
211+
UriComponents resultWithScopeId = UriComponentsBuilder
212+
.fromUriString("http://[1abc:2abc:3abc::5ABC:6abc%eth0]:8080/resource").build().encode();
221213
assertEquals("[1abc:2abc:3abc::5ABC:6abc%25eth0]", resultWithScopeId.getHost());
222214

223-
UriComponents resultIPv4compatible = UriComponentsBuilder
224-
.fromUriString("http://[::192.168.1.1]:8080/resource").build().encode();
215+
UriComponents resultIPv4compatible = UriComponentsBuilder
216+
.fromUriString("http://[::192.168.1.1]:8080/resource").build().encode();
225217
assertEquals("[::192.168.1.1]", resultIPv4compatible.getHost());
226218
}
227219

228-
// SPR-11970
229-
230-
@Test
220+
@Test // SPR-11970
231221
public void fromUriStringNoPathWithReservedCharInQuery() {
232222
UriComponents result = UriComponentsBuilder.fromUriString("http://example.com?foo=bar@baz").build();
233223
assertTrue(StringUtils.isEmpty(result.getUserInfo()));
@@ -253,9 +243,7 @@ public void fromHttpRequest() throws URISyntaxException {
253243
assertEquals("a=1", result.getQuery());
254244
}
255245

256-
// SPR-12771
257-
258-
@Test
246+
@Test // SPR-12771
259247
public void fromHttpRequestResetsPortBeforeSettingIt() throws Exception {
260248
MockHttpServletRequest request = new MockHttpServletRequest();
261249
request.addHeader("X-Forwarded-Proto", "https");
@@ -275,6 +263,67 @@ public void fromHttpRequestResetsPortBeforeSettingIt() throws Exception {
275263
assertEquals("/rest/mobile/users/1", result.getPath());
276264
}
277265

266+
@Test //SPR-14761
267+
public void fromHttpRequestWithForwardedIPv4Host() {
268+
MockHttpServletRequest request = new MockHttpServletRequest();
269+
request.setScheme("http");
270+
request.setServerName("localhost");
271+
request.setServerPort(-1);
272+
request.setRequestURI("/mvc-showcase");
273+
request.addHeader("Forwarded", "host=192.168.0.1");
274+
275+
HttpRequest httpRequest = new ServletServerHttpRequest(request);
276+
UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build();
277+
278+
assertEquals("http://192.168.0.1/mvc-showcase", result.toString());
279+
}
280+
281+
@Test //SPR-14761
282+
public void fromHttpRequestWithForwardedIPv6() {
283+
MockHttpServletRequest request = new MockHttpServletRequest();
284+
request.setScheme("http");
285+
request.setServerName("localhost");
286+
request.setServerPort(-1);
287+
request.setRequestURI("/mvc-showcase");
288+
request.addHeader("Forwarded", "host=[1abc:2abc:3abc::5ABC:6abc]");
289+
290+
HttpRequest httpRequest = new ServletServerHttpRequest(request);
291+
UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build();
292+
293+
assertEquals("http://[1abc:2abc:3abc::5ABC:6abc]/mvc-showcase", result.toString());
294+
}
295+
296+
@Test //SPR-14761
297+
public void fromHttpRequestWithForwardedIPv6Host() {
298+
MockHttpServletRequest request = new MockHttpServletRequest();
299+
request.setScheme("http");
300+
request.setServerName("localhost");
301+
request.setServerPort(-1);
302+
request.setRequestURI("/mvc-showcase");
303+
request.addHeader("X-Forwarded-Host", "[1abc:2abc:3abc::5ABC:6abc]");
304+
305+
HttpRequest httpRequest = new ServletServerHttpRequest(request);
306+
UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build();
307+
308+
assertEquals("http://[1abc:2abc:3abc::5ABC:6abc]/mvc-showcase", result.toString());
309+
}
310+
311+
@Test //SPR-14761
312+
public void fromHttpRequestWithForwardedIPv6HostAndPort() {
313+
MockHttpServletRequest request = new MockHttpServletRequest();
314+
request.setScheme("http");
315+
request.setServerName("localhost");
316+
request.setServerPort(-1);
317+
request.setRequestURI("/mvc-showcase");
318+
request.addHeader("X-Forwarded-Host", "[1abc:2abc:3abc::5ABC:6abc]:8080");
319+
320+
HttpRequest httpRequest = new ServletServerHttpRequest(request);
321+
UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build();
322+
323+
assertEquals("http://[1abc:2abc:3abc::5ABC:6abc]:8080/mvc-showcase", result.toString());
324+
}
325+
326+
278327
@Test
279328
public void fromHttpRequestWithForwardedHost() {
280329
MockHttpServletRequest request = new MockHttpServletRequest();
@@ -290,9 +339,7 @@ public void fromHttpRequestWithForwardedHost() {
290339
assertEquals("http://anotherHost/mvc-showcase", result.toString());
291340
}
292341

293-
// SPR-10701
294-
295-
@Test
342+
@Test // SPR-10701
296343
public void fromHttpRequestWithForwardedHostIncludingPort() {
297344
MockHttpServletRequest request = new MockHttpServletRequest();
298345
request.setScheme("http");
@@ -308,9 +355,7 @@ public void fromHttpRequestWithForwardedHostIncludingPort() {
308355
assertEquals(443, result.getPort());
309356
}
310357

311-
// SPR-11140
312-
313-
@Test
358+
@Test // SPR-11140
314359
public void fromHttpRequestWithForwardedHostMultiValuedHeader() {
315360
MockHttpServletRequest request = new MockHttpServletRequest();
316361
request.setScheme("http");
@@ -325,9 +370,7 @@ public void fromHttpRequestWithForwardedHostMultiValuedHeader() {
325370
assertEquals(-1, result.getPort());
326371
}
327372

328-
// SPR-11855
329-
330-
@Test
373+
@Test // SPR-11855
331374
public void fromHttpRequestWithForwardedHostAndPort() {
332375
MockHttpServletRequest request = new MockHttpServletRequest();
333376
request.setScheme("http");
@@ -343,9 +386,7 @@ public void fromHttpRequestWithForwardedHostAndPort() {
343386
assertEquals(9090, result.getPort());
344387
}
345388

346-
// SPR-11872
347-
348-
@Test
389+
@Test // SPR-11872
349390
public void fromHttpRequestWithForwardedHostWithDefaultPort() {
350391
MockHttpServletRequest request = new MockHttpServletRequest();
351392
request.setScheme("http");
@@ -378,9 +419,7 @@ public void fromHttpRequestWithForwardedHostWithForwardedScheme() {
378419
assertEquals(-1, result.getPort());
379420
}
380421

381-
// SPR-12771
382-
383-
@Test
422+
@Test // SPR-12771
384423
public void fromHttpRequestWithForwardedProtoAndDefaultPort() {
385424
MockHttpServletRequest request = new MockHttpServletRequest();
386425
request.setScheme("http");
@@ -397,9 +436,7 @@ public void fromHttpRequestWithForwardedProtoAndDefaultPort() {
397436
assertEquals("https://84.198.58.199/mvc-showcase", result.toString());
398437
}
399438

400-
// SPR-12813
401-
402-
@Test
439+
@Test // SPR-12813
403440
public void fromHttpRequestWithForwardedPortMultiValueHeader() {
404441
MockHttpServletRequest request = new MockHttpServletRequest();
405442
request.setScheme("http");
@@ -415,9 +452,7 @@ public void fromHttpRequestWithForwardedPortMultiValueHeader() {
415452
assertEquals("http://a.example.org/mvc-showcase", result.toString());
416453
}
417454

418-
// SPR-12816
419-
420-
@Test
455+
@Test // SPR-12816
421456
public void fromHttpRequestWithForwardedProtoMultiValueHeader() {
422457
MockHttpServletRequest request = new MockHttpServletRequest();
423458
request.setScheme("http");
@@ -434,9 +469,7 @@ public void fromHttpRequestWithForwardedProtoMultiValueHeader() {
434469
assertEquals("https://a.example.org/mvc-showcase", result.toString());
435470
}
436471

437-
// SPR-12742
438-
439-
@Test
472+
@Test // SPR-12742
440473
public void fromHttpRequestWithTrailingSlash() throws Exception {
441474
UriComponents before = UriComponentsBuilder.fromPath("/foo/").build();
442475
UriComponents after = UriComponentsBuilder.newInstance().uriComponents(before).build();
@@ -506,9 +539,7 @@ public void pathSegmentsSomeEmpty() {
506539
assertEquals(Arrays.asList("foo", "bar"), result.getPathSegments());
507540
}
508541

509-
// SPR-12398
510-
511-
@Test
542+
@Test // SPR-12398
512543
public void pathWithDuplicateSlashes() throws URISyntaxException {
513544
UriComponents uriComponents = UriComponentsBuilder.fromPath("/foo/////////bar").build();
514545
assertEquals("/foo/bar", uriComponents.getPath());
@@ -645,7 +676,7 @@ public void relativeUrls() throws Exception {
645676
@Test
646677
public void emptySegments() throws Exception {
647678
assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").path("/x/y/z").build().toString(), equalTo("http://example.com/abc/x/y/z"));
648-
assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").pathSegment("x", "y", "z").build().toString(), equalTo("http://example.com/abc/x/y/z"));
679+
assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").pathSegment("x", "y", "z").build().toString(), equalTo("http://example.com/abc/x/y/z"));
649680
assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").path("/x/").path("/y/z").build().toString(), equalTo("http://example.com/abc/x/y/z"));
650681
assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").pathSegment("x").path("y").build().toString(), equalTo("http://example.com/abc/x/y"));
651682
}
@@ -686,9 +717,7 @@ public void testClone() throws URISyntaxException {
686717
assertEquals("f2", result2.getFragment());
687718
}
688719

689-
// SPR-11856
690-
691-
@Test
720+
@Test // SPR-11856
692721
public void fromHttpRequestForwardedHeader() throws Exception {
693722
MockHttpServletRequest request = new MockHttpServletRequest();
694723
request.addHeader("Forwarded", "proto=https; host=84.198.58.199");

0 commit comments

Comments
 (0)