I'm trying to close a Ktor WebSocket in the event that the user tries to connect to the socket with an invalid token. When I close the socket, I never get a Close frame on the client side. What is the appropriate way to close the connection in this case and why might my client never be receiving the close frame?
fun Application.configureWebSocketRouting(
messageProcessor: MessageProcessor
) {
routing {
webSocket(path = CHAT_WEBSOCKET_PATH) {
val session = this
try {
authenticate("access") {
launch { messageProcessor.process(call.principal<User>(), session) }
}
} catch (e: Exception) {
session.close(reason = CloseReason(CloseReason.Codes.VIOLATED_POLICY, "unauthenticated user"))
}
}
}
}
Edit - Here is my client side code. I have handling for both Text and Close frames and do logging for each. I never see any logs for Close frames. Additionally, I have logic to NOT retry in the event I get a close frame and my client continually retries connecting.
override fun initialize(retryCount: Int) {
messageScope.launch {
var reconnected = false
logger.d("Attempting websocket connection...")
try {
session = client.webSocketSession { url(urlString) }
reconnected = true
SocketFlows._connectivityFlow.emit(ConnectivityState.Connected())
logger.d("Connected to websocket")
val messageStates = session.incoming.consumeAsFlow()
.mapNotNull { frame ->
when (frame) {
is Frame.Close -> handleCloseFrame(frame)
is Frame.Text -> handleTextFrame(frame)
else -> { } //NOOP
}
}
.filterIsInstance<SocketEvent.ChatMessageEvent>()
SocketFlows._messageFlow.emitAll(messageStates)
} catch (e: Exception) {
logger.e("Websocket exception", e)
}
logger.d("Disconnected from websocket: ")
//Attempt to reconnect
val currentDelay = (minRetryDelayMs * retryCount).coerceAtMost(maxRetryDelayMs)
SocketFlows._connectivityFlow.emit(ConnectivityState.Disconnected(currentDelay))
delay(currentDelay)
val count = if (reconnected) {
0
} else {
retryCount + 1
}
initialize(count)
}
}
The Close
frames are control frames, which are handled internally and don't get exposed to the session handler. You can observe all WebSockets frames with a traffic analyzing tool like Wireshark.
Also, you can establish a raw WebSockets session with the HttpClient.webSocketRaw
method, where frames of all types are available and must be handled.