CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Core server functionality for the Ktor asynchronous web framework, providing essential building blocks for HTTP servers including application lifecycle management, routing foundations, request/response handling, and plugin architecture specifically compiled for JVM targets.

Pending
Overview
Eval results
Files

plugins.mddocs/

Plugin System and Extensions

Plugin architecture for extending Ktor functionality with application-level and route-scoped plugins, providing lifecycle management, configuration systems, and extensible middleware patterns.

Capabilities

Plugin Architecture

Core plugin interfaces and implementation patterns for creating reusable extensions to Ktor applications.

/**
 * Base plugin interface for all Ktor plugins
 */
interface Plugin<
    in TPipeline : Pipeline<*, PipelineCall>,
    out TConfiguration : Any,
    TPlugin : Any
> {
    /** Unique key for plugin identification */
    val key: AttributeKey<TPlugin>
    /** Install plugin into pipeline */
    fun install(pipeline: TPipeline, configure: TConfiguration.() -> Unit): TPlugin
}

/**
 * Interface for application-level plugins
 */
interface BaseApplicationPlugin<
    TPipeline : ApplicationCallPipeline,
    TConfiguration : Any,
    TPlugin : Any
> : Plugin<TPipeline, TConfiguration, TPlugin>

/**
 * Interface for simple application plugins
 */
interface ApplicationPlugin<TConfiguration : Any> : 
    BaseApplicationPlugin<ApplicationCallPipeline, TConfiguration, PluginInstance>

/**
 * Interface for route-scoped plugins  
 */
interface BaseRouteScopedPlugin<TConfiguration : Any, TPlugin : Any> :
    Plugin<ApplicationCallPipeline, TConfiguration, TPlugin>

/**
 * Interface for simple route-scoped plugins
 */
interface RouteScopedPlugin<TConfiguration : Any> : 
    BaseRouteScopedPlugin<TConfiguration, PluginInstance>

/**
 * Builder for creating plugin configurations
 */
class PluginBuilder<TConfiguration : Any> {
    /** Plugin configuration instance */
    var pluginConfig: TConfiguration? = null
    /** Called during call processing */
    fun onCall(block: suspend PipelineContext<Unit, ApplicationCall>.(ApplicationCall) -> Unit)
    /** Called when receiving request content */
    fun onCallReceive(block: suspend PipelineContext<ApplicationReceiveRequest, ApplicationCall>.(ApplicationReceiveRequest) -> Unit) 
    /** Called when sending response content */
    fun onCallRespond(block: suspend PipelineContext<Any, ApplicationCall>.(Any) -> Unit)
}

/**
 * Builder for creating route-scoped plugin configurations
 */
class RouteScopedPluginBuilder<TConfiguration : Any> {
    /** Plugin configuration instance */
    var pluginConfig: TConfiguration? = null
    /** Called during call processing */
    fun onCall(block: suspend PipelineContext<Unit, ApplicationCall>.(ApplicationCall) -> Unit)
    /** Called when receiving request content */
    fun onCallReceive(block: suspend PipelineContext<ApplicationReceiveRequest, ApplicationCall>.(ApplicationReceiveRequest) -> Unit) 
    /** Called when sending response content */
    fun onCallRespond(block: suspend PipelineContext<Any, ApplicationCall>.(Any) -> Unit)
}

Plugin Creation

Factory functions for creating custom application and route-scoped plugins with configuration support.

/**
 * Creates an application plugin with configuration
 * @param name - Plugin name for identification
 * @param createConfiguration - Function to create default configuration
 * @param body - Plugin builder configuration
 */
fun <PluginConfigT : Any> createApplicationPlugin(
    name: String,
    createConfiguration: () -> PluginConfigT,
    body: PluginBuilder<PluginConfigT>.() -> Unit
): ApplicationPlugin<PluginConfigT>

/**
 * Creates an application plugin with configuration path
 * @param name - Plugin name for identification
 * @param configurationPath - Path in configuration file to plugin config
 * @param createConfiguration - Function to create configuration from ApplicationConfig
 * @param body - Plugin builder configuration
 */
fun <PluginConfigT : Any> createApplicationPlugin(
    name: String,
    configurationPath: String,
    createConfiguration: (config: ApplicationConfig) -> PluginConfigT,
    body: PluginBuilder<PluginConfigT>.() -> Unit
): ApplicationPlugin<PluginConfigT>

/**
 * Creates a simple application plugin without configuration
 * @param name - Plugin name
 * @param body - Plugin installation logic
 */
fun createApplicationPlugin(
    name: String,
    body: PluginBuilder<Unit>.() -> Unit
): ApplicationPlugin<Unit>

/**
 * Creates a route-scoped plugin with configuration
 * @param name - Plugin name for identification  
 * @param createConfiguration - Function to create default configuration
 * @param body - Plugin builder configuration
 */
fun <PluginConfigT : Any> createRouteScopedPlugin(
    name: String,
    createConfiguration: () -> PluginConfigT,
    body: RouteScopedPluginBuilder<PluginConfigT>.() -> Unit
): RouteScopedPlugin<PluginConfigT>

/**
 * Creates a simple route-scoped plugin without configuration
 * @param name - Plugin name
 * @param body - Plugin installation logic
 */
fun createRouteScopedPlugin(
    name: String,
    body: RouteScopedPluginBuilder<Unit>.() -> Unit
): RouteScopedPlugin<Unit>

Plugin Installation

Functions for installing plugins into applications and routes with configuration support.

/**
 * Install application plugin with configuration
 * @param plugin - Plugin to install
 * @param configure - Configuration block
 */
fun <TConfiguration : Any> Application.install(
    plugin: ApplicationPlugin<TConfiguration>,
    configure: TConfiguration.() -> Unit = {}
): PluginInstance

/**
 * Install base application plugin with configuration
 * @param plugin - Plugin to install
 * @param configure - Configuration block
 */
fun <TConfiguration : Any, TPlugin : Any> ApplicationCallPipeline.install(
    plugin: BaseApplicationPlugin<*, TConfiguration, TPlugin>,
    configure: TConfiguration.() -> Unit = {}
): TPlugin

/**
 * Get installed plugin instance
 * @param plugin - Plugin to get
 * @return Plugin instance or null if not installed
 */
fun <TPlugin : Any> ApplicationCallPipeline.pluginOrNull(
    plugin: Plugin<*, *, TPlugin>
): TPlugin?

/**
 * Get installed plugin instance
 * @param plugin - Plugin to get
 * @return Plugin instance
 * @throws PluginNotInstalledException if plugin not installed
 */
fun <TPlugin : Any> ApplicationCallPipeline.plugin(
    plugin: Plugin<*, *, TPlugin>
): TPlugin

/**
 * Install route-scoped plugin with configuration
 * @param plugin - Plugin to install
 * @param configure - Configuration block  
 */
fun <TConfiguration : Any> Route.install(
    plugin: RouteScopedPlugin<TConfiguration, *>,
    configure: TConfiguration.() -> Unit = {}
)

/**
 * Get installed plugin instance
 * @param plugin - Plugin key to retrieve
 * @return Plugin instance
 */
fun <A : Any, B : Any> Application.plugin(plugin: Plugin<A, B, *>): B

/**
 * Check if plugin is installed
 * @param plugin - Plugin to check
 * @return True if plugin is installed
 */
fun Application.pluginOrNull(plugin: Plugin<*, *, *>): Any?

Built-in Plugin Exceptions

Standard exception types for plugin error handling and request validation.

/**
 * Exception for bad request errors (400)
 */
class BadRequestException(message: String, cause: Throwable? = null) : Exception(message, cause)

/**
 * Exception for not found errors (404)
 */
class NotFoundException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)

/**
 * Exception for unsupported media type errors (415)
 */
class UnsupportedMediaTypeException(contentType: ContentType) : Exception("Content type $contentType is not supported")

/**
 * Exception for missing request parameters
 */
class MissingRequestParameterException(parameterName: String) : BadRequestException("Request parameter $parameterName is missing")

/**
 * Exception for parameter conversion errors
 */
class ParameterConversionException(
    parameterName: String,
    type: String,
    cause: Throwable? = null
) : BadRequestException("Value for parameter $parameterName cannot be converted to $type", cause)

/**
 * Exception for configuration errors
 */
class ConfigurationException(message: String, cause: Throwable? = null) : Exception(message, cause)

Plugin Configuration

Base classes and patterns for plugin configuration with validation and type safety.

/**
 * Base interface for plugin configurations
 */
interface PluginConfiguration

/**
 * Configuration builder with validation
 */
abstract class ConfigurationBuilder {
    /** Validate configuration before installation */
    abstract fun validate()
    /** Build final configuration */
    abstract fun build(): PluginConfiguration
}

/**
 * Attribute key for storing plugin instances
 */
data class AttributeKey<T>(val name: String) {
    companion object {
        /** Create new attribute key */
        fun <T> create(name: String): AttributeKey<T> = AttributeKey(name)
    }
}

Plugin Lifecycle Hooks

Hooks for plugin lifecycle management and integration with application events.

/**
 * Plugin lifecycle events
 */
interface PluginLifecycle {
    /** Called after plugin installation */
    fun onInstall() {}
    /** Called when application starts */
    fun onStart() {}
    /** Called when application stops */  
    fun onStop() {}
    /** Called when application configuration changes */
    fun onConfigurationChange() {}
}

/**
 * Hook definition for plugin events
 */
class PluginHook<T : Function<Unit>> {
    /** Install event handler */
    fun install(handler: T)
    /** Uninstall event handler */
    fun uninstall(handler: T)
}

Usage Examples:

import io.ktor.server.application.*
import io.ktor.server.plugins.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

// Creating a simple application plugin
val RequestLoggingPlugin = createApplicationPlugin("RequestLogging") {
    onInstall { pipeline ->
        pipeline.intercept(ApplicationCallPipeline.Setup) {
            val start = System.currentTimeMillis()
            
            proceed()
            
            val duration = System.currentTimeMillis() - start
            application.log.info("${call.request.httpMethod.value} ${call.request.uri} - ${duration}ms")
        }
    }
}

// Creating a configurable application plugin
class RateLimitConfig {
    var requestsPerMinute: Int = 60
    var keyProvider: (ApplicationCall) -> String = { it.request.local.remoteHost }
}

val RateLimitPlugin = createApplicationPlugin(
    name = "RateLimit",
    createConfiguration = ::RateLimitConfig
) {
    val config = pluginConfig as RateLimitConfig
    val requestCounts = mutableMapOf<String, MutableList<Long>>()
    
    onInstall { pipeline ->
        pipeline.intercept(ApplicationCallPipeline.Plugins) {
            val key = config.keyProvider(call)
            val now = System.currentTimeMillis()
            val windowStart = now - 60_000 // 1 minute window
            
            // Clean old requests
            requestCounts[key]?.removeAll { it < windowStart }
            
            // Check rate limit
            val requests = requestCounts.getOrPut(key) { mutableListOf() }
            if (requests.size >= config.requestsPerMinute) {
                call.respond(HttpStatusCode.TooManyRequests, "Rate limit exceeded")
                return@intercept finish()
            }
            
            // Add current request
            requests.add(now)
            proceed()
        }
    }
}

// Creating a route-scoped plugin
class AuthConfig {
    var validate: suspend (String) -> Boolean = { false }
    var challenge: suspend ApplicationCall.() -> Unit = {
        respond(HttpStatusCode.Unauthorized, "Authentication required")
    }
}

val BasicAuthPlugin = createRouteScopedPlugin(
    name = "BasicAuth",
    createConfiguration = ::AuthConfig
) {
    val config = pluginConfig as AuthConfig
    
    onInstall { pipeline ->
        pipeline.intercept(ApplicationCallPipeline.Plugins) {
            val authHeader = call.request.headers["Authorization"]
            
            if (authHeader?.startsWith("Basic ") != true) {
                config.challenge(call)
                return@intercept finish()
            }
            
            val token = authHeader.removePrefix("Basic ")
            if (!config.validate(token)) {
                config.challenge(call)
                return@intercept finish()
            }
            
            proceed()
        }
    }
}

// Installing and using plugins
fun Application.configurePlugins() {
    // Install simple plugin
    install(RequestLoggingPlugin)
    
    // Install configurable plugin
    install(RateLimitPlugin) {
        requestsPerMinute = 100
        keyProvider = { call ->
            call.request.headers["X-API-Key"] ?: call.request.local.remoteHost
        }
    }
}

fun Application.configureRouting() {
    routing {
        // Public routes
        get("/") {
            call.respondText("Welcome!")
        }
        
        get("/health") {
            call.respondText("OK")
        }
        
        // Protected admin routes
        route("/admin") {
            install(BasicAuthPlugin) {
                validate = { token ->
                    // Decode and validate Basic auth token
                    val decoded = Base64.getDecoder().decode(token).toString(Charsets.UTF_8)
                    val (username, password) = decoded.split(":", limit = 2)
                    username == "admin" && password == "secret"
                }
                challenge = {
                    response.headers.append("WWW-Authenticate", "Basic realm=\"Admin Area\"")
                    respond(HttpStatusCode.Unauthorized, "Admin access required")
                }
            }
            
            get("/dashboard") {
                call.respondText("Admin Dashboard")
            }
            
            get("/users") {
                call.respondText("User Management")
            }
        }
        
        // API routes with specific rate limiting
        route("/api") {
            install(RateLimitPlugin) {
                requestsPerMinute = 30 // Lower limit for API
            }
            
            get("/data") {
                call.respond(mapOf("data" to "API response"))
            }
        }
    }
}

// Advanced plugin with lifecycle management
class DatabaseConnectionPlugin {
    lateinit var connection: DatabaseConnection
    
    companion object : ApplicationPlugin<DatabaseConfig, DatabaseConnectionPlugin, DatabaseConnectionPlugin> {
        override val key = AttributeKey<DatabaseConnectionPlugin>("DatabaseConnection")
        
        override fun install(
            pipeline: ApplicationCallPipeline,
            configure: DatabaseConfig.() -> Unit
        ): DatabaseConnectionPlugin {
            val config = DatabaseConfig().apply(configure)
            val plugin = DatabaseConnectionPlugin()
            
            plugin.connection = createConnection(config)
            
            // Add connection to call attributes
            pipeline.intercept(ApplicationCallPipeline.Setup) {
                call.attributes.put(DatabaseConnectionKey, plugin.connection)
            }
            
            return plugin
        }
    }
}

data class DatabaseConfig(
    var url: String = "jdbc:h2:mem:test",
    var driver: String = "org.h2.Driver",
    var user: String = "sa",
    var password: String = ""
)

val DatabaseConnectionKey = AttributeKey<DatabaseConnection>("DatabaseConnection")

// Usage with lifecycle
fun Application.configureDatabasePlugin() {
    install(DatabaseConnectionPlugin) {
        url = "jdbc:postgresql://localhost/mydb"
        user = "dbuser"
        password = "dbpass"
    }
    
    // Access database in routes
    routing {
        get("/users") {
            val db = call.attributes[DatabaseConnectionKey]
            val users = db.query("SELECT * FROM users")
            call.respond(users)
        }
    }
}

Install with Tessl CLI

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

docs

application.md

config.md

engine.md

index.md

plugins.md

request.md

response.md

routing.md

tile.json