diff --git a/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/closest.kt b/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/closest.kt index bbcabe47..24b690f8 100644 --- a/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/closest.kt +++ b/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/closest.kt @@ -3,7 +3,6 @@ package org.kodein.di.ktor import io.ktor.server.application.* import io.ktor.server.routing.* import io.ktor.util.* -import io.ktor.util.pipeline.* import org.kodein.di.DI import org.kodein.di.LazyDI @@ -50,7 +49,6 @@ public fun Route.closestDI(): LazyDI { /** * Getting the global [DI] container from the [ApplicationCall] */ -public fun PipelineContext<*, ApplicationCall>.closestDI(): LazyDI { - val routingCall = (this.call as RoutingApplicationCall) - return routingCall.route.closestDI() +public fun RoutingContext.closestDI(): LazyDI { + return call.route.closestDI() } diff --git a/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/scopes.kt b/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/scopes.kt index 3cd7ad18..c6fdf7e9 100644 --- a/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/scopes.kt +++ b/framework/ktor/kodein-di-framework-ktor-server-jvm/src/main/kotlin/org/kodein/di/ktor/scopes.kt @@ -1,6 +1,7 @@ package org.kodein.di.ktor import io.ktor.server.application.* +import io.ktor.server.routing.RoutingCall import io.ktor.server.sessions.* import org.kodein.di.bindings.Scope import org.kodein.di.bindings.ScopeRegistry @@ -68,5 +69,13 @@ public inline fun CurrentSession.clearSessionScope() { } //endregion //region Request scope -public object CallScope : WeakContextScope() +public object CallScope : WeakContextScope() { + override fun getRegistry(context: ApplicationCall): ScopeRegistry { + val actualContext = when (context) { + is RoutingCall -> context.pipelineCall + else -> context + } + return super.getRegistry(actualContext) + } +} //endregion \ No newline at end of file diff --git a/framework/ktor/kodein-di-framework-ktor-server-jvm/src/test/kotlin/org/kodein/di/ktor/KtorApplication.kt b/framework/ktor/kodein-di-framework-ktor-server-jvm/src/test/kotlin/org/kodein/di/ktor/KtorApplication.kt index 81ecf2d1..7b4c86b1 100644 --- a/framework/ktor/kodein-di-framework-ktor-server-jvm/src/test/kotlin/org/kodein/di/ktor/KtorApplication.kt +++ b/framework/ktor/kodein-di-framework-ktor-server-jvm/src/test/kotlin/org/kodein/di/ktor/KtorApplication.kt @@ -3,14 +3,16 @@ package org.kodein.di.ktor import io.ktor.server.application.Application import io.ktor.server.application.ApplicationCall import io.ktor.server.application.ApplicationCallPipeline -import io.ktor.server.application.application +import io.ktor.server.application.Hook import io.ktor.server.application.call +import io.ktor.server.application.createRouteScopedPlugin import io.ktor.server.application.install import io.ktor.server.application.log import io.ktor.server.plugins.defaultheaders.DefaultHeaders import io.ktor.server.request.httpMethod import io.ktor.server.request.uri import io.ktor.server.response.respondText +import io.ktor.server.routing.application import io.ktor.server.routing.get import io.ktor.server.routing.post import io.ktor.server.routing.route @@ -19,8 +21,10 @@ import io.ktor.server.sessions.SessionStorageMemory import io.ktor.server.sessions.Sessions import io.ktor.server.sessions.cookie import io.ktor.server.sessions.get +import io.ktor.server.sessions.reflectionSessionSerializer import io.ktor.server.sessions.sessions import io.ktor.server.sessions.set +import io.ktor.util.pipeline.PipelinePhase import org.kodein.di.DI import org.kodein.di.bind import org.kodein.di.instance @@ -73,6 +77,7 @@ private fun Application.sessionModule() { install(Sessions) { cookie("SESSION_FEATURE_SESSION_ID", SessionStorageMemory()) { cookie.path = "/" // Specify cookie's path '/' so it can be used in the whole site + serializer = reflectionSessionSerializer() } } @@ -116,9 +121,9 @@ fun Application.requestModule() { routing { route(ROUTE_REQUEST) { suspend fun logPhase( - phase: String, - applicationCall: ApplicationCall, - proceed: suspend () -> Unit + phase: String, + applicationCall: ApplicationCall, + proceed: suspend () -> Unit ) { val random by closestDI().on(applicationCall).instance() randomDto.randomInstances.add(phase to "$random") @@ -126,27 +131,41 @@ fun Application.requestModule() { proceed() } - intercept(ApplicationCallPipeline.Setup) { - randomDto.randomInstances.clear() - logPhase("[Setup]", context) { proceed() } - } - intercept(ApplicationCallPipeline.Monitoring) { - logPhase("[Monitoring]", context) { proceed() } - } - intercept(ApplicationCallPipeline.Plugins) { - logPhase("[Features]", context) { proceed() } - } - intercept(ApplicationCallPipeline.Call) { - logPhase("[Call]", context) { proceed() } - } - intercept(ApplicationCallPipeline.Fallback) { - logPhase("[Fallback]", context) { proceed() } + val interceptor = createRouteScopedPlugin("Interceptor") { + class PhaseHook(val phase: PipelinePhase) : Hook Unit> { + override fun install( + pipeline: ApplicationCallPipeline, + handler: suspend (ApplicationCall) -> Unit + ) { + pipeline.intercept(phase) { + handler(call) + proceed() + } + } + } + on(PhaseHook(ApplicationCallPipeline.Setup)) { + randomDto.randomInstances.clear() + logPhase("[Setup]", it) { } + } + on(PhaseHook(ApplicationCallPipeline.Monitoring)) { + logPhase("[Monitoring]", it) { } + } + on(PhaseHook(ApplicationCallPipeline.Plugins)) { + logPhase("[Plugins]", it) { } + } + on(PhaseHook(ApplicationCallPipeline.Call)) { + logPhase("[Call]", it) { } + } + on(PhaseHook(ApplicationCallPipeline.Fallback)) { + logPhase("[Fallback]", it) { } + } } + install(interceptor) get { - val random by closestDI().on(context).instance() + val random by closestDI().on(call).instance() application.log.info("DI ${closestDI().container} / Random instance: $random") - logPhase("[GET]", context) { + logPhase("[GET]", call) { call.respondText(randomDto.randomInstances.joinToString { "${it.first}=${it.second}" }) } } diff --git a/framework/ktor/kodein-di-framework-ktor-server-jvm/src/test/kotlin/org/kodein/di/ktor/KtorTests.kt b/framework/ktor/kodein-di-framework-ktor-server-jvm/src/test/kotlin/org/kodein/di/ktor/KtorTests.kt index f901d70e..0ad33732 100644 --- a/framework/ktor/kodein-di-framework-ktor-server-jvm/src/test/kotlin/org/kodein/di/ktor/KtorTests.kt +++ b/framework/ktor/kodein-di-framework-ktor-server-jvm/src/test/kotlin/org/kodein/di/ktor/KtorTests.kt @@ -139,7 +139,7 @@ class KtorTests { keyValue.first() to keyValue.last() } - assertEquals(5, pairs.size) // Ensure we pass through 5 phases (Setup, Monitoring, Features, Call, GET) + assertEquals(5, pairs.size) // Ensure we pass through 5 phases (Setup, Monitoring, Plugins, Call, GET) assertEquals(1, pairs.map { it.second }.distinct().count()) // For all the phases we only have 1 Random instance } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ddc9d3cc..ea911b91 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ compose-bom = "2024.05.00" kotlinpoet = "1.14.2" ksp = "1.9.20-1.0.14" # Ktor -ktor = "2.3.6" +ktor = "3.0.1" # JxInject javax-inject = "1" # TornadoFX @@ -52,8 +52,7 @@ ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.re # Ktor ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" } ktor-server-sessions = { module = "io.ktor:ktor-server-sessions", version.ref = "ktor" } -ktor-test-server-host = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" } -ktor-test-server = { module = "io.ktor:ktor-server-tests", version.ref = "ktor" } +ktor-test-server = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" } ktor-test-server-default-headers = { module = "io.ktor:ktor-server-default-headers", version.ref = "ktor" } # JxInject javax-inject = { module = "javax.inject:javax.inject", version.ref = "javax-inject" }