Skip to content

Conversation

@vietj
Copy link
Member

@vietj vietj commented Jun 13, 2025

Motivation:

When closing a client WebSocket, the client sends a close frame and expects the server to respond with a close frame and then close the connection.

Some servers can misbehave by ignoring the close frame and this let the client deal with the socket.

The client has a closing timeout which handle the case where the server responds with a close frame but does not actually close the connection.

The timeout can be extended to handle the case where the server does not send a close frame.

Changes:

Start the closing timeout after sending successfully the close frame instead of upon reception of the server close frame.

@emattheis
Copy link

@vietj this code resolves #5592 but introduces a small concern: it is not possible to determine if the connection was closed normally within the close handler since the close status is set to the status of the initial frame in the closing handshake. I suppose this is an entirely separate issue, but thought I would point it out. Perhaps we should avoid firing the close handler if a close frame was not received?

@vietj
Copy link
Member Author

vietj commented Jun 13, 2025

@vietj this code resolves #5592 but introduces a small concern: it is not possible to determine if the connection was closed normally within the close handler since the close status is set to the status of the initial frame in the closing handshake. I suppose this is an entirely separate issue, but thought I would point it out. Perhaps we should avoid firing the close handler if a close frame was not received?

@vietj this code resolves #5592 but introduces a small concern: it is not possible to determine if the connection was closed normally within the close handler since the close status is set to the status of the initial frame in the closing handshake. I suppose this is an entirely separate issue, but thought I would point it out. Perhaps we should avoid firing the close handler if a close frame was not received?

that's a good point, we can use the status code 1006 to indicate that : Abnormal Closure

@vietj
Copy link
Member Author

vietj commented Jun 13, 2025

@emattheis I think actually it does not apply to this case because the client initiates the close operation and provides a status code. The server should respond with a close frame that is an echo of the client sent close frame so the status should be the same value as provided by the client that initiated the closure.

@vietj vietj force-pushed the start-client-websocket-timeout-on-close-frame-write branch from 5dde6a4 to ce8502c Compare June 13, 2025 14:25
@emattheis
Copy link

that's a good point, we can use the status code 1006 to indicate that : Abnormal Closure

This gets tricky, though, because the sent and received close frames should always match. We don't want to misrepresent what went over the wire. Per the Vert.x API the close status and reason should be null, when no close frame has been received from the remote endpoint. I think the deeper issue here is that the status and reason are sent to the value from the local endpoint if initiated locally.

@vietj
Copy link
Member Author

vietj commented Jun 13, 2025

@emattheis the main difference with the normal case is that vertx end handler will not be called and instead the exception handler will be

@vietj
Copy link
Member Author

vietj commented Jun 13, 2025

@emattheis what you are saying kind of makes sense too, but changing that would be breaking with users expecting a status code not null

@vietj
Copy link
Member Author

vietj commented Jun 13, 2025

I think the only case then is to use status code 1006 instead which signals this case instead

…ame.

Motivation:

When closing a client WebSocket, the client sends a close frame and expects the server to respond with a close frame and then close the connection.

Some servers can misbhave by ignoring the close frame and this let the client deal with the socket.

The client has a closing timeout which handle the case where the server responds with a close frame but does not actually close the connection.

The timeout can be extended to handle the case where the server does not send a close frame.

Changes:

Start the closing timeout after sending succesfully the close frame instead of upon reception of the server close frame.

When a WebSocket closes without receiving a close frame, the WebSocket status code is the value 1006 (per https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5)
@vietj vietj force-pushed the start-client-websocket-timeout-on-close-frame-write branch from ce8502c to f5b69e8 Compare June 13, 2025 14:46
@vietj
Copy link
Member Author

vietj commented Jun 13, 2025

@emattheis I updated the code with the usage of 1006 (https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5)

@emattheis
Copy link

emattheis commented Jun 13, 2025

@emattheis I updated the code with the usage of 1006 (https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5)

Personally, I think this is more confusing, because we have no way to tell if that was actually the closure code set by the client or the server or Vert.x. At least in the current release we know the code was set (explicitly or implicitly) by calling WebSocket::close, or was received from the remote endpoint.

On the other hand, I guess if I really want to know if the close frame was received from the remote endpoint I can register an exception handler and look for the closed exception, so it won't matter if the status code is changed by Vert.x.

@vietj vietj merged commit f1bdfa7 into 4.x Jun 13, 2025
8 of 13 checks passed
@vietj vietj deleted the start-client-websocket-timeout-on-close-frame-write branch June 13, 2025 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vert.x hangs indefinitely on close if websocket client does not receive CLOSE frame from server

2 participants