Ktor Server Core library providing foundational infrastructure for building asynchronous web applications and REST APIs with Kotlin
—
Ktor's application management system provides comprehensive lifecycle control, plugin architecture, and event handling for web applications. The Application class serves as the central coordinator for all server functionality.
class Application internal constructor(
environment: ApplicationEnvironment,
developmentMode: Boolean,
rootPath: String,
monitor: Events,
parentCoroutineContext: CoroutineContext,
engineProvider: () -> ApplicationEngine
) : ApplicationCallPipeline, CoroutineScope {
val engine: ApplicationEngine
var rootPath: String
val monitor: Events
val parentCoroutineContext: CoroutineContext
suspend fun disposeAndJoin()
}// Access application properties
val app: Application = /* ... */
// Engine that runs the application (may be null during testing)
val engine: ApplicationEngine? = app.engine
// Root path for the application (useful for sub-applications)
val rootPath: String = app.rootPath
// Event monitoring system
val monitor: Events = app.monitor
// Parent coroutine context for structured concurrency
val parentContext: CoroutineContext = app.parentCoroutineContext// Dispose application resources
val disposeJob: Job = application.dispose()
// Dispose and wait for completion
application.disposeAndJoin()The ApplicationCallPipeline processes incoming requests through defined phases.
// Standard pipeline phases
object ApplicationPhase {
val Setup = PipelinePhase("Setup")
val Monitoring = PipelinePhase("Monitoring")
val Plugins = PipelinePhase("Plugins")
val Call = PipelinePhase("Call")
val Fallback = PipelinePhase("Fallback")
}class ApplicationCallPipeline(
developmentMode: Boolean = false,
environment: ApplicationEnvironment
) {
val receivePipeline: ApplicationReceivePipeline
val sendPipeline: ApplicationSendPipeline
val developmentMode: Boolean
val environment: ApplicationEnvironment
}interface ApplicationCall : CoroutineScope {
val attributes: Attributes
val request: ApplicationRequest
val response: ApplicationResponse
val application: Application
val parameters: Parameters
suspend fun <T> receiveNullable(typeInfo: TypeInfo): T?
suspend fun respond(message: Any?, typeInfo: TypeInfo?)
}
interface PipelineCall : ApplicationCall {
override val request: PipelineRequest
override val response: PipelineResponse
}interface PipelineCall : ApplicationCall {
val request: PipelineRequest
val response: PipelineResponse
}// Access current call from pipeline context
val PipelineContext<*, PipelineCall>.call: PipelineCall
// Access current application from pipeline context
val PipelineContext<*, PipelineCall>.application: Applicationinterface Plugin<TPipeline : Any, TConfiguration : Any, TPlugin : Any> {
val key: AttributeKey<TPlugin>
fun install(pipeline: TPipeline, configure: TConfiguration.() -> Unit): TPlugin
}// Base application plugin interface
interface BaseApplicationPlugin<TPipeline : Any, TConfiguration : Any, TPlugin : Any> :
Plugin<TPipeline, TConfiguration, TPlugin>
// Simplified application plugin interface
interface ApplicationPlugin<TConfiguration : Any> :
BaseApplicationPlugin<Application, TConfiguration, *>// Install plugin with configuration
fun <P : Pipeline<*, ApplicationCall>, B : Any, F : Any> P.install(
plugin: Plugin<P, B, F>,
configure: B.() -> Unit = {}
): F
// Example plugin installation
fun Application.configurePlugins() {
install(ContentNegotiation) {
json {
prettyPrint = true
isLenient = true
}
}
install(CallLogging) {
level = Level.INFO
filter { call -> call.request.path().startsWith("/api/") }
}
}// Get installed plugin instance (throws if not installed)
fun <F : Any> ApplicationCallPipeline.plugin(key: AttributeKey<F>): F
// Get plugin instance or null
fun <F : Any> ApplicationCallPipeline.pluginOrNull(key: AttributeKey<F>): F?
// Access plugin registry
val ApplicationCallPipeline.pluginRegistry: PluginRegistry
// Example plugin access
fun Application.usePlugin() {
val contentNegotiation = plugin(ContentNegotiation)
val logging = pluginOrNull(CallLogging)
if (logging != null) {
// Plugin is installed, use it
}
}// Create application plugin with configuration
fun <TConfiguration : Any> createApplicationPlugin(
name: String,
createConfiguration: () -> TConfiguration,
body: PluginBuilder<TConfiguration>.() -> Unit
): ApplicationPlugin<TConfiguration>
// Create route-scoped plugin
fun <TConfiguration : Any> createRouteScopedPlugin(
name: String,
createConfiguration: () -> TConfiguration,
body: PluginBuilder<TConfiguration>.() -> Unit
): Plugin<Route, TConfiguration, Unit>
// Example custom plugin
val CustomPlugin = createApplicationPlugin("CustomPlugin", ::CustomConfig) {
val config = pluginConfig
onCall { call ->
// Plugin logic for each call
call.attributes.put(CustomAttributeKey, config.value)
}
onCallReceive { call ->
// Intercept request content
transformBody { data ->
// Transform incoming data
processData(data)
}
}
}
// Plugin configuration class
class CustomConfig {
var value: String = "default"
var enabled: Boolean = true
}// Thrown when attempting to install duplicate plugin
class DuplicatePluginException(key: String) : Exception(
"Plugin $key is already installed"
)
// Thrown when accessing non-installed plugin
class MissingApplicationPluginException(key: AttributeKey<*>) : Exception(
"Plugin ${key.name} has not been installed"
)fun Application.handlePluginErrors() {
try {
install(SomePlugin)
install(SomePlugin) // This will throw DuplicatePluginException
} catch (e: DuplicatePluginException) {
log.warn("Plugin already installed: ${e.message}")
}
try {
val plugin = plugin(NonInstalledPlugin.key)
} catch (e: MissingApplicationPluginException) {
log.error("Required plugin not installed: ${e.message}")
}
}// Application lifecycle events
object ApplicationStarting : EventDefinition<Application>()
object ApplicationModulesLoading : EventDefinition<Application>()
object ApplicationModulesLoaded : EventDefinition<Application>()
object ApplicationStarted : EventDefinition<Application>()
object ServerReady : EventDefinition<ApplicationEngine>()
object ApplicationStopPreparing : EventDefinition<Application>()
object ApplicationStopping : EventDefinition<Application>()
object ApplicationStopped : EventDefinition<Application>()fun Application.subscribeToEvents() {
// Subscribe to application start
monitor.subscribe(ApplicationStarted) { application ->
application.log.info("Application started successfully")
// Initialize resources, open connections, etc.
}
// Subscribe to server ready (engine-specific)
monitor.subscribe(ServerReady) { engine ->
val connectors = engine.resolvedConnectors()
log.info("Server ready on connectors: $connectors")
}
// Subscribe to application stop preparation
monitor.subscribe(ApplicationStopPreparing) { application ->
application.log.info("Preparing to stop application")
// Clean up resources, close connections, etc.
}
// Subscribe to application stopped
monitor.subscribe(ApplicationStopped) { application ->
application.log.info("Application stopped")
}
}// Raise custom events
fun Application.raiseCustomEvent() {
// Define custom event
object CustomEvent : EventDefinition<String>()
// Subscribe to custom event
monitor.subscribe(CustomEvent) { data ->
log.info("Custom event received: $data")
}
// Raise the event
monitor.raise(CustomEvent, "event data")
}interface Hook<HookHandler : Function<Unit>> {
fun install(pipeline: ApplicationCallPipeline, handler: HookHandler)
}fun Application.setupHooks() {
// Example hook usage (implementation depends on specific hooks available)
// Hooks provide extension points for plugin developers
// Install hook handler
MyHook.install(this) { context ->
// Hook implementation
log.debug("Hook executed for call: ${context.call.request.uri}")
}
}data class ServerConfig internal constructor(
val environment: ApplicationEnvironment,
val rootPath: String = "",
val developmentMode: Boolean = false,
val parentCoroutineContext: CoroutineContext? = null
) {
// Internal properties
internal val modules: List<Application.() -> Unit>
internal val watchPaths: List<String>
}class ServerConfigBuilder {
var environment: ApplicationEnvironment
var watchPaths: MutableList<String>
var rootPath: String = ""
var developmentMode: Boolean = false
var parentCoroutineContext: CoroutineContext? = null
// Install application module
fun module(body: Application.() -> Unit)
}// Create server configuration
fun serverConfig(
configure: ServerConfigBuilder.() -> Unit = {}
): ServerConfig
// Example server configuration
val config = serverConfig {
environment = applicationEnvironment {
// Environment configuration
}
developmentMode = true
rootPath = "/api"
module {
// Application module configuration
configureRouting()
configureSerialization()
}
module {
// Additional module
configureSecurity()
}
}// Access logger from Application
val Application.log: Logger
// Example logging usage
fun Application.configureLogging() {
log.info("Configuring application")
monitor.subscribe(ApplicationStarted) { app ->
app.log.info("Application started successfully")
}
monitor.subscribe(ApplicationStopPreparing) { app ->
app.log.warn("Application is preparing to stop")
}
}// MDC (Mapped Diagnostic Context) for structured logging
interface MDCProvider {
fun withMDCBlock(block: () -> Unit)
}fun main() {
val server = embeddedServer(Netty, port = 8080) {
// Install core plugins
install(ContentNegotiation) {
json()
}
install(CallLogging) {
level = Level.INFO
}
// Subscribe to application events
monitor.subscribe(ApplicationStarted) { app ->
app.log.info("Server started on port 8080")
}
// Configure routing
routing {
get("/") {
call.respondText("Hello, Ktor!")
}
get("/plugins") {
val plugins = pluginRegistry.allKeys.map { it.name }
call.respond(plugins)
}
}
// Custom plugin installation
install(createApplicationPlugin("RequestId") {
onCall { call ->
val requestId = UUID.randomUUID().toString()
call.attributes.put(RequestIdKey, requestId)
call.response.header("X-Request-ID", requestId)
}
})
}
server.start(wait = true)
}
val RequestIdKey = AttributeKey<String>("RequestId")This comprehensive documentation covers all aspects of Ktor's application management system, from basic lifecycle control to advanced plugin development and event handling.
Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-server-core