https://kotlinlang.org logo
Join Slack
Powered by
# ktor
  • n

    Nicolas

    06/23/2025, 10:54 AM
    Hello Teams 😄 I am doing my server with ktor and I used webSocket to do a chat, I hope my question will not be dumb because I am iOS developer at firt 😄 I have the following code to set up my WebSocket with ktor. My problem is the following: if I turn off the internet on the mobile side (iOS) after setup a connection with the socket for about 3 minutes, my socket is no longer active, probably because the ping/pong fails on front side from iOS side . However, the finally block on server side is often called a long time ago, so the webSocket is close after 20 minutes. How can I prevent this memory leak? and to be sure that after 3minute offline from the client, the webSocket will be remove from my server please. I Have setup the ping pong like this
    Copy code
    fun Application.configureWebSocket(){
        install(WebSockets) {
            pingPeriod = 15.seconds
            timeout = 15.seconds
            maxFrameSize = kotlin.Long.MAX_VALUE
            masking = false
    
        }
    }
    Copy code
    routing {
        webSocket("ws") {
            val token = call.request.queryParameters["token"]
            if (token == null) {
                close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "No Token"))
                return@webSocket
            }
    
            val decodedJWT = try { JwtFactory.buildverifier().verify(token) }
            catch (e: Exception) {
                close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "Invalid Token: ${e.message}"))
                return@webSocket
            }
    
            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@webSocket
            }
    
            val sessionId = decodedJWT.id?.let {
                runCatching { UUID.fromString(it) }.getOrNull()
            } ?: run {
                close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "Invalid or missing sessionId (jti)"))
                return@webSocket
            }
            <http://logger.info|logger.info>("$userId is connected")
    
            try {
                println("$userId start")
                incoming.consumeEach {
                    when (it) {
    
                        is Frame.Text -> {
                            val text = it.readText()
                            println("tototot $userId Received: $text")
                        }
                        is Frame.Close -> {
                            println("tototot $userId WebSocket closed by server with reason: ${it.readReason()}")
                        }
                        is Frame.Ping -> {
                            println("tototot $userId ping: $it")
                        }
                        is Frame.Pong -> {
                            println("tototot $userId pong: $it")
                        }  else -> {
                            println("tototot $userId else: $it")
                        }
                    }
    
    
                }
            } catch (e: Exception) {
                println("$userId error $e")
             } finally {
                println("$userId finally remove")
            }
    
            println("$userId end")
        }
    }
    and I found something weird If I open a websocket on my iOS phone, turnoff the internet (and close my phone) I keep logging this on KTOR: io.ktor.websocket.WebSocket - WebSocket Pinger: received valid pong frame Frame PONG For me if I close my phone the webscket should try a ping and should not receive a pong and close, For me the goal of ping pong is to avoid all this. I am not sure if I have doing everything good 🙂 I am hosting my server on Render Thank you in advance for your time
    a
    • 2
    • 17
  • j

    jeggy

    06/24/2025, 8:53 AM
    We have created an attribute like this:
    Copy code
    val rawRouteAttribute = AttributeKey<String>("rawRouteAttribute")
    environment.monitor.subscribe(Routing.RoutingCallStarted) { call ->
        call.attributes.put(rawRouteAttribute, call.route.parent.toString())
    }
    and then retrieving it back in the
    CallLogging
    plugin. But when looking at java flight recording, we can see that in our call logging plugin we get a few
    java.lang.IllegalStateException
    exceptions and with the message of
    No instance for key AttributeKey: rawRouteAttribute
    . I'm wondering in what cases does ktor use the
    CallLogging
    plugin but not fire a
    RoutingCallStarted
    event?
    a
    • 2
    • 2
  • p

    Poulastaa

    06/24/2025, 8:14 PM
    Hi.... I need some study material on how to create service-discovery or load-balancing using ktor. I need some help on connecting microservices and creating api-gateway.
  • l

    Leo N

    06/25/2025, 10:23 AM
    Hi folks :), We have some client libraries that come with Ktor 2.3.X and we are working on upgrading our Server to Ktor 3. I presume passing in Ktor 3 HttpClient is impossible into those client libraries. How would you approach this scenario?
    a
    • 2
    • 1
  • h

    hallvard

    06/26/2025, 8:06 AM
    According to https://kotlinlang.slack.com/archives/C0A974TJ9/p1668415739284229?thread_ts=1668345003.071279&amp;cid=C0A974TJ9, plugins will be executed in the same order as they are installed. I can't seem to find this information in the official documentation. Is it missing, or am I missing something?
    a
    • 2
    • 2
  • r

    ryanbreaker

    06/26/2025, 7:24 PM
    Is anyone familiar with an existing method of doing Zod-like payload validation within Ktor or Kotlin in general? For example, being able to stack errors such as missing fields whereas standard deserialization would fail right away without changing the shape of the target object?
    👀 1
  • s

    Samuel

    06/27/2025, 3:48 AM
    This bug is killing me: • https://youtrack.jetbrains.com/issue/KTOR-8583 Any chance for nightly builds so I can just pin to one of those? 🙏
    a
    • 2
    • 2
  • r

    reactormonk

    06/27/2025, 8:31 AM
    https://api.ktor.io/ktor-client/ktor-client-core/io.ktor.client.plugins/-http-request-retry.html Can I set different retry policies for different requests?
    a
    • 2
    • 2
  • b

    bryanstern

    06/27/2025, 10:42 PM
    Hi, I'm playing around with ktor 3.2.0, targeting js, and my headers are not showing up in the requests I'm observing in the browser dev tools. Any ideas what I might be doing wrong?
    Copy code
    httpClient.wss(
          request = {
            url(websocketUrl)
    
            header("foo", "bar")
          },
        ) {
          // TODO: consume websocket
        }
    a
    • 2
    • 1
  • s

    solonovamax

    06/27/2025, 10:47 PM
    Hi, I'm trying to integrate Authentic into a ktor application for authentication I've gone through the docs for using oauth2 and can roughly get something working. I'm assuming that what I would then need to do is insert the access token into my database, and check that the token is in the db? how should I then handle expiry & the refresh token? it does not explain this anywhere and I've tried searching around yet can find no such explanation anywhere else.
    a
    d
    • 3
    • 3
  • l

    Luv Kumar

    07/01/2025, 1:50 PM
    Hi all, i am working on a common library targeting js, android and ios targets. without ktor library my js bundle size is around
    383K
    and with ktor my bundle size goes to around
    617K
    increasing bundle size by
    234K
    . I have found some old you track tickets around similar issues, but not able to find if there is any way around this size increase. Is this something which is expected or can it be improved ?
    b
    • 2
    • 5
  • u

    ursus

    07/01/2025, 10:42 PM
    Hi, I have audio processing usecase - and I think I want your advice, because building it (high performance) on top of coroutines seems wrong (?) Basic idea is there is a producer (websocket streaming audio from client) and a consumer and there is a ring buffer between them. The usual (and blocking) standard way of solving this. What do you think about spawning threads in ktor server? Is there a way to make it atleast play nice with shutdown? // Channel mostly want me to use immutable data which is obviously no go for lots of binary data, so I'm not sure - communicating via a mutable structure seems like a bad idea right? Ideas?
    s
    • 2
    • 24
  • s

    Simon Binder

    07/02/2025, 3:56 AM
    Hi, I want to test an endpoint that is supposed to return an infinite stream of data (until cancelled by the client). I'm using the test host and
    HttpStatement.execute
    methods for this. But while this seems to work fine in an actual server (not written with ktor, I just want to test the client), this demo appears stuck, as if the response never arrives. Does anyone know what I need to do to test long-running stream responses?
    Copy code
    package com.powersync
    
    import io.ktor.client.HttpClient
    import io.ktor.client.HttpClientConfig
    import io.ktor.client.call.body
    import io.ktor.client.plugins.HttpTimeout
    import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
    import io.ktor.client.request.preparePost
    import io.ktor.http.HttpHeaders
    import io.ktor.http.content.OutgoingContent
    import io.ktor.server.engine.ConnectorType
    import io.ktor.server.engine.EngineConnectorBuilder
    import io.ktor.server.response.header
    import io.ktor.server.response.respond
    import <http://io.ktor.server.routing.post|io.ktor.server.routing.post>
    import io.ktor.server.testing.ApplicationTestBuilder
    import io.ktor.utils.io.ByteReadChannel
    import io.ktor.utils.io.ByteWriteChannel
    import io.ktor.utils.io.awaitFreeSpace
    import io.ktor.utils.io.readUTF8Line
    import io.ktor.utils.io.writeStringUtf8
    import kotlinx.coroutines.coroutineScope
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.launch
    import kotlin.time.Duration.Companion.seconds
    
    public suspend fun main() {
        coroutineScope {
            val server = testServer()
            val client = server {
                install(HttpTimeout)
                install(ContentNegotiation)
            }
    
            launch {
                val response = client.preparePost("<https://test.com/sync/stream>")
                response.execute {
                    println("has response") // it never gets to this point
    
                    val channel: ByteReadChannel = it.body()
    
                    while (!channel.isClosedForRead) {
                        val line = channel.readUTF8Line()
                        if (line != null) {
                            println("has line: $line")
                        }
                    }
                }
            }
        }
    }
    
    internal fun testServer(): (HttpClientConfig<*>.() -> Unit) -> HttpClient {
        val application = ApplicationTestBuilder().apply {
            engine {
                connectors.add(EngineConnectorBuilder(ConnectorType.HTTPS).apply {
                    host = "<http://test.com|test.com>"
                    port = 443
                })
            }
    
            routing {
                post("/sync/stream") {
                    val content = object : OutgoingContent.WriteChannelContent() {
                        override suspend fun writeTo(channel: ByteWriteChannel) {
                             while (true) {
                                 channel.awaitFreeSpace()
                                 channel.writeStringUtf8("test\n")
                                 channel.flush()
                                 delay(1.0.seconds)
                            }
                        }
                    }
    
                    call.response.header(HttpHeaders.ContentType, "application/x-ndjson")
                    call.response.header(HttpHeaders.CacheControl, "no-store")
                    call.response.header(HttpHeaders.Connection, "keep-alive")
                    call.response.header("X-Accel-Buffering", "no")
                    call.respond(content)
                }
            }
        }
    
        return application::createClient
    }
    I can see in the debugger that the
    while
    loop keeps running so I assume there's an internal buffer somewhere. Is there a way to turn that off?
    a
    • 2
    • 1
  • b

    bk9735732777

    07/03/2025, 6:33 AM
    Hi guys i am having trouble with deploying my ktor backend to render via Docker Neither port nor sslPort specified. Use command line options -port/-sslPort or configure connectors in application.conf I am getting this error.
    Copy code
    # Stage 1: Cache Gradle dependencies
    FROM gradle:latest AS cache
    RUN mkdir -p /home/gradle/cache_home
    ENV GRADLE_USER_HOME=/home/gradle/cache_home
    COPY build.gradle.* gradle.properties /home/gradle/app/
    COPY gradle /home/gradle/app/gradle
    WORKDIR /home/gradle/app
    RUN gradle clean build -i --stacktrace
    
    # Stage 2: Build Application
    FROM gradle:latest AS build
    COPY --from=cache /home/gradle/cache_home /home/gradle/.gradle
    COPY --chown=gradle:gradle . /home/gradle/src
    WORKDIR /home/gradle/src
    # Build the fat JAR, Gradle also supports shadow
    # and boot JAR by default.
    RUN gradle buildFatJar --no-daemon
    
    # Stage 3: Create the Runtime Image
    FROM amazoncorretto:22 AS runtime
    EXPOSE 8080
    RUN mkdir /app
    COPY --from=build /home/gradle/src/build/libs/*.jar /app/ktor-docker-sample.jar
    ENTRYPOINT ["java","-jar","/app/ktor-docker-sample.jar"]
    This is my docker file
    g
    • 2
    • 24
  • a

    Andrei Shilov

    07/04/2025, 3:27 PM
    Hi there folks! I have a questions what has changed in behaviour of
    intercept
    for
    Pipelines
    between 2.x a and 3.2 we used to have nested routes like
    Copy code
    route("x"){
       get("") {...}
       route("/b") {
         intercept(ApplicationCallPipeline.Call) { ...}
         get("")
       } 
    }
    in 3.2 nested intercept affects parent intercept and previously
    intercept(ApplicationCallPipeline.Call)
    was always called before we end up in the handler code and now it is not called before the handler is called Maybe we were relying on some undocumented behaviour ... do not really know. Any help is appreciated, Thx in advance!
    a
    • 2
    • 1
  • a

    Albertas

    07/05/2025, 8:10 PM
    Is 3.2's new type safe config working for anyone else? Always throws a MissingFieldException with 3.2.1, in this case with
    Fields [apiKey, emails, templates] are required for type with serial name <config class>, but they were missing
    The only thing that works is serializing primitive types.
    b
    • 2
    • 9
  • j

    James

    07/08/2025, 6:21 AM
    For Ktor Client, is it possible to install the Auth module, but specify which authentication provider to use per request?
    r
    a
    • 3
    • 3
  • l

    Lukasz Kalnik

    07/08/2025, 8:16 AM
    I have a multiplatform Android / iOS app and want to use on Android OkHttp with custom DNS implementation. The Ktor
    HttpClient
    is declared in the shared multiplatform part. How can I inject
    OkHttp
    with the custom DNS into the shared part only for Android?
    s
    • 2
    • 8
  • a

    Aryan Gupta

    07/08/2025, 4:22 PM
    In Ktor client,
    Attributes
    can be used to pass custom data throughout the request lifecycle. When using the
    OkHttpEngine
    , I'm looking for a way to map these Ktor
    Attributes
    to OkHttp
    Tags
    . As the current
    OkHttpEngine
    implementation within KTOR doesn't seem to bridge this , how can I achieve this mapping?
    a
    • 2
    • 6
  • a

    Arjan van Wieringen

    07/09/2025, 12:58 PM
    I see the DI docs have been added: https://ktor.io/docs/server-dependency-injection.html. Some questions though: • dependencies block is not clear that is is part of Application scope. We can't have Call-scoped dependencies? • Are the dependencies singletons (I assume so, but it is not made explicit)? Can they be made not singletons? Is there support for 'multitons'?
    👍 2
    s
    b
    p
    • 4
    • 20
  • h

    Helio

    07/10/2025, 7:51 AM
    Hello 👋🏽 I was wondering if you could shed some light in terms of the best practices to monitor the performance of a server endpoint in Ktor. I understand that Ktor exposes some metrics such as
    ktor.http.server.requests.upper_99
    . But I need to add an extra property on top of this metric that says if the request took longer than 1s, add a new property saying that
    failed
    otherwise
    met
    . Questions: 1. Is it possible to add new metric on top of this existing metric? Otherwise, would the code below be the most appropriate way to do it? a. I know that after every endpoint execution Ktor logs the result, such as.
    200 OK: GET - /healthcheck in 35ms
    . Is it possible to extract the value instead of calculating it?
    Copy code
    get("/healthcheck") {
            val startTime = System.currentTimeMillis()
            println(call.request.uri)
            call.respondText(Constants.HEALTHCHECK_RESPONSE, ContentType.Text.Plain)
            val endTime = System.currentTimeMillis() // Capture the end time
            val duration = endTime - startTime // Calculate the duration
            if (duration < 1000L) {
                //Send Success metric
            }
        }
    2. What would be the most appropriate way to get the name of the path.
    call.request.call.route.parent
    ? Is this the most accurate way? Assuming I don't want to get the value of the "queryParameter" and how the endpoint is defined instead. Thanks for your help.
    a
    • 2
    • 7
  • m

    Marcus Ilgner

    07/10/2025, 10:10 AM
    Hi all 👋 Can anyone give me a hint on how to get a Ktor server to use the
    Authorization
    information from the
    connection_init
    message of a websocket connection for authentication? I have installed the
    Authentication
    plugin - works fine for regular JWT Bearer Tokens in the
    Authorization
    header of regular HTTP requests - and also set up a
    ContextFactory
    for subscriptions can handle the authorization part. Unfortunately, the authentication part doesn't work yet, as it seems like the
    connection_init
    message isn't used by the
    Authentication
    plugin to set up the principal. The client used is
    graphql-ws
    and it looks like adding the auth information to
    connection_init
    is the only supported method. I read this comment about how using regular HTTP headers for websockets might not be a good idea and think that probably there is some way to get Ktor to use the
    connection_init
    payload. Any ideas / reading material?
    • 1
    • 1
  • a

    ayodele

    07/10/2025, 11:59 AM
    Hey guys, is the ktor server route matching function exposed? Or anyone can point me in the right direction??
    a
    • 2
    • 1
  • u

    ursus

    07/11/2025, 12:34 AM
    How can I test my Ktor client WebSocket code? Do I need to fake the WebSocket implementation, or can I swap in a lower-level component for tests, sort of like the mock engine for http?
    s
    a
    • 3
    • 2
  • a

    Arjan van Wieringen

    07/11/2025, 7:51 AM
    Just a basic DI question. I've always learned that property injection is not ideal, and one should always prefer constructor injection since it makes testability better among others things. However, in almost all Ktor examples for DI I see the equivalent of property injection but functionally. What do I mean?
    Copy code
    fun Application.myModule() {
        val myDep: Dep by dependencies // or val myDep: Dep = dependencies.resolve();
    
        // more code
    }
    I don't see why we should make make the functions so aware of a DI container. Also, this isn't really dependency injection. This is just a service container then 🙂 This is also pretty prevalent in the official KotlinConf backend app which runs on Koin. Since modules are functions, and functions have arguments, isn't it better to do:
    Copy code
    fun Application.myModule(myDep: Dep) { ... }
    
    // And within Application scope
    myModule(myDep = dependencies.resolve())
    Is there a reason to do one over the other?
    v
    b
    • 3
    • 11
  • d

    dalexander

    07/11/2025, 1:43 PM
    I'm encountering an issue trying to make a ktor-client request with some cookies. Using the
    cookie()
    method url encodes the cookie data for some reason which makes the data invalid, and trying to set the Cookie header directly seems to have no effect on the request based on what the logs are showing me (I don't see a Cookie header after setting it). Ktor version is 3.2.1
    a
    • 2
    • 6
  • a

    Aryan Gupta

    07/11/2025, 3:37 PM
    I have passed a wrong hash in Certificate Pinner , but I am still getting 200 status code using this client implementation . What could be the reason here ? I believe this call should fail with exception
    Copy code
    val cp = CertificatePinner.Builder().add("ktor.io", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=").build()
    usePreconfiguredSession(
        session =  NSURLSession.sessionWithConfiguration(
            NSURLSessionConfiguration.defaultSessionConfiguration,
            NsUrlSessionDelegate(),
            delegateQueue = NSOperationQueue()
        ),
        delegate = KtorNSURLSessionDelegate(cp),
    )
    • 1
    • 2
  • c

    chrisjenx

    07/11/2025, 3:41 PM
    There doesn't seem to be a way to provide a new CoroutineContext for a Call, i.e. this doesn't work:
    Copy code
    //fun Application.installContextCaching() {
    //    intercept(ApplicationCallPipeline.Plugins) {
    //        withContextCache {
    //            proceedWith(subject)
    //        }
    //    }
    //}
    Seems the pipelining don't wrap interceptors which is what is normally expected.?
    m
    • 2
    • 6
  • a

    Arjan van Wieringen

    07/12/2025, 7:44 AM
    When will https://api.ktor.io/ show version 3.2.1? I was scratching my head why I couldn't find the
    configure
    function in the
    TestApplicationBuilder
    docs.
  • u

    ursus

    07/12/2025, 2:33 PM
    Copy code
    HttpClient(OkHttp) {
        engine {
            preconfigured = okHttpClient
        }
        install(DefaultRequest) {
            url("<http://10.0.2.2:8081/>")
        }
        install(ContentNegotiation) {
            json(json)
        }
        install(WebSockets)
    }
    
    val webSocketSession = ktorClient.webSocketSession("ws")
    webSocketSession
    doesnt seem to pickup the
    DefaultRequest
    base url If I duplicate the url again like this
    Copy code
    val wsUrl = URLBuilder("<http://10.0.2.2:8081/>")
        .apply {
            protocol = URLProtocol.WS
            path("ws")
        }
        .buildString()
    val webSocketSession = ktorClient.webSocketSession(wsUrl)
    then it works Is that normal? Am I using it wrong?
    o
    • 2
    • 2