skip to Main Content

I’ve found documentation for an insecure ktor websocket server (ws://…):

https://ktor.io/docs/creating-web-socket-chat.html#creating-the-chat-client

I’ve found documentation for a secure ktor http server (https://…)

https://github.com/ktorio/ktor-documentation/tree/main/codeSnippets/snippets/ssl-embedded-server

But I can’t seem to find or figure out how to serve a secure ktor websocket server (wss://…)

I’d rather not use an SSL reverse proxy like nginx in front of it.

EDIT: Here’s code:

import io.ktor.application.*
import io.ktor.http.cio.websocket.*
import io.ktor.network.tls.certificates.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.websocket.*
import java.io.*

fun main() {
    val keyStoreFile = File("build/keystore.jks")
    val keystore = generateCertificate(
        file = keyStoreFile,
        keyAlias = "sampleAlias",
        keyPassword = "foobar",
        jksPassword = "foobar"
    )

    val environment = applicationEngineEnvironment {
        sslConnector(
            keyStore = keystore,
            keyAlias = "sampleAlias",
            keyStorePassword = { "foobar".toCharArray() },
            privateKeyPassword = { "foobar".toCharArray() }) {
            port = 8443
            keyStorePath = keyStoreFile
        }
        module(Application::module)
    }

    embeddedServer(Netty, environment).start(wait = true)
}

private fun Application.module() {
    install(WebSockets)
    routing {
        get("/") { // works at https://localhost:8443 in Firefox after approving cert
            call.respondText("This is https")
        }
        webSocket("/chat") { // fails at wss://localhost:8443/chat in Websocket js client with "Firefox can’t establish a connection to the server"
            send("This is wss")
        }
    }
}

2

Answers


  1. Chosen as BEST ANSWER

    The problem was using a self-signed certificate. I tried Chrome and set it to accept a self-signed certificate on localhost, which works. The error message Firebox provided suggested that a connection could not be established, when in fact it rejected the certificate.

    A secure websocket server using ktor does in fact work like @AleksiTierman suggested. It is configured the same way as an https server.


  2. I’ve tested this with postman and works fine.
    You can not use ws:// or wss:// protocols in browsers, use postman or other websocket clients instead.

    Application.kt :

    import com.example.plugins.configureRouting
    import com.example.plugins.configureSerialization
    import com.example.plugins.configureSockets
    import io.ktor.network.tls.certificates.*
    import io.ktor.server.engine.*
    import io.ktor.server.netty.*
    import org.slf4j.LoggerFactory
    import java.io.File
    
    
    fun main() {
        val keyStoreFile = File("build/keystore.jks")
        val keystore = generateCertificate(
            file = keyStoreFile,
            keyAlias = "sampleAlias",
            keyPassword = "foobar",
            jksPassword = "foobar"
        )
    
        val environment = applicationEngineEnvironment {
            log = LoggerFactory.getLogger("ktor.application")
            
            // no ssl, ws://127.0.0.1:6751
            connector {
                host = "127.0.0.1"
                port = 6751
            }
            
            // with ssl, wss://127.0.0.1:6752
            sslConnector(
                keyStore = keystore,
                keyAlias = "sampleAlias",
                keyStorePassword = { "foobar".toCharArray() },
                privateKeyPassword = { "foobar".toCharArray() }) {
                keyStorePath = keyStoreFile
                host = "127.0.0.1"
                port = 6752
            }
    
            this.module {
                configureSockets()
                configureRouting()
            }
        }
    
        embeddedServer(Netty, environment).start(wait = true)
    
    /*
        embeddedServer(Netty, port = 6752, host = "127.0.0.1") {
            configureSockets()
            configureRouting()
        }.start(wait = true)
    
     */
    }
    

    Sockets.kt :

    import io.ktor.serialization.kotlinx.*
    import io.ktor.server.application.*
    import io.ktor.server.routing.*
    import io.ktor.server.websocket.*
    import io.ktor.websocket.*
    import kotlinx.coroutines.channels.ClosedReceiveChannelException
    import kotlinx.serialization.json.Json
    import java.time.Duration
    
    
    fun Application.configureSockets() {
        routing {
            [email protected](WebSockets) {
                contentConverter = KotlinxWebsocketSerializationConverter(Json)
                pingPeriod = Duration.ofSeconds(3)
                timeout = Duration.ofSeconds(5)
                maxFrameSize = Long.MAX_VALUE
                masking = false
            }
    
            webSocket("/echo") {
                println("onConnect")
                try {
                    for (frame in incoming) {
                        val text = (frame as Frame.Text).readText()
                        println("onMessage")
                        send(Frame.Text(text))
                    }
                } catch (e: ClosedReceiveChannelException) {
                    e.printStackTrace()
                    println("onClose ${closeReason.await()}")
                } catch (e: Throwable) {
                    e.printStackTrace()
                    println("onError ${closeReason.await()}")
                }
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search