From 537add5cf0a75c34950cedd5231e2b58c344ea05 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Fri, 3 Nov 2023 11:57:01 -0400 Subject: [PATCH] GH-8785: Propagate WebSocket client connect fail Fixes https://github.com/spring-projects/spring-integration/issues/8785 The `ClientWebSocketContainer.start()` delegates to the `IntegrationWebSocketConnectionManager` which performs an async connection to the server. * Wait for `connectionLatch` in the `ClientWebSocketContainer.start()` and check for `this.openConnectionException != null` to re-throw. Mark `ClientWebSocketContainer` as stopped in that case **Cherry-pick to `6.1.x` & `6.0.x`** --- .../websocket/ClientWebSocketContainer.java | 14 ++++++++++++++ .../ClientWebSocketContainerTests.java | 19 ++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ClientWebSocketContainer.java b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ClientWebSocketContainer.java index b89db27cc3..2046329f6c 100644 --- a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ClientWebSocketContainer.java +++ b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ClientWebSocketContainer.java @@ -192,6 +192,20 @@ public final class ClientWebSocketContainer extends IntegrationWebSocketContaine this.openConnectionException = null; this.connectionLatch = new CountDownLatch(1); this.connectionManager.start(); + + try { + this.connectionLatch.await(this.connectionTimeout, TimeUnit.SECONDS); + } + catch (InterruptedException ex) { + logger.error("'clientSession' has not been established during 'openConnection'"); + } + + if (this.openConnectionException != null) { + // The next 'this.connectionManager.stop()' call resets 'this.openConnectionException' to null + IllegalStateException exceptionToRethrow = new IllegalStateException(this.openConnectionException); + this.connectionManager.stop(); + throw exceptionToRethrow; + } } } finally { diff --git a/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/ClientWebSocketContainerTests.java b/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/ClientWebSocketContainerTests.java index 084962e9ab..ea1a83e063 100644 --- a/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/ClientWebSocketContainerTests.java +++ b/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/ClientWebSocketContainerTests.java @@ -28,6 +28,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import jakarta.websocket.DeploymentException; import org.apache.tomcat.websocket.Constants; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -126,10 +127,8 @@ public class ClientWebSocketContainerTests { failure.set(true); - container.start(); - assertThatIllegalStateException() - .isThrownBy(() -> container.getSession(null)) + .isThrownBy(container::start) .withCauseInstanceOf(CancellationException.class); failure.set(false); @@ -170,6 +169,20 @@ public class ClientWebSocketContainerTests { .isEqualTo(ConcurrentWebSocketSessionDecorator.OverflowStrategy.DROP); } + @Test + public void webSocketContainerFailsOnStartForInvalidUrl() { + StandardWebSocketClient webSocketClient = new StandardWebSocketClient(); + + ClientWebSocketContainer container = + new ClientWebSocketContainer(webSocketClient, server.getWsBaseUrl() + "/no_such_endpoint"); + + assertThatIllegalStateException() + .isThrownBy(container::start) + .withCauseExactlyInstanceOf(DeploymentException.class) + .withStackTraceContaining( + "The HTTP response from the server [404] did not permit the HTTP upgrade to WebSocket"); + } + private static class TestWebSocketListener implements WebSocketListener { public final CountDownLatch messageLatch = new CountDownLatch(1);