Handle early closure of WebSocket connection
Ensure the error is routed to the graphQlSessionSink when the WebSocket connection is closed before the GraphQLSession is initialized. Closes gh-1098
This commit is contained in:
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -339,7 +339,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
|||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(closeStatusMessage);
|
logger.debug(closeStatusMessage);
|
||||||
}
|
}
|
||||||
graphQlSession.terminateRequests(closeStatusMessage, closeStatus);
|
terminateGraphQlSession(graphQlSession, closeStatus, closeStatusMessage, null);
|
||||||
})
|
})
|
||||||
.doOnError((cause) -> {
|
.doOnError((cause) -> {
|
||||||
CloseStatus closeStatus = CloseStatus.NO_STATUS_CODE;
|
CloseStatus closeStatus = CloseStatus.NO_STATUS_CODE;
|
||||||
@@ -347,11 +347,21 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
|||||||
if (logger.isErrorEnabled()) {
|
if (logger.isErrorEnabled()) {
|
||||||
logger.error(closeStatusMessage);
|
logger.error(closeStatusMessage);
|
||||||
}
|
}
|
||||||
graphQlSession.terminateRequests(closeStatusMessage, closeStatus);
|
terminateGraphQlSession(graphQlSession, closeStatus, closeStatusMessage, cause);
|
||||||
})
|
})
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void terminateGraphQlSession(
|
||||||
|
GraphQlSession session, CloseStatus closeStatus, String closeStatusMessage, @Nullable Throwable cause) {
|
||||||
|
|
||||||
|
if (sessionNotInitialized()) {
|
||||||
|
this.graphQlSessionSink.tryEmitError(new IllegalStateException(closeStatusMessage, cause));
|
||||||
|
this.graphQlSessionSink = Sinks.unsafe().one();
|
||||||
|
}
|
||||||
|
session.terminateRequests(closeStatusMessage, closeStatus);
|
||||||
|
}
|
||||||
|
|
||||||
private String initCloseStatusMessage(CloseStatus status, @Nullable Throwable ex, GraphQlSession session) {
|
private String initCloseStatusMessage(CloseStatus status, @Nullable Throwable ex, GraphQlSession session) {
|
||||||
String reason = session + " disconnected";
|
String reason = session + " disconnected";
|
||||||
if (isStopped()) {
|
if (isStopped()) {
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -317,7 +317,7 @@ public class WebSocketGraphQlTransportTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void errorBeforeConnectionAck() {
|
void errorBeforeConnectionAckWithStart() {
|
||||||
|
|
||||||
// Errors before GraphQL session initialized should be routed, no hanging on start
|
// Errors before GraphQL session initialized should be routed, no hanging on start
|
||||||
|
|
||||||
@@ -325,9 +325,26 @@ public class WebSocketGraphQlTransportTests {
|
|||||||
handler.connectionInitHandler(initPayload -> Mono.error(new IllegalStateException("boo")));
|
handler.connectionInitHandler(initPayload -> Mono.error(new IllegalStateException("boo")));
|
||||||
|
|
||||||
TestWebSocketClient client = new TestWebSocketClient(handler);
|
TestWebSocketClient client = new TestWebSocketClient(handler);
|
||||||
|
String expectedMessage = "disconnected with CloseStatus[code=1002, reason=null]";
|
||||||
|
|
||||||
StepVerifier.create(createTransport(client).start())
|
StepVerifier.create(createTransport(client).start())
|
||||||
.expectErrorMessage("boo")
|
.expectErrorSatisfies(ex -> assertThat(ex).hasMessageEndingWith(expectedMessage))
|
||||||
|
.verify(TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-1098
|
||||||
|
void errorBeforeConnectionAckWithRequest() {
|
||||||
|
|
||||||
|
// Errors before GraphQL session initialized should be routed, no hanging on start
|
||||||
|
|
||||||
|
MockGraphQlWebSocketServer handler = new MockGraphQlWebSocketServer();
|
||||||
|
handler.connectionInitHandler(initPayload -> Mono.error(new IllegalStateException("boo")));
|
||||||
|
|
||||||
|
TestWebSocketClient client = new TestWebSocketClient(session -> session.close(CloseStatus.POLICY_VIOLATION));
|
||||||
|
String expectedMessage = "disconnected with CloseStatus[code=1008, reason=null]";
|
||||||
|
|
||||||
|
StepVerifier.create(createTransport(client).execute(new DefaultGraphQlRequest("{Query1}")))
|
||||||
|
.expectErrorSatisfies(ex -> assertThat(ex).hasMessageEndingWith(expectedMessage))
|
||||||
.verify(TIMEOUT);
|
.verify(TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user