diff --git a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java index 2239eee4082..48c21c8840f 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java +++ b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java @@ -52,7 +52,7 @@ final class HierarchicalUriComponents extends UriComponents { private final String host; - private final int port; + private final String port; private final PathComponent path; @@ -73,7 +73,7 @@ final class HierarchicalUriComponents extends UriComponents { * @param encoded whether the components are already encoded * @param verify whether the components need to be checked for illegal characters */ - HierarchicalUriComponents(String scheme, String userInfo, String host, int port, PathComponent path, + HierarchicalUriComponents(String scheme, String userInfo, String host, String port, PathComponent path, MultiValueMap queryParams, String fragment, boolean encoded, boolean verify) { super(scheme, fragment); @@ -109,7 +109,7 @@ final class HierarchicalUriComponents extends UriComponents { @Override public int getPort() { - return this.port; + return Integer.parseInt(this.port); } @Override @@ -308,6 +308,7 @@ final class HierarchicalUriComponents extends UriComponents { String expandedScheme = expandUriComponent(getScheme(), uriVariables); String expandedUserInfo = expandUriComponent(this.userInfo, uriVariables); String expandedHost = expandUriComponent(this.host, uriVariables); + String expandedPort = expandUriComponent(this.port, uriVariables); PathComponent expandedPath = this.path.expand(uriVariables); MultiValueMap expandedQueryParams = new LinkedMultiValueMap(this.queryParams.size()); @@ -321,7 +322,7 @@ final class HierarchicalUriComponents extends UriComponents { expandedQueryParams.put(expandedName, expandedValues); } String expandedFragment = expandUriComponent(this.getFragment(), uriVariables); - return new HierarchicalUriComponents(expandedScheme, expandedUserInfo, expandedHost, this.port, expandedPath, + return new HierarchicalUriComponents(expandedScheme, expandedUserInfo, expandedHost, expandedPort, expandedPath, expandedQueryParams, expandedFragment, false, false); } @@ -359,7 +360,7 @@ final class HierarchicalUriComponents extends UriComponents { if (this.host != null) { uriBuilder.append(host); } - if (this.port != -1) { + if (!"-1".equals(this.port)) { uriBuilder.append(':'); uriBuilder.append(port); } @@ -432,7 +433,7 @@ final class HierarchicalUriComponents extends UriComponents { int result = ObjectUtils.nullSafeHashCode(getScheme()); result = 31 * result + ObjectUtils.nullSafeHashCode(this.userInfo); result = 31 * result + ObjectUtils.nullSafeHashCode(this.host); - result = 31 * result + this.port; + result = 31 * result + ObjectUtils.nullSafeHashCode(this.port); result = 31 * result + this.path.hashCode(); result = 31 * result + this.queryParams.hashCode(); result = 31 * result + ObjectUtils.nullSafeHashCode(getFragment()); 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 6c8fdaad5a6..f6582dc94c8 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 @@ -70,7 +70,7 @@ public class UriComponentsBuilder { private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")"; - private static final String PORT_PATTERN = "(\\d*)"; + private static final String PORT_PATTERN = "(\\d*(?:\\{[^/]+?\\})?)"; private static final String PATH_PATTERN = "([^?#]*)"; @@ -96,7 +96,7 @@ public class UriComponentsBuilder { private String host; - private int port = -1; + private String port = "-1"; private CompositePathComponentBuilder pathBuilder = new CompositePathComponentBuilder(); @@ -192,7 +192,7 @@ public class UriComponentsBuilder { builder.userInfo(userInfo); builder.host(host); if (StringUtils.hasLength(port)) { - builder.port(Integer.parseInt(port)); + builder.port(port); } builder.path(path); builder.query(query); @@ -237,7 +237,7 @@ public class UriComponentsBuilder { builder.host(host); String port = m.group(7); if (StringUtils.hasLength(port)) { - builder.port(Integer.parseInt(port)); + builder.port(port); } builder.path(m.group(8)); builder.query(m.group(10)); @@ -272,7 +272,7 @@ public class UriComponentsBuilder { return new OpaqueUriComponents(this.scheme, this.ssp, this.fragment); } else { - return new HierarchicalUriComponents(this.scheme, this.userInfo, this.host, this.port, + return new HierarchicalUriComponents(this.scheme, this.userInfo, this.host, String.valueOf(this.port), this.pathBuilder.build(), this.queryParams, this.fragment, encoded, true); } } @@ -333,7 +333,7 @@ public class UriComponentsBuilder { this.host = uri.getHost(); } if (uri.getPort() != -1) { - this.port = uri.getPort(); + this.port = String.valueOf(uri.getPort()); } if (StringUtils.hasLength(uri.getRawPath())) { this.pathBuilder = new CompositePathComponentBuilder(uri.getRawPath()); @@ -353,7 +353,7 @@ public class UriComponentsBuilder { private void resetHierarchicalComponents() { this.userInfo = null; this.host = null; - this.port = -1; + this.port = "-1"; this.pathBuilder = new CompositePathComponentBuilder(); this.queryParams.clear(); } @@ -393,7 +393,7 @@ public class UriComponentsBuilder { this.host = uriComponents.getHost(); } if (uriComponents.getPort() != -1) { - this.port = uriComponents.getPort(); + this.port = String.valueOf(uriComponents.getPort()); } if (StringUtils.hasLength(uriComponents.getPath())) { List segments = uriComponents.getPathSegments(); @@ -462,6 +462,18 @@ public class UriComponentsBuilder { */ public UriComponentsBuilder port(int port) { Assert.isTrue(port >= -1, "'port' must not be < -1"); + this.port = String.valueOf(port); + resetSchemeSpecificPart(); + return this; + } + + /** + * Set the URI port. Passing {@code "-1"} will clear the port of this builder. + * The given port may contain URI template variables. + * @param port the URI port + * @return this UriComponentsBuilder + */ + public UriComponentsBuilder port(String port) { this.port = port; resetSchemeSpecificPart(); return this; diff --git a/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java b/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java index 390acb60c20..f0d06f49d86 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java +++ b/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java @@ -54,6 +54,7 @@ public class AbstractJettyServerTestCase { protected static String helloWorld = "H\u00e9llo W\u00f6rld"; + protected static int port; protected static String baseUrl; protected static MediaType textContentType; @@ -63,7 +64,7 @@ public class AbstractJettyServerTestCase { @BeforeClass public static void startJettyServer() throws Exception { - int port = SocketUtils.findAvailableTcpPort(); + port = SocketUtils.findAvailableTcpPort(); jettyServer = new Server(port); baseUrl = "http://localhost:" + port; ServletContextHandler handler = new ServletContextHandler(); diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java index 07bc0420186..0f86efd5473 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java @@ -229,6 +229,14 @@ public class RestTemplateIntegrationTests extends AbstractJettyServerTestCase { assertTrue(s.contains("\"without\":\"without\"")); } + // SPR-12123 + + @Test + public void serverPort() { + String s = template.getForObject("http://localhost:{port}/get", String.class, port); + assertEquals("Invalid content", helloWorld, s); + } + public interface MyJacksonView1 {}; public interface MyJacksonView2 {}; diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsTests.java index 458a08a5936..2d71a8234c2 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsTests.java @@ -80,6 +80,28 @@ public class UriComponentsTests { assertEquals("http://example.com/1 2 3 4", uriComponents.toUriString()); } + // SPR-12123 + + @Test + public void port() { + UriComponents uriComponents1 = UriComponentsBuilder.fromUriString( + "http://example.com:8080/bar").build(); + UriComponents uriComponents2 = UriComponentsBuilder.fromUriString( + "http://example.com/bar").port(8080).build(); + UriComponents uriComponents3 = UriComponentsBuilder.fromUriString( + "http://example.com/bar").port("{port}").build().expand(8080); + UriComponents uriComponents4 = UriComponentsBuilder.fromUriString( + "http://example.com/bar").port("808{digit}").build().expand(0); + assertEquals(8080, uriComponents1.getPort()); + assertEquals("http://example.com:8080/bar", uriComponents1.toUriString()); + assertEquals(8080, uriComponents2.getPort()); + assertEquals("http://example.com:8080/bar", uriComponents2.toUriString()); + assertEquals(8080, uriComponents3.getPort()); + assertEquals("http://example.com:8080/bar", uriComponents3.toUriString()); + assertEquals(8080, uriComponents4.getPort()); + assertEquals("http://example.com:8080/bar", uriComponents4.toUriString()); + } + @Test(expected = IllegalStateException.class) public void expandEncoded() { UriComponentsBuilder.fromPath("/{foo}").build().encode().expand("bar");