InsertKoinIO / koin

Koin - a pragmatic lightweight dependency injection framework for Kotlin & Kotlin Multiplatform
https://insert-koin.io
Apache License 2.0
9.09k stars 719 forks source link

Unexpected behavior with RequestScope declarations in Ktor #2010

Open perracodex opened 1 month ago

perracodex commented 1 month ago

Describe the bug I encountered unexpected behavior regarding Request Scope lifecycle declared variables in Ktor, when using createRouteScopedPlugin.

It appears that when declaring a value using call.scope.declare within the AuthenticationChecked hook, the value persists across different calls instead of being discarded after the call completes. In addition, the declared value in the scope is not propagated through the pipeline.

Please find the attached sample project and refer to the console output for further details

To Reproduce Find attached project. Open the localhost as shown in the console when the project starts. Keep refreshing the browser to see the results in the console.

Expected behavior

  1. Scope declared variables should not persist across calls.
  2. Declared variables in the call pipeline should be propagated.

Koin module and version: koin-core:4.0.0

Snippet or Sample project to help reproduce

ktor-koin.zip

Short snippet from the project, for quick context:

val CustomPlugin: RouteScopedPlugin<Unit> = createRouteScopedPlugin(
    name = "CustomPlugin"
) {
    on(hook = AuthenticationChecked) { call ->
        // This is not getting the value from the basic authentication plugin.
        // "Probably expected behaviour" due to Basic Authentication nature,
        // need confirmation on this.
        var value: SomeData? = call.scope.getOrNull<SomeData>()

        // MAIN ISSUE: The first time the value is null, since it was not passed along.
        // So, we create a dummy one for the sake of this test.
        //
        // Expected Behaviour: Since it is being declared in the "call.scope", its
        // lifecycle should be limited to the call scope,
        //
        // Current behaviour: It persists across calls. so on the next call the
        // getOrNull() returns the value from the previous call.
        //
        // SECOND ISSUE:
        // This value is not propagated to the call in the routes. Is like if 2 call scopes exist.
        if (value == null) {
            println("[In CustomPlugin] No value found in Koin Scope: Creating new one.")
            call.scope.declare(instance = SomeData(data = "test_random"))
        } else {
            println("[In CustomPlugin] Reusing Koin Scope from different call.")
        }

        value = call.scope.getOrNull<SomeData>()
        println("[In CustomPlugin] Koin Scope Data: $value")
    }
}
arnaudgiuliani commented 1 week ago

I don't see any createRouteScopedPlugin in Koin. Is it something personal?

perracodex commented 1 week ago

@arnaudgiuliani createRouteScopedPlugin is part of Ktor, not Koin. It’s used to create plugins scoped to specific routes in Ktor. See above an attached sample project.