WEBSocket + KTOR

Hello everyone I have a small issue and I don’t know how to fix,

I have the following code to set up my WebSocket. I’m trying to build a chat, and I store my sockets in a repository so that when client1 sends a message to client2, I can notify client2 via their WebSocket if they are connected.

My problem is the following: if I turn off the internet on the mobile side for about 3 minutes, my socket is no longer active, probably because the ping/pong fails. However, the finally block is never called, so I never remove the socket from the repository. How can I prevent this memory leak? and to be sure that after 3minute offline the webSocket will be remove from my reposotory
Thank you in advance

fun Application.configureWebSocketRouting() {

    val webSocketRepository by inject<WebSocketRepositoryInterface>()
    val logger by inject<Logger>()

    routing {
       // webSocket("ws") {
        webSocketRaw("ws") {
            val token = call.request.queryParameters["token"]
            if (token == null) {
                close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "No Token"))
                return@webSocketRaw
            }

            val decodedJWT = try { JwtFactory.buildverifier().verify(token) }
            catch (e: Exception) {
                close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "Invalid Token: ${e.message}"))
                return@webSocketRaw
            }

            val userId: UUID = try { UUID.fromString(decodedJWT.getClaim(JwtClaimConstant.claimUserId).asString())  }
            catch (e: Exception) {
                close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "Invalid Token: ${e.message}"))
                return@webSocketRaw
            }

            val sessionId = decodedJWT.id?.let {
                runCatching { UUID.fromString(it) }.getOrNull()
            } ?: run {
                close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "Invalid or missing sessionId (jti)"))
                return@webSocketRaw
            }
            logger.info("$userId is connected")

            try {
                webSocketRepository.onJoin(userId, sessionId,this)
                incoming.consumeEach {
                    when (it) {
                        is Frame.Text -> {
                            val text = it.readText()
                            println("tototot Received: $text")
                        }
                        is Frame.Close -> {
                            println("tototot WebSocket closed by server with reason: ${it.readReason()}")
                        }
                        else -> {}
                    }


                }
            } catch (e: Exception) {
                println("tototot error $e")
             } finally {
                println("tototot finally remove")
                webSocketRepository.removeSocket(userId, this)
                logger.info("$userId not connected anymore")
            }
        }
    }
}

// configuration

fun Application.configureWebSocket(){
    install(WebSockets) {
        pingPeriod = Duration.ofSeconds(15)
        timeout = Duration.ofSeconds(30)
        maxFrameSize = kotlin.Long.MAX_VALUE
        masking = false

    }
}

Could you post a link to your project on GitHub or wherever you have it? This code snippet is not enough for me since I haven’t used websockets in KTOR yet, so there’s a lot of bits of your code that I don’t understand. For example, what is incoming? Where is it defined?

hello thank you :slight_smile:
What do you need to have ? :slight_smile:
incoming it provide by websocket to have access to message from websock it’s from ktor side

1 Like

If the finally block is not called, it will essentially always be because your try{} block never exits. It needs to either return or throw.

I am not familiar with ktor either, but I don’t see where your consumeEach block would exit. Maybe incoming#consumeEach is supposed to throw ? Barring that, on Frame.Close your code just prints and continues to loop, and it also does nothing for any other event that’s not text. I’d expect some return somewhere, maybe in the handling of Frame.Close.