Skip to content

Commit 3cadde0

Browse files
committed
Protect against available port actually being unavailable
Closes gh-19355
1 parent f75c73e commit 3cadde0

File tree

3 files changed

+50
-19
lines changed

3 files changed

+50
-19
lines changed

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ public void defaultUriEncoding() {
288288
}
289289

290290
@Test
291-
public void primaryConnectorPortClashThrowsWebServerException() throws IOException {
291+
public void primaryConnectorPortClashThrowsWebServerException() throws Exception {
292292
doWithBlockedPort((port) -> {
293293
TomcatServletWebServerFactory factory = getFactory();
294294
factory.setPort(port);
@@ -300,7 +300,7 @@ public void primaryConnectorPortClashThrowsWebServerException() throws IOExcepti
300300
}
301301

302302
@Test
303-
public void startupFailureDoesNotResultInUnstoppedThreadsBeingReported() throws IOException {
303+
public void startupFailureDoesNotResultInUnstoppedThreadsBeingReported() throws Exception {
304304
super.portClashOfPrimaryConnectorResultsInPortInUseException();
305305
String string = this.outputCapture.toString();
306306
assertThat(string).doesNotContain("appears to have started a thread named [main]");

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.security.KeyStore;
2424
import java.time.Duration;
2525
import java.util.Arrays;
26+
import java.util.concurrent.Callable;
2627

2728
import javax.net.ssl.KeyManagerFactory;
2829
import javax.net.ssl.SSLException;
@@ -92,12 +93,15 @@ public void tearDown() {
9293
protected abstract AbstractReactiveWebServerFactory getFactory();
9394

9495
@Test
95-
public void specificPort() {
96+
public void specificPort() throws Exception {
9697
AbstractReactiveWebServerFactory factory = getFactory();
97-
int specificPort = SocketUtils.findAvailableTcpPort(41000);
98-
factory.setPort(specificPort);
99-
this.webServer = factory.getWebServer(new EchoHandler());
100-
this.webServer.start();
98+
int specificPort = doWithRetry(() -> {
99+
int port = SocketUtils.findAvailableTcpPort(41000);
100+
factory.setPort(port);
101+
this.webServer = factory.getWebServer(new EchoHandler());
102+
this.webServer.start();
103+
return port;
104+
});
101105
Mono<String> result = getWebClient().build().post().uri("/test").contentType(MediaType.TEXT_PLAIN)
102106
.body(BodyInserters.fromObject("Hello World")).exchange()
103107
.flatMap((response) -> response.bodyToMono(String.class));
@@ -113,6 +117,7 @@ public void basicSslFromClassPath() {
113117
@Test
114118
public void basicSslFromFileSystem() {
115119
testBasicSslWithKeyStore("src/test/resources/test.jks", "password");
120+
116121
}
117122

118123
protected final void testBasicSslWithKeyStore(String keyStore, String keyPassword) {
@@ -335,6 +340,19 @@ protected void assertForwardHeaderIsUsed(AbstractReactiveWebServerFactory factor
335340
assertThat(body).isEqualTo("https");
336341
}
337342

343+
private <T> T doWithRetry(Callable<T> action) throws Exception {
344+
Exception lastFailure = null;
345+
for (int i = 0; i < 10; i++) {
346+
try {
347+
return action.call();
348+
}
349+
catch (Exception ex) {
350+
lastFailure = ex;
351+
}
352+
}
353+
throw new IllegalStateException("Action was not successful in 10 attempts", lastFailure);
354+
}
355+
338356
protected static class EchoHandler implements HttpHandler {
339357

340358
public EchoHandler() {

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.HashMap;
4545
import java.util.Locale;
4646
import java.util.Map;
47+
import java.util.concurrent.Callable;
4748
import java.util.concurrent.atomic.AtomicBoolean;
4849
import java.util.concurrent.atomic.AtomicReference;
4950
import java.util.zip.GZIPInputStream;
@@ -248,10 +249,13 @@ public void loadOnStartAfterContextIsInitialized() {
248249
@Test
249250
public void specificPort() throws Exception {
250251
AbstractServletWebServerFactory factory = getFactory();
251-
int specificPort = SocketUtils.findAvailableTcpPort(41000);
252-
factory.setPort(specificPort);
253-
this.webServer = factory.getWebServer(exampleServletRegistration());
254-
this.webServer.start();
252+
int specificPort = doWithRetry(() -> {
253+
int port = SocketUtils.findAvailableTcpPort(41000);
254+
factory.setPort(port);
255+
this.webServer = factory.getWebServer(exampleServletRegistration());
256+
this.webServer.start();
257+
return port;
258+
});
255259
assertThat(getResponse("http://localhost:" + specificPort + "/hello")).isEqualTo("Hello World");
256260
assertThat(this.webServer.getPort()).isEqualTo(specificPort);
257261
}
@@ -822,7 +826,7 @@ public void serverHeaderIsDisabledByDefault() throws Exception {
822826
}
823827

824828
@Test
825-
public void portClashOfPrimaryConnectorResultsInPortInUseException() throws IOException {
829+
public void portClashOfPrimaryConnectorResultsInPortInUseException() throws Exception {
826830
doWithBlockedPort((port) -> {
827831
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> {
828832
AbstractServletWebServerFactory factory = getFactory();
@@ -834,7 +838,7 @@ public void portClashOfPrimaryConnectorResultsInPortInUseException() throws IOEx
834838
}
835839

836840
@Test
837-
public void portClashOfSecondaryConnectorResultsInPortInUseException() throws IOException {
841+
public void portClashOfSecondaryConnectorResultsInPortInUseException() throws Exception {
838842
doWithBlockedPort((port) -> {
839843
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> {
840844
AbstractServletWebServerFactory factory = getFactory();
@@ -1128,19 +1132,28 @@ public void service(ServletRequest request, ServletResponse response) throws IOE
11281132
return bean;
11291133
}
11301134

1131-
protected final void doWithBlockedPort(BlockedPortAction action) throws IOException {
1132-
int port = SocketUtils.findAvailableTcpPort(40000);
1133-
ServerSocket serverSocket = new ServerSocket();
1135+
private <T> T doWithRetry(Callable<T> action) throws Exception {
1136+
Exception lastFailure = null;
11341137
for (int i = 0; i < 10; i++) {
11351138
try {
1136-
serverSocket.bind(new InetSocketAddress(port));
1137-
break;
1139+
return action.call();
11381140
}
11391141
catch (Exception ex) {
1142+
lastFailure = ex;
11401143
}
11411144
}
1145+
throw new IllegalStateException("Action was not successful in 10 attempts", lastFailure);
1146+
}
1147+
1148+
protected final void doWithBlockedPort(BlockedPortAction action) throws Exception {
1149+
ServerSocket serverSocket = new ServerSocket();
1150+
int blockedPort = doWithRetry(() -> {
1151+
int port = SocketUtils.findAvailableTcpPort(40000);
1152+
serverSocket.bind(new InetSocketAddress(port));
1153+
return port;
1154+
});
11421155
try {
1143-
action.run(port);
1156+
action.run(blockedPort);
11441157
}
11451158
finally {
11461159
serverSocket.close();

0 commit comments

Comments
 (0)