kotlinjvmktor

Using configure script with Ktor 3 CoroutineScope.embeddedServer extension function


I am using Ktor 3.x and am looking to start an embedded server using the Coroutine scope extension function, while also supplying my own configure script.

The documentation I am referencing is here.

As we can see from the function signature, there exists one which allows you to pass your own configure: TConfiguration.() -> Unit = {}.

fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> embeddedServer(
    factory: ApplicationEngineFactory<TEngine, TConfiguration>, 
    environment: ApplicationEnvironment = applicationEnvironment(), 
    configure: TConfiguration.() -> Unit = {}, 
    module: Application.() -> Unit = {}
): EmbeddedServer<TEngine, TConfiguration>

Observing the extension functions from CoroutineScope, they do not allow us to pass a configure block:

fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> CoroutineScope.embeddedServer(
    factory: ApplicationEngineFactory<TEngine, TConfiguration>, 
    vararg connectors: EngineConnectorConfig = arrayOf(), 
    watchPaths: List<String> = listOf(WORKING_DIRECTORY_PATH), 
    parentCoroutineContext: CoroutineContext = EmptyCoroutineContext, 
    module: Application.() -> Unit
): EmbeddedServer<TEngine, TConfiguration>

&

fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> CoroutineScope.embeddedServer(
    factory: ApplicationEngineFactory<TEngine, TConfiguration>, 
    port: Int = 80, 
    host: String = "0.0.0.0", 
    watchPaths: List<String> = listOf(WORKING_DIRECTORY_PATH), 
    parentCoroutineContext: CoroutineContext = EmptyCoroutineContext, 
    module: Application.() -> Unit
): EmbeddedServer<TEngine, TConfiguration>

What is the expected way to gain the configure functionality when working with the extension function?


Solution

  • Unfortunately, there is no way to configure the engine while using the CoroutineScope.embeddedServer method. However, you can define your own overloaded method, which would allow that:

    fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> CoroutineScope.embeddedServer(
        factory: ApplicationEngineFactory<TEngine, TConfiguration>,
        environment: ApplicationEnvironment = applicationEnvironment(),
        configure: TConfiguration.() -> Unit = {},
        module: Application.() -> Unit = {}
    ): EmbeddedServer<TEngine, TConfiguration> {
        val applicationProperties = serverConfig(environment) {
            this.parentCoroutineContext = coroutineContext + parentCoroutineContext
            this.module(module)
        }
    
        return embeddedServer(factory, applicationProperties, configure)
    }
    

    Here is an example of the method usage:

    CoroutineScope(Dispatchers.IO).embeddedServer(
        Netty,
        applicationEnvironment {},
        configure = {
            maxHeaderSize = 1024 * 1024
            connectors.add(EngineConnectorBuilder().withPort(8833))
        }
    ) {
        routing {
            get {
                call.respondText("Hello World!")
            }
        }
    }.start(wait = true)