CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor-server-core

Ktor Server Core library providing foundational infrastructure for building asynchronous web applications and REST APIs with Kotlin

Pending
Overview
Eval results
Files

engine.mddocs/

Server Engines

Ktor's server engine system provides flexible deployment options through multiple engine implementations. The engine architecture separates application logic from server infrastructure, enabling the same application to run on different server technologies.

ApplicationEngine Interface

Core Engine Interface

interface ApplicationEngine {
    val environment: ApplicationEnvironment
    
    fun start(wait: Boolean = false): ApplicationEngine
    suspend fun startSuspend(wait: Boolean = false): ApplicationEngine
    fun stop(gracePeriodMillis: Long = 500, timeoutMillis: Long = 500)
    suspend fun resolvedConnectors(): List<EngineConnectorConfig>
}

Engine Lifecycle

// Start server and optionally wait
val engine = embeddedServer(Netty, port = 8080) {
    routing {
        get("/") { call.respondText("Hello!") }
    }
}

// Start without blocking
engine.start(wait = false)

// Start with blocking (typical for main function)
engine.start(wait = true)

// Start asynchronously  
runBlocking {
    engine.startSuspend(wait = false)
    // Do other work
    delay(1000)
    engine.stop(1000, 5000)
}

// Stop with grace period and timeout
engine.stop(
    gracePeriodMillis = 1000, // Allow 1 second for graceful shutdown
    timeoutMillis = 5000      // Force shutdown after 5 seconds
)

ApplicationEngineFactory Interface

Engine Factory

interface ApplicationEngineFactory<TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> {
    fun create(
        environment: ApplicationEnvironment,
        configure: TConfiguration.() -> Unit = {}
    ): TEngine
}

Available Engine Factories

// Netty engine (high-performance NIO)
object Netty : ApplicationEngineFactory<NettyApplicationEngine, NettyApplicationEngine.Configuration>

// CIO engine (coroutine-based)  
object CIO : ApplicationEngineFactory<CIOApplicationEngine, CIOApplicationEngine.Configuration>

// Jetty engine (traditional servlet)
object Jetty : ApplicationEngineFactory<JettyApplicationEngine, JettyApplicationEngine.Configuration>

// Tomcat engine (traditional servlet)
object Tomcat : ApplicationEngineFactory<TomcatApplicationEngine, TomcatApplicationEngine.Configuration>

Embedded Server Creation

Basic Embedded Server

// Create embedded server with factory and configuration
fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> embeddedServer(
    factory: ApplicationEngineFactory<TEngine, TConfiguration>,
    port: Int = 80,
    host: String = "0.0.0.0",
    configure: TConfiguration.() -> Unit = {},
    module: Application.() -> Unit
): EmbeddedServer<TEngine, TConfiguration>

// Simple embedded server
fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/") {
                call.respondText("Hello, Ktor!")
            }
        }
    }.start(wait = true)
}

Advanced Embedded Server

// Create embedded server with environment
fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> embeddedServer(
    factory: ApplicationEngineFactory<TEngine, TConfiguration>,
    environment: ApplicationEnvironment,
    configure: TConfiguration.() -> Unit = {}
): EmbeddedServer<TEngine, TConfiguration>

// Example with custom environment
fun main() {
    val environment = applicationEnvironment {
        config = HoconApplicationConfig(ConfigFactory.load())
        log = LoggerFactory.getLogger("Application")
    }
    
    embeddedServer(Netty, environment) {
        // Netty-specific configuration
        connectionGroupSize = 2
        workerGroupSize = 5
        callGroupSize = 10
        
        connector {
            port = 8080
            host = "0.0.0.0"
        }
        
        connector {
            port = 8443
            host = "0.0.0.0"
            // SSL configuration would go here
        }
    }.start(wait = true)
}

Engine Configuration

Base Engine Configuration

open class ApplicationEngine.Configuration {
    var parallelism: Int = availableProcessors()
    var connectionGroupSize: Int = parallelism
    var workerGroupSize: Int = parallelism  
    var callGroupSize: Int = parallelism
    var shutdownGracePeriod: Long = 1000
    var shutdownTimeout: Long = 5000
    
    internal val connectors = mutableListOf<EngineConnectorConfig>()
    
    fun takeFrom(other: Configuration) {
        // Copy configuration from another instance
        parallelism = other.parallelism
        connectionGroupSize = other.connectionGroupSize
        workerGroupSize = other.workerGroupSize
        callGroupSize = other.callGroupSize
        shutdownGracePeriod = other.shutdownGracePeriod
        shutdownTimeout = other.shutdownTimeout
    }
}

Connector Configuration

interface EngineConnectorConfig {
    val type: ConnectorType
    val host: String  
    val port: Int
}

enum class ConnectorType {
    HTTP, HTTPS
}

// Configure connectors
embeddedServer(Netty, port = 8080) {
    // Default connector is automatically created
    
    // Add additional connectors
    connector {
        port = 8443
        host = "0.0.0.0"
        // Additional connector configuration
    }
}

Base Engine Implementation

BaseApplicationEngine

abstract class BaseApplicationEngine(
    environment: ApplicationEnvironment,
    pipeline: EnginePipeline
) : ApplicationEngine {
    
    override val environment: ApplicationEnvironment = environment
    
    // Lifecycle management
    abstract suspend fun startServer(): Unit
    abstract suspend fun stopServer(): Unit
    
    // Resolved connector information
    override fun resolvedConnectors(): List<EngineConnectorConfig>
}

Base Call Implementation

abstract class BaseApplicationCall(
    application: Application
) : ApplicationCall {
    
    override val application: Application = application
    override val attributes: Attributes = Attributes()
    
    abstract override val request: BaseApplicationRequest
    abstract override val response: BaseApplicationResponse
    
    // Parameter resolution
    private val _parameters = lazy { 
        request.queryParameters + request.rawParameters
    }
    override val parameters: Parameters get() = _parameters.value
}

abstract class BaseApplicationRequest(call: ApplicationCall) : ApplicationRequest
abstract class BaseApplicationResponse(call: ApplicationCall) : ApplicationResponse

Engine Pipeline

EnginePipeline

class EnginePipeline(
    val developmentMode: Boolean = false
) : Pipeline<Unit, ApplicationCall>(Before, Call) {
    
    companion object {
        val Before = PipelinePhase("Before")
        val Call = PipelinePhase("Call") 
    }
}

// Default engine pipeline creation
fun defaultEnginePipeline(
    environment: ApplicationEnvironment
): EnginePipeline {
    return EnginePipeline(environment.developmentMode)
}

Default Pipeline Implementation

class DefaultEnginePipeline(
    application: Application,
    developmentMode: Boolean = false
) : EnginePipeline(developmentMode) {
    
    init {
        // Install default pipeline phases and interceptors
        intercept(Call) { 
            try {
                // Execute application call pipeline
                application.execute(call)
            } catch (e: Throwable) {
                handleEngineException(e)
            }
        }
    }
}

Environment and Configuration

ApplicationEnvironment

interface ApplicationEnvironment {
    val rootPath: String
    val log: Logger
    val config: ApplicationConfig
    val monitor: Events
    val developmentMode: Boolean
    val parentCoroutineContext: CoroutineContext
    val watchPaths: List<String>
}

ApplicationEnvironmentBuilder

class ApplicationEnvironmentBuilder {
    var rootPath: String = ""
    var log: Logger = LoggerFactory.getLogger("Application") 
    var config: ApplicationConfig = MapApplicationConfig()
    var developmentMode: Boolean = false
    var parentCoroutineContext: CoroutineContext? = null
    val watchPaths: MutableList<String> = mutableListOf()
    
    fun build(): ApplicationEnvironment
}

// Create application environment
fun applicationEnvironment(
    block: ApplicationEnvironmentBuilder.() -> Unit = {}
): ApplicationEnvironment {
    return ApplicationEnvironmentBuilder().apply(block).build()
}

Environment Configuration

// Configure environment from config file/object
fun ApplicationEnvironmentBuilder.configure(
    config: ApplicationConfig
) {
    this.config = config
    
    // Extract common configuration
    rootPath = config.propertyOrNull("ktor.deployment.rootPath")?.getString() ?: ""
    developmentMode = config.propertyOrNull("ktor.development")?.getString()?.toBoolean() ?: false
    
    // Configure watch paths for development
    config.configList("ktor.deployment.watch").forEach { watchConfig ->
        watchPaths.add(watchConfig.property("path").getString())
    }
}

// Example configuration usage
fun createEnvironment(): ApplicationEnvironment {
    return applicationEnvironment {
        config = HoconApplicationConfig(ConfigFactory.load())
        configure(config)
        
        log = LoggerFactory.getLogger("MyApp")
        developmentMode = System.getenv("ENVIRONMENT") != "production"
    }
}

Deployment Configuration

Command Line Configuration

class CommandLineConfig(args: Array<String>) {
    val port: Int
    val host: String
    val configPath: String?
    val developmentMode: Boolean
    
    // Parse command line arguments
    init {
        // Implementation parses -port, -host, -config, -dev arguments
    }
}

// Configuration keys
object ConfigKeys {
    const val applicationIdPath = "ktor.application.id"
    const val hostConfigPath = "ktor.deployment.host"
    const val portConfigPath = "ktor.deployment.port"
    const val shutdownGracePeriodPath = "ktor.deployment.shutdown.gracePeriod"
    const val shutdownTimeoutPath = "ktor.deployment.shutdown.timeout"
}

Configuration Loading

// Load common configuration from ApplicationConfig
fun ApplicationEngine.Configuration.loadCommonConfiguration(config: ApplicationConfig) {
    config.propertyOrNull("ktor.deployment.shutdown.gracePeriod")?.getString()?.toLong()?.let {
        shutdownGracePeriod = it
    }
    
    config.propertyOrNull("ktor.deployment.shutdown.timeout")?.getString()?.toLong()?.let {
        shutdownTimeout = it  
    }
    
    config.propertyOrNull("ktor.deployment.connectionGroupSize")?.getString()?.toInt()?.let {
        connectionGroupSize = it
    }
    
    config.propertyOrNull("ktor.deployment.workerGroupSize")?.getString()?.toInt()?.let {
        workerGroupSize = it
    }
}

Utility Functions

Shutdown Hooks

// Add shutdown hook to embedded server
fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> 
EmbeddedServer<TEngine, TConfiguration>.addShutdownHook() {
    Runtime.getRuntime().addShutdownHook(Thread {
        stop(1000, 5000)
    })
}

// Example usage
fun main() {
    val server = embeddedServer(Netty, port = 8080) {
        routing {
            get("/") { call.respondText("Hello!") }
        }
    }
    
    server.addShutdownHook()
    server.start(wait = true)
}

Coroutine Integration

// Stop server when coroutine context is cancelled  
suspend fun ApplicationEngine.stopServerOnCancellation() {
    try {
        awaitCancellation()
    } finally {
        stopSuspend(1000, 5000)
    }
}

// Usage with structured concurrency
fun main() = runBlocking {
    val server = embeddedServer(Netty, port = 8080) {
        routing {
            get("/") { call.respondText("Hello!") }
        }
    }
    
    launch {
        server.start()
        server.stopServerOnCancellation()
    }
    
    // Server will stop when this coroutine scope completes
    delay(60000) // Run for 1 minute
}

Exception Handling

Default Exception Handler

// Default uncaught exception handler for engines
class DefaultUncaughtExceptionHandler : Thread.UncaughtExceptionHandler {
    override fun uncaughtException(t: Thread, e: Throwable) {
        // Log uncaught exceptions
        logger.error("Uncaught exception in thread ${t.name}", e) 
    }
}

// Map exceptions to HTTP status codes
fun defaultExceptionStatusCode(exception: Throwable): HttpStatusCode {
    return when (exception) {
        is NotFoundException -> HttpStatusCode.NotFound
        is ParameterConversionException -> HttpStatusCode.BadRequest
        is UnsupportedMediaTypeException -> HttpStatusCode.UnsupportedMediaType
        is PayloadTooLargeException -> HttpStatusCode.PayloadTooLarge
        else -> HttpStatusCode.InternalServerError
    }
}

Deployment Examples

Production Deployment

fun main() {
    val environment = applicationEnvironment {
        // Load configuration from application.conf
        config = HoconApplicationConfig(ConfigFactory.load())
        
        // Configure from config file
        configure(config)
        
        // Production settings
        developmentMode = false
        log = LoggerFactory.getLogger("ProductionApp")
    }
    
    val server = embeddedServer(Netty, environment) {
        // Production configuration
        connectionGroupSize = 4
        workerGroupSize = 8  
        callGroupSize = 16
        
        shutdownGracePeriod = 5000
        shutdownTimeout = 10000
        
        // Multiple connectors for different interfaces
        connector {
            port = 8080
            host = "0.0.0.0"
        }
        
        connector {
            port = 8443
            host = "0.0.0.0"
            // SSL configuration
        }
        
        connector {
            port = 9090
            host = "127.0.0.1" // Internal management interface
        }
    }
    
    // Add shutdown hook for graceful termination
    server.addShutdownHook()
    
    // Start server
    server.start(wait = true)
}

Development Deployment

fun main() {
    val server = embeddedServer(Netty, port = 8080) {
        // Development configuration - optimized for fast restarts
        connectionGroupSize = 1
        workerGroupSize = 1
        callGroupSize = 1
        
        connector {
            port = 8080
            host = "127.0.0.1" // Local only
        }
    }
    
    Runtime.getRuntime().addShutdownHook(Thread {
        server.stop(100, 1000) // Faster shutdown for development
    })
    
    server.start(wait = true)
}

Multi-Application Deployment

fun main() {
    // Create shared environment
    val sharedEnvironment = applicationEnvironment {
        config = HoconApplicationConfig(ConfigFactory.load())
        configure(config)
    }
    
    // API server
    val apiServer = embeddedServer(Netty, sharedEnvironment) {
        connector {
            port = 8080
            host = "0.0.0.0"
        }
    }
    
    // Admin server  
    val adminServer = embeddedServer(CIO, sharedEnvironment) {
        connector {
            port = 8081
            host = "127.0.0.1" // Admin interface local only
        }
    }
    
    // Start both servers
    apiServer.start(wait = false)
    adminServer.start(wait = false)
    
    // Keep main thread alive
    runBlocking {
        awaitCancellation()
    }
}

Engine-Specific Configuration

Netty Configuration

embeddedServer(Netty, port = 8080) {
    // Netty-specific settings
    connectionGroupSize = 2      // Boss threads
    workerGroupSize = 8         // Worker threads  
    callGroupSize = 16          // Call handler threads
    
    shutdownGracePeriod = 2000
    shutdownTimeout = 5000
    
    // Request timeout configuration
    requestQueueLimit = 16
    runningLimit = 32
    
    // TCP configuration
    tcpKeepAlive = true
    reuseAddress = true
    
    connector {
        port = 8080
        host = "0.0.0.0"
    }
}

CIO Configuration

embeddedServer(CIO, port = 8080) {
    // CIO-specific settings
    connectionGroupSize = 1
    workerGroupSize = availableProcessors()
    callGroupSize = availableProcessors() * 2
    
    connectionIdleTimeout = 45000
    
    connector {
        port = 8080 
        host = "0.0.0.0"
    }
}

This comprehensive documentation covers all aspects of Ktor's server engine system, from basic embedded server creation to advanced production deployment configurations.

Install with Tessl CLI

npx tessl i tessl/maven-io-ktor--ktor-server-core

docs

application.md

configuration.md

engine.md

index.md

request-response.md

routing.md

tile.json