Ktor server core module for iOS ARM64 target providing essential server-side functionality for building asynchronous web applications and microservices in Kotlin Multiplatform
—
HTTP-specific utilities, link headers, server push support, logging capabilities, and various helper functions for enhanced server functionality.
Support for HTTP Link headers as defined in RFC 8288.
/**
* Represents HTTP Link header information
*/
data class Link(
/** URI reference for the link */
val uri: String,
/** Relation types for this link */
val rel: List<String>,
/** Content type of the linked resource */
val type: ContentType? = null,
/** Human-readable title for the link */
val title: String? = null
)
/**
* Add Link header to response
*/
fun ApplicationResponse.linkHeader(link: Link)
/**
* Add Link header to response with simplified parameters
*/
fun ApplicationResponse.linkHeader(
uri: String,
rel: String,
type: ContentType? = null,
title: String? = null
)Support for HTTP/2 server push functionality.
/**
* Initiate HTTP/2 server push
*/
suspend fun ApplicationCall.push(block: ResponsePushBuilder.() -> Unit)
/**
* Builder for HTTP/2 push promises
*/
class ResponsePushBuilder {
/** URL to push */
var url: Url
/** HTTP method for pushed request */
var method: HttpMethod
/** Headers for pushed request */
val headers: HeadersBuilder
}Default content transformations for request and response processing.
/**
* Configure default content transformations
*/
fun ApplicationSendPipeline.defaultTransformations()
/**
* Configure default content transformations for receive pipeline
*/
fun ApplicationReceivePipeline.defaultTransformations()Utilities for parsing HTTP headers and cookies.
/**
* Parse Cookie header into individual cookies
*/
fun parseClientCookiesHeader(cookiesHeader: String): Map<String, String>
/**
* Parse Set-Cookie header
*/
fun parseServerSetCookieHeader(setCookieHeader: String): CookieMapped Diagnostic Context (MDC) support for structured logging.
/**
* Provides MDC (Mapped Diagnostic Context) functionality
*/
interface MDCProvider {
/** Get MDC value by key */
fun get(key: String): String?
/** Set MDC value */
fun put(key: String, value: String?)
/** Remove MDC value */
fun remove(key: String)
/** Clear all MDC values */
fun clear()
/** Get copy of current MDC context */
fun getCopyOfContextMap(): Map<String, String>?
/** Set entire MDC context */
fun setContextMap(contextMap: Map<String, String>)
}
/**
* Call logging plugin for HTTP request/response logging
*/
object CallLogging : ApplicationPlugin<CallLoggingConfig>Data class for representing connection point information, useful for proxy scenarios.
/**
* Represents connection point information for forwarded requests
*/
data class OriginConnectionPoint(
/** URI scheme (http, https) */
val scheme: String,
/** Host name */
val host: String,
/** Port number */
val port: Int,
/** HTTP version */
val version: String
)Enhanced parameter handling and URL building utilities.
/**
* Type alias for parameter collections (string-based key-value pairs)
*/
typealias Parameters = StringValues
/**
* Get parameter or throw exception if missing
*/
fun Parameters.getOrFail(name: String): String
/**
* Encode parameters to URL format
*/
fun encodeParameters(parameters: Parameters): String
/**
* Parse URL-encoded parameters
*/
fun parseParameters(encoded: String): ParametersUtilities for building URLs relative to current request context.
/**
* Build URLs relative to current request
*/
fun ApplicationCall.url(block: URLBuilder.() -> Unit = {}): String
/**
* Build paths relative to current request
*/
fun ApplicationCall.path(vararg segments: String): String
/**
* Build URL with specific path segments
*/
fun ApplicationCall.href(path: String, block: URLBuilder.() -> Unit = {}): StringUtilities for path manipulation and normalization.
/**
* Combine path segments safely
*/
fun combinePath(vararg segments: String): String
/**
* Resolve relative paths
*/
fun resolvePath(base: String, relative: String): String
/**
* Normalize and relativize paths
*/
fun normalizeAndRelativize(path: String): StringCopy-on-write hash map implementation for thread-safe operations.
/**
* Thread-safe copy-on-write hash map implementation
*/
class CopyOnWriteHashMap<K, V> : MutableMap<K, V> {
/** Get value by key */
override fun get(key: K): V?
/** Put key-value pair, returns previous value */
override fun put(key: K, value: V): V?
/** Remove key, returns previous value */
override fun remove(key: K): V?
/** Clear all entries */
override fun clear()
/** Get current size */
override val size: Int
/** Check if empty */
override fun isEmpty(): Boolean
/** Check if contains key */
override fun containsKey(key: K): Boolean
/** Check if contains value */
override fun containsValue(value: V): Boolean
/** Get all keys */
override val keys: MutableSet<K>
/** Get all values */
override val values: MutableCollection<V>
/** Get all entries */
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
}Plugin for automatic data type conversion.
/**
* Plugin for data conversion between types
*/
object DataConversion : ApplicationPlugin<DataConversionConfig>
/**
* Configuration for data conversion
*/
class DataConversionConfig {
/** Add conversion from one type to another */
fun <T, R> convert(from: KClass<T>, to: KClass<R>, converter: (T) -> R)
/** Add conversion with conditions */
fun <T, R> convert(
from: KClass<T>,
to: KClass<R>,
predicate: (T) -> Boolean,
converter: (T) -> R
)
}Exception classes for common HTTP error scenarios.
/**
* Exception for bad requests (400)
*/
class BadRequestException(message: String = "Bad Request", cause: Throwable? = null) : Exception(message, cause)
/**
* Exception for not found resources (404)
*/
class NotFoundException(message: String = "Not Found", cause: Throwable? = null) : Exception(message, cause)
/**
* Exception when content cannot be transformed to requested type
*/
class CannotTransformContentToTypeException(
message: String,
cause: Throwable? = null
) : Exception(message, cause)
/**
* Exception for unsupported media types (415)
*/
class UnsupportedMediaTypeException(
contentType: ContentType,
cause: Throwable? = null
) : Exception("Unsupported media type: $contentType", cause)import io.ktor.server.application.*
import io.ktor.server.http.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.linkHeaders() {
routing {
get("/article/{id}") {
val articleId = call.parameters["id"]
// Add link headers for related resources
call.response.linkHeader(
uri = "/api/articles/$articleId",
rel = "canonical"
)
call.response.linkHeader(Link(
uri = "/articles/$articleId/comments",
rel = listOf("related", "comments"),
type = ContentType.Application.Json,
title = "Article Comments"
))
call.response.linkHeader(
uri = "/css/article.css",
rel = "stylesheet",
type = ContentType.Text.CSS
)
call.respondText("Article $articleId")
}
get("/api/users") {
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
val limit = call.request.queryParameters["limit"]?.toIntOrNull() ?: 20
// Pagination links
if (page > 1) {
call.response.linkHeader(
uri = "/api/users?page=${page - 1}&limit=$limit",
rel = "prev"
)
}
call.response.linkHeader(
uri = "/api/users?page=${page + 1}&limit=$limit",
rel = "next"
)
call.response.linkHeader(
uri = "/api/users?page=1&limit=$limit",
rel = "first"
)
call.respond(emptyList<String>()) // Mock response
}
}
}import io.ktor.server.application.*
import io.ktor.server.http.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.serverPush() {
routing {
get("/") {
// Push related resources before sending main response
call.push {
url = Url("/css/main.css")
method = HttpMethod.Get
headers.append(HttpHeaders.Accept, "text/css")
}
call.push {
url = Url("/js/main.js")
method = HttpMethod.Get
headers.append(HttpHeaders.Accept, "application/javascript")
}
call.push {
url = Url("/images/logo.png")
method = HttpMethod.Get
headers.append(HttpHeaders.Accept, "image/*")
}
// Send main HTML response
call.respondText("""
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/css/main.css">
<script src="/js/main.js" defer></script>
</head>
<body>
<img src="/images/logo.png" alt="Logo">
<h1>Welcome</h1>
</body>
</html>
""".trimIndent(), ContentType.Text.Html)
}
}
}import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.util.*
fun Application.urlBuilding() {
routing {
get("/user/{id}") {
val userId = call.parameters["id"]
// Build URLs relative to current request
val profileUrl = call.url {
path("profile")
}
val apiUrl = call.url {
path("api", "users", userId ?: "")
parameters.append("include", "profile")
}
val avatarPath = call.path("avatar.jpg")
val editHref = call.href("/edit") {
parameters.append("id", userId ?: "")
}
call.respond(mapOf(
"userId" to userId,
"profileUrl" to profileUrl,
"apiUrl" to apiUrl,
"avatarPath" to avatarPath,
"editHref" to editHref
))
}
get("/docs/{...}") {
val pathSegments = call.parameters.getAll("...")
val docPath = pathSegments?.joinToString("/") ?: ""
// Path utilities
val normalizedPath = normalizeAndRelativize(docPath)
val combinedPath = combinePath("docs", "v1", normalizedPath)
val resolvedPath = resolvePath("/documentation", combinedPath)
call.respond(mapOf(
"originalPath" to docPath,
"normalizedPath" to normalizedPath,
"combinedPath" to combinedPath,
"resolvedPath" to resolvedPath
))
}
}
}import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.util.*
fun Application.parameterHandling() {
routing {
get("/required") {
try {
val userId = call.request.queryParameters.getOrFail("userId")
val action = call.request.queryParameters.getOrFail("action")
call.respond(mapOf(
"userId" to userId,
"action" to action
))
} catch (e: IllegalArgumentException) {
call.respond(HttpStatusCode.BadRequest, "Missing required parameters")
}
}
post("/form") {
val formParams = call.receiveParameters()
// Encode parameters back to URL format
val encoded = encodeParameters(formParams)
call.respond(mapOf(
"received" to formParams.toMap(),
"encoded" to encoded
))
}
}
}import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.util.*
class SessionManager {
private val sessions = CopyOnWriteHashMap<String, SessionData>()
fun createSession(sessionId: String, data: SessionData) {
sessions[sessionId] = data
}
fun getSession(sessionId: String): SessionData? {
return sessions[sessionId]
}
fun removeSession(sessionId: String): SessionData? {
return sessions.remove(sessionId)
}
fun getAllSessions(): Map<String, SessionData> {
return sessions.toMap()
}
fun getActiveSessionCount(): Int {
return sessions.size
}
}
data class SessionData(
val userId: String,
val createdAt: Long,
val lastAccessedAt: Long
)
fun Application.sessionHandling() {
val sessionManager = SessionManager()
routing {
post("/login") {
val sessionId = generateSessionId()
val sessionData = SessionData(
userId = "user123",
createdAt = System.currentTimeMillis(),
lastAccessedAt = System.currentTimeMillis()
)
sessionManager.createSession(sessionId, sessionData)
call.respond(mapOf(
"sessionId" to sessionId,
"activeSessions" to sessionManager.getActiveSessionCount()
))
}
get("/session/{id}") {
val sessionId = call.parameters["id"]
val session = sessionManager.getSession(sessionId ?: "")
if (session != null) {
call.respond(session)
} else {
call.respond(HttpStatusCode.NotFound, "Session not found")
}
}
}
}
private fun generateSessionId(): String {
return java.util.UUID.randomUUID().toString()
}import io.ktor.server.application.*
import io.ktor.server.plugins.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.errorHandling() {
routing {
get("/validate/{value}") {
val value = call.parameters["value"]
if (value.isNullOrBlank()) {
throw BadRequestException("Value parameter is required")
}
val intValue = value.toIntOrNull()
?: throw BadRequestException("Value must be a valid integer")
if (intValue < 0) {
throw BadRequestException("Value must be positive")
}
call.respond(mapOf("validatedValue" to intValue))
}
get("/resource/{id}") {
val resourceId = call.parameters["id"]
val resource = findResourceById(resourceId)
if (resource == null) {
throw NotFoundException("Resource with ID $resourceId not found")
}
call.respond(resource)
}
post("/convert") {
val contentType = call.request.contentType
when {
contentType.match(ContentType.Application.Json) -> {
try {
val data = call.receive<Map<String, Any>>()
call.respond(data)
} catch (e: Exception) {
throw CannotTransformContentToTypeException(
"Failed to parse JSON content",
e
)
}
}
contentType.match(ContentType.Application.Xml) -> {
throw UnsupportedMediaTypeException(contentType)
}
else -> {
throw UnsupportedMediaTypeException(contentType)
}
}
}
}
}
private fun findResourceById(id: String?): Map<String, Any>? {
// Mock implementation
return if (id == "123") {
mapOf("id" to id, "name" to "Sample Resource")
} else {
null
}
}import io.ktor.server.application.*
import io.ktor.server.plugins.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
data class User(val id: Int, val name: String, val email: String)
data class UserDto(val id: String, val displayName: String, val contactEmail: String)
fun Application.dataConversion() {
install(DataConversion) {
// Convert User to UserDto
convert<User, UserDto> { user ->
UserDto(
id = user.id.toString(),
displayName = user.name,
contactEmail = user.email
)
}
// Convert String to Int with validation
convert<String, Int>(
predicate = { it.matches(Regex("\\d+")) }
) { it.toInt() }
// Convert String to Boolean
convert<String, Boolean> { str ->
when (str.lowercase()) {
"true", "yes", "1" -> true
"false", "no", "0" -> false
else -> throw IllegalArgumentException("Invalid boolean value: $str")
}
}
}
routing {
get("/user/{id}") {
val userId = call.parameters["id"]?.toIntOrNull()
?: return@get call.respond(HttpStatusCode.BadRequest)
val user = User(userId, "John Doe", "john@example.com")
// DataConversion will automatically convert User to UserDto
call.respond(user)
}
}
}import io.ktor.server.application.*
import io.ktor.server.logging.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.loggingAndMdc() {
install(CallLogging) {
level = Level.INFO
mdc("requestId") {
java.util.UUID.randomUUID().toString()
}
mdc("userId") { call ->
call.request.headers["X-User-ID"]
}
filter { call ->
!call.request.path().startsWith("/health")
}
}
routing {
get("/api/data") {
val mdcProvider = application.environment.monitor.mdcProvider
// Add custom MDC values
mdcProvider?.put("operation", "fetchData")
mdcProvider?.put("startTime", System.currentTimeMillis().toString())
try {
// Simulate data fetching
val data = fetchData()
mdcProvider?.put("recordCount", data.size.toString())
application.environment.log.info("Data fetch completed successfully")
call.respond(data)
} catch (e: Exception) {
mdcProvider?.put("error", e.message ?: "Unknown error")
application.environment.log.error("Data fetch failed", e)
call.respond(HttpStatusCode.InternalServerError, "Data fetch failed")
} finally {
mdcProvider?.remove("operation")
mdcProvider?.remove("startTime")
mdcProvider?.remove("recordCount")
mdcProvider?.remove("error")
}
}
}
}
private fun fetchData(): List<String> {
// Mock data fetching
return listOf("item1", "item2", "item3")
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-server-core-iosarm64