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.
—
Hierarchical configuration system with type conversion, environment-specific settings, and support for multiple configuration sources including files, environment variables, and programmatic configuration.
Core interface for accessing application configuration with hierarchical property access and type conversion.
/**
* Interface for application configuration access
*/
interface ApplicationConfig {
/** Get configuration property by path */
fun property(path: String): ApplicationConfigValue
/** Get optional configuration property */
fun propertyOrNull(path: String): ApplicationConfigValue?
/** Get nested configuration section */
fun config(path: String): ApplicationConfig
/** Get list of configuration sections */
fun configList(path: String): List<ApplicationConfig>
/** Get all configuration keys at current level */
fun keys(): Set<String>
/** Convert configuration to map */
fun toMap(): Map<String, Any?>
}
/**
* Configuration value with type conversion
*/
interface ApplicationConfigValue {
/** Get value as string */
fun getString(): String
/** Get value as list of strings */
fun getList(): List<String>
}
/** Access configuration from application call */
val ApplicationCall.config: ApplicationConfig get() = application.environment.configExtension functions for type-safe configuration value access with default values and validation.
/**
* Get configuration value as Int
* @param default - Default value if property not found or conversion fails
* @return Integer value or default
*/
fun ApplicationConfigValue.getInt(default: Int = 0): Int = try {
getString().toInt()
} catch (e: Exception) {
default
}
/**
* Get configuration value as Long
*/
fun ApplicationConfigValue.getLong(default: Long = 0L): Long = try {
getString().toLong()
} catch (e: Exception) {
default
}
/**
* Get configuration value as Boolean
*/
fun ApplicationConfigValue.getBoolean(default: Boolean = false): Boolean = try {
getString().toBoolean()
} catch (e: Exception) {
default
}
/**
* Get configuration value as Double
*/
fun ApplicationConfigValue.getDouble(default: Double = 0.0): Double = try {
getString().toDouble()
} catch (e: Exception) {
default
}
/**
* Get required configuration value as Int
* @throws ConfigurationException if property not found or conversion fails
*/
fun ApplicationConfigValue.getIntOrFail(): Int = try {
getString().toInt()
} catch (e: Exception) {
throw ConfigurationException("Cannot convert configuration value to Int", e)
}
/**
* Get required configuration value as Long
*/
fun ApplicationConfigValue.getLongOrFail(): Long = try {
getString().toLong()
} catch (e: Exception) {
throw ConfigurationException("Cannot convert configuration value to Long", e)
}
/**
* Get required configuration value as Boolean
*/
fun ApplicationConfigValue.getBooleanOrFail(): Boolean = try {
getString().toBoolean()
} catch (e: Exception) {
throw ConfigurationException("Cannot convert configuration value to Boolean", e)
}Concrete implementations of ApplicationConfig for different configuration sources and patterns.
/**
* Map-based implementation of ApplicationConfig
*/
class MapApplicationConfig : ApplicationConfig {
/** Create config from map */
constructor(map: Map<String, Any>)
/** Create config from vararg pairs */
constructor(vararg values: Pair<String, Any>)
/** Put configuration value */
fun put(path: String, value: String)
/** Put all values from map */
fun putAll(values: Map<String, Any>)
/** Remove configuration value */
fun remove(path: String)
/** Clear all configuration */
fun clear()
}
/**
* Merges multiple configuration sources with precedence
*/
class MergedApplicationConfig(vararg sources: ApplicationConfig) : ApplicationConfig {
/** Add configuration source with higher precedence */
fun withFallback(config: ApplicationConfig): MergedApplicationConfig
}
/**
* Empty configuration implementation
*/
object EmptyApplicationConfig : ApplicationConfigUtilities for loading configuration from various sources including files, environment variables, and system properties.
/**
* Load configuration from HOCON file
* @param file - Configuration file to load
* @return ApplicationConfig instance
*/
fun loadApplicationConfig(file: File): ApplicationConfig
/**
* Load configuration from resource
* @param resource - Resource path to load
* @return ApplicationConfig instance
*/
fun loadApplicationConfig(resource: String): ApplicationConfig
/**
* Load configuration from multiple sources
* @param sources - Configuration sources in priority order
* @return Merged configuration
*/
fun loadApplicationConfig(vararg sources: ApplicationConfig): ApplicationConfig
/**
* Create configuration from environment variables
* @param prefix - Environment variable prefix to filter
* @return Configuration from environment
*/
fun environmentConfig(prefix: String = ""): ApplicationConfig
/**
* Create configuration from system properties
* @param prefix - System property prefix to filter
* @return Configuration from system properties
*/
fun systemPropertiesConfig(prefix: String = ""): ApplicationConfigUtilities for validating configuration values and providing meaningful error messages.
/**
* Configuration validation exception
*/
class ConfigurationException(message: String, cause: Throwable? = null) : Exception(message, cause)
/**
* Validate configuration property exists
* @param path - Configuration path to check
* @throws ConfigurationException if property not found
*/
fun ApplicationConfig.requireProperty(path: String): ApplicationConfigValue {
return propertyOrNull(path) ?: throw ConfigurationException("Required configuration property '$path' not found")
}
/**
* Validate configuration section exists
* @param path - Configuration section path to check
* @throws ConfigurationException if section not found
*/
fun ApplicationConfig.requireConfig(path: String): ApplicationConfig {
return try {
config(path)
} catch (e: Exception) {
throw ConfigurationException("Required configuration section '$path' not found", e)
}
}
/**
* Validate configuration value is in allowed set
* @param allowedValues - Set of allowed values
* @throws ConfigurationException if value not in set
*/
fun ApplicationConfigValue.requireOneOf(vararg allowedValues: String): String {
val value = getString()
if (value !in allowedValues) {
throw ConfigurationException("Configuration value '$value' must be one of: ${allowedValues.joinToString()}")
}
return value
}Scoped configuration access for different parts of the application with inheritance and overrides.
/**
* Scoped configuration access
*/
class ConfigurationScope(
private val config: ApplicationConfig,
private val path: String = ""
) {
/** Get property in current scope */
fun property(name: String): ApplicationConfigValue = config.property(scopedPath(name))
/** Get optional property in current scope */
fun propertyOrNull(name: String): ApplicationConfigValue? = config.propertyOrNull(scopedPath(name))
/** Create nested scope */
fun scope(name: String): ConfigurationScope = ConfigurationScope(config, scopedPath(name))
private fun scopedPath(name: String) = if (path.isEmpty()) name else "$path.$name"
}
/**
* Create configuration scope for specific section
* @param path - Section path for scope
* @return Scoped configuration access
*/
fun ApplicationConfig.scope(path: String): ConfigurationScope = ConfigurationScope(this, path)Usage Examples:
import io.ktor.server.application.*
import io.ktor.server.config.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
// Basic configuration access
fun Application.basicConfigExample() {
// Access configuration values
val appName = environment.config.property("app.name").getString()
val port = environment.config.property("server.port").getInt(8080)
val debugMode = environment.config.propertyOrNull("app.debug")?.getBoolean() ?: false
log.info("Starting $appName on port $port (debug: $debugMode)")
routing {
get("/config") {
call.respondText("App: $appName, Port: $port, Debug: $debugMode")
}
}
}
// Configuration with validation
fun Application.validatedConfigExample() {
try {
// Required configuration
val dbUrl = environment.config.requireProperty("database.url").getString()
val dbDriver = environment.config.requireProperty("database.driver").getString()
// Optional with defaults
val connectionTimeout = environment.config.propertyOrNull("database.timeout")?.getInt() ?: 30
val maxConnections = environment.config.propertyOrNull("database.maxConnections")?.getInt() ?: 10
// Validated enums
val logLevel = environment.config.requireProperty("logging.level")
.requireOneOf("DEBUG", "INFO", "WARN", "ERROR")
log.info("Database: $dbUrl (timeout: ${connectionTimeout}s, max connections: $maxConnections)")
log.info("Log level: $logLevel")
} catch (e: ConfigurationException) {
log.error("Configuration error: ${e.message}")
throw e
}
}
// Nested configuration sections
fun Application.nestedConfigExample() {
// Database configuration section
val dbConfig = environment.config.config("database")
val dbUrl = dbConfig.property("url").getString()
val dbUser = dbConfig.property("user").getString()
val dbPassword = dbConfig.property("password").getString()
// Server configuration list
val serverConfigs = environment.config.configList("servers")
for ((index, serverConfig) in serverConfigs.withIndex()) {
val host = serverConfig.property("host").getString()
val port = serverConfig.property("port").getInt()
log.info("Server $index: $host:$port")
}
routing {
get("/servers") {
val servers = serverConfigs.map { server ->
mapOf(
"host" to server.property("host").getString(),
"port" to server.property("port").getInt()
)
}
call.respond(servers)
}
}
}
// Programmatic configuration
fun Application.programmaticConfigExample() {
// Create configuration from map
val appConfig = MapApplicationConfig(
"app.name" to "My Ktor App",
"app.version" to "1.0.0",
"server.host" to "localhost",
"server.port" to "8080"
)
// Merge with environment variables
val envConfig = environmentConfig("KTOR_")
val mergedConfig = MergedApplicationConfig(envConfig, appConfig)
// Access merged configuration
val appName = mergedConfig.property("app.name").getString()
val host = mergedConfig.property("server.host").getString()
val port = mergedConfig.property("server.port").getInt()
log.info("App: $appName running on $host:$port")
}
// Configuration scopes
fun Application.scopedConfigExample() {
// Create database scope
val dbScope = environment.config.scope("database")
val url = dbScope.property("url").getString()
val user = dbScope.property("user").getString()
// Create cache scope
val cacheScope = environment.config.scope("cache")
val cacheSize = cacheScope.property("size").getInt(1000)
val cacheTtl = cacheScope.property("ttl").getLong(3600)
// Nested scopes
val redisScope = cacheScope.scope("redis")
val redisHost = redisScope.property("host").getString()
val redisPort = redisScope.property("port").getInt(6379)
log.info("Database: $url (user: $user)")
log.info("Cache: size=$cacheSize, ttl=${cacheTtl}s")
log.info("Redis: $redisHost:$redisPort")
}
// Route-specific configuration access
fun Application.routeConfigExample() {
routing {
get("/admin") {
// Access configuration within route
val adminEnabled = call.config.propertyOrNull("admin.enabled")?.getBoolean() ?: false
if (!adminEnabled) {
call.respond(HttpStatusCode.NotFound)
return@get
}
val adminTitle = call.config.property("admin.title").getString()
call.respondText("Welcome to $adminTitle")
}
get("/api/limits") {
val rateLimit = call.config.property("api.rateLimit").getInt(100)
val maxPayload = call.config.property("api.maxPayloadSize").getLong(1024 * 1024)
call.respond(mapOf(
"rateLimit" to rateLimit,
"maxPayloadSize" to maxPayload
))
}
}
}Example Configuration File (application.conf):
app {
name = "My Ktor Application"
version = "1.0.0"
debug = false
}
server {
host = "0.0.0.0"
port = 8080
}
database {
url = "jdbc:postgresql://localhost:5432/myapp"
driver = "org.postgresql.Driver"
user = "appuser"
password = "secret"
timeout = 30
maxConnections = 20
}
cache {
size = 10000
ttl = 3600
redis {
host = "localhost"
port = 6379
}
}
logging {
level = "INFO"
}
servers = [
{
host = "server1.example.com"
port = 8080
},
{
host = "server2.example.com"
port = 8080
}
]
admin {
enabled = true
title = "Admin Panel"
}
api {
rateLimit = 1000
maxPayloadSize = 5242880 # 5MB
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-server-core-jvm