-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Version
4.5.14
Context
While debugging an issue with a Quarkus WebSockets Next client I discovered that websocket connections cannot be forced closed. The API exposed by Quarkus is a thin layer over the Vert.x WebSocketClient and I my initial thought was that Quarkus was not properly closing Vert.x resources. However, after some more thorough testing it became clear that there was no obvious way to force a connection closed. It appears that the mechanism exposed by Vert.x requires a websocket closing handshake to succeed before actually closing the underlying socket. In my case, I encountered a server that takes 5 minutes to respond to a CLOSE frame from the client causing Vert.x to hang for 5 minutes on shutdown.
Steps to reproduce
- create a Vert.x WebSocketClient
- connect the WebSocketClient to a server that doesn't respond to CLOSE frames in a timely manner
- close Vert.x
public static void main(String[] args) throws InterruptedException {
var ssl = true;
var port = 443;
var host = "<host>";
var uri = "<uri>";
var connectLatch = new CountDownLatch(1);
var connectStart = new AtomicLong();
var closeFrameLatch = new CountDownLatch(1);
var closeFrameSent = new AtomicLong();
var clientCloseLatch = new CountDownLatch(1);
var clientCloseStart = new AtomicLong();
var vertxCloseLatch = new CountDownLatch(1);
var vertxCloseStart = new AtomicLong();
var vertx = Vertx.vertx();
var wsClient = vertx.createWebSocketClient(new WebSocketClientOptions().setSsl(ssl));
System.out.println("Connecting socket...");
wsClient.connect(new WebSocketConnectOptions()
.setHost(host)
.setPort(port)
.setURI(uri))
.onComplete(connect -> {
if (connect.succeeded()) {
System.out.printf("Socket connected in %1.3f seconds.%n", (System.nanoTime() - connectStart.get()) / 1_000_000_000.0);
var ws = connect.result();
ws.closeHandler(v -> System.out.printf("Received close frame after %1.3f seconds%n", (System.nanoTime() - closeFrameSent.get()) / 1_000_000_000.0));
ws.close().onComplete(close -> {
if (close.succeeded()) {
closeFrameSent.set(System.nanoTime());
System.out.println("Sent close frame.");
} else {
System.out.printf("Failed to send close frame. %s%n", close.cause());
}
closeFrameLatch.countDown();
});
} else {
System.out.printf("Failed to connect socket. %s%n", connect.cause());
closeFrameLatch.countDown();
}
connectLatch.countDown();
});
connectStart.set(System.nanoTime());
connectLatch.await();
closeFrameLatch.await();
System.out.println("Closing client...");
wsClient.close().onComplete(close -> {
if (close.succeeded()) {
System.out.printf("Client closed in %1.3f seconds.%n", (System.nanoTime() - clientCloseStart.get()) / 1_000_000_000.0);
} else {
System.out.printf("Failed to close client. %s%n", close.cause());
}
clientCloseLatch.countDown();
});
clientCloseStart.set(System.nanoTime());
clientCloseLatch.await();
System.out.println("Closing Vert.x...");
vertx.close().onComplete(close -> {
if (close.succeeded()) {
System.out.printf("Vert.x closed in %1.3f seconds.%n", (System.nanoTime() - vertxCloseStart.get()) / 1_000_000_000.0);
} else {
System.out.printf("Failed to close Vert.x. %s%n", close.cause());
}
vertxCloseLatch.countDown();
});
vertxCloseStart.set(System.nanoTime());
vertxCloseLatch.await();
}Normal server results:
Connecting socket...
Socket connected in 0.528 seconds.
Sent close frame.
Closing client...
Received close frame after 0.097 seconds
Client closed in 0.099 seconds.
Closing Vert.x...
Vert.x closed in 0.007 seconds.
Misbehaving server results:
Connecting socket...
Socket connected in 0.700 seconds.
Sent close frame.
Closing client...
Received close frame after 300.003 seconds
Client closed in 310.013 seconds.
Closing Vert.x...
Vert.x closed in 0.018 seconds.
Do you have a reproducer?
No response