A multiplatform asynchronous framework for creating microservices, web applications, and HTTP clients written in Kotlin from the ground up
—
Plugin installation, configuration, and built-in plugins for extending client functionality with features like timeouts, cookies, authentication, and more.
Core interface for creating and installing client plugins.
/**
* Interface for HTTP client plugins
*/
interface HttpClientPlugin<out TConfig : Any, TPlugin : Any> {
/** Unique key for identifying the plugin */
val key: AttributeKey<TPlugin>
/**
* Prepare the plugin with configuration
* @param block Configuration block
* @return Configured plugin instance
*/
fun prepare(block: TConfig.() -> Unit = {}): TPlugin
/**
* Install the plugin into a client
* @param plugin Prepared plugin instance
* @param scope HttpClient to install into
*/
fun install(plugin: TPlugin, scope: HttpClient)
}Methods for installing plugins into HttpClient configurations.
/**
* Install a plugin with configuration in HttpClientConfig
* @param plugin The plugin to install
* @param configure Configuration block for the plugin
*/
fun <TBuilder : Any, TPlugin : Any> HttpClientConfig<*>.install(
plugin: HttpClientPlugin<TBuilder, TPlugin>,
configure: TBuilder.() -> Unit = {}
)
/**
* Install a plugin by key with custom installation logic
* @param key String identifier for the plugin
* @param block Installation logic
*/
fun HttpClientConfig<*>.install(key: String, block: HttpClient.() -> Unit)Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.*
val client = HttpClient {
// Install plugin with configuration
install(HttpTimeout) {
requestTimeoutMillis = 30000
connectTimeoutMillis = 10000
socketTimeoutMillis = 60000
}
// Install plugin without configuration
install(HttpRedirect)
// Install by string key
install("CustomPlugin") {
// Custom installation logic
}
}Configures request, connection, and socket timeouts.
/**
* HTTP timeout plugin for configuring request timeouts
*/
val HttpTimeout: ClientPlugin<HttpTimeoutConfig>
/**
* Configuration for HTTP timeout plugin
*/
class HttpTimeoutConfig {
/** Total request timeout in milliseconds */
var requestTimeoutMillis: Long? = null
/** Connection timeout in milliseconds */
var connectTimeoutMillis: Long? = null
/** Socket timeout in milliseconds */
var socketTimeoutMillis: Long? = null
}
/**
* Configure timeout for a specific request
* @param block Configuration block for timeout
*/
fun HttpRequestBuilder.timeout(block: HttpTimeoutConfig.() -> Unit)Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
// Global timeout configuration
val client = HttpClient {
install(HttpTimeout) {
requestTimeoutMillis = 30000
connectTimeoutMillis = 5000
socketTimeoutMillis = 60000
}
}
// Per-request timeout configuration
val response = client.get("https://api.example.com/slow-endpoint") {
timeout {
requestTimeoutMillis = 60000
}
}Handles HTTP redirects automatically.
/**
* HTTP redirect plugin for handling redirects
*/
val HttpRedirect: ClientPlugin<HttpRedirect.Config>
class HttpRedirect {
/**
* Configuration for HTTP redirect plugin
*/
class Config {
/** Whether to check HTTP method when following redirects */
var checkHttpMethod: Boolean = true
/** Whether to allow HTTPS to HTTP downgrades */
var allowHttpsDowngrade: Boolean = false
/** Maximum number of redirect jumps to follow */
var maxJumps: Int = 20
}
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.*
val client = HttpClient {
install(HttpRedirect) {
checkHttpMethod = false
allowHttpsDowngrade = true
maxJumps = 10
}
}Validates responses and handles exceptions.
/**
* HTTP call validator plugin for response validation
*/
val HttpCallValidator: ClientPlugin<HttpCallValidator.Config>
class HttpCallValidator {
/**
* Configuration for HTTP call validator plugin
*/
class Config {
/**
* Add response validation logic
* @param block Validation block that receives the response
*/
fun validateResponse(block: suspend (response: HttpResponse) -> Unit)
/**
* Add exception handling logic for requests
* @param block Exception handling block
*/
fun handleResponseExceptionWithRequest(
block: suspend (exception: Throwable, request: HttpRequest) -> Unit
)
}
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.statement.*
val client = HttpClient {
install(HttpCallValidator) {
validateResponse { response ->
if (response.status.value !in 200..299) {
throw ResponseException(response, "HTTP ${response.status}")
}
}
handleResponseExceptionWithRequest { exception, request ->
println("Request to ${request.url} failed: ${exception.message}")
}
}
}Sets the User-Agent header for requests.
/**
* User-Agent plugin for setting user agent header
*/
val UserAgent: ClientPlugin<UserAgentConfig>
/**
* Configuration for User-Agent plugin
*/
class UserAgentConfig {
/** User agent string to use */
var agent: String = "Ktor client"
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.*
val client = HttpClient {
install(UserAgent) {
agent = "MyApplication/1.0.0"
}
}Sets default configuration for all requests.
/**
* Default request plugin for setting default request configuration
*/
val DefaultRequest: ClientPlugin<DefaultRequest.Config>
class DefaultRequest {
/**
* Configuration for default request plugin
*/
class Config {
/**
* Configure default request settings
* @param block Configuration block applied to all requests
*/
fun request(block: HttpRequestBuilder.() -> Unit)
}
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.http.*
val client = HttpClient {
install(DefaultRequest) {
request {
host = "api.example.com"
url {
protocol = URLProtocol.HTTPS
path("v1/")
}
header(HttpHeaders.Authorization, "Bearer $token")
}
}
}
// This request will use the default configuration
val response = client.get("users") // Becomes https://api.example.com/v1/usersHandles HTTP cookies automatically.
/**
* HTTP cookies plugin for cookie management
*/
val HttpCookies: ClientPlugin<HttpCookies.Config>
class HttpCookies {
/**
* Configuration for HTTP cookies plugin
*/
class Config {
/** Cookie storage implementation */
var storage: CookiesStorage = AcceptAllCookiesStorage()
}
}
/**
* Interface for cookie storage implementations
*/
interface CookiesStorage : Closeable {
/**
* Get cookies for a request URL
* @param requestUrl URL of the request
* @return List of applicable cookies
*/
suspend fun get(requestUrl: Url): List<Cookie>
/**
* Add a cookie from a response
* @param requestUrl URL of the request
* @param cookie Cookie to store
*/
suspend fun addCookie(requestUrl: Url, cookie: Cookie)
}
/**
* Cookie storage that accepts all cookies
*/
class AcceptAllCookiesStorage : CookiesStorage
/**
* Cookie storage with constant cookies
*/
class ConstantCookiesStorage(vararg cookies: Cookie) : CookiesStorageUsage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.cookies.*
import io.ktor.http.*
val client = HttpClient {
install(HttpCookies) {
storage = AcceptAllCookiesStorage()
}
}
// Custom cookie storage
val customStorage = ConstantCookiesStorage(
Cookie("session", "abc123", domain = "example.com"),
Cookie("preferences", "theme=dark", domain = "example.com")
)
val clientWithCustomCookies = HttpClient {
install(HttpCookies) {
storage = customStorage
}
}Provides HTTP response caching functionality.
/**
* HTTP cache plugin for response caching
*/
val HttpCache: ClientPlugin<HttpCache.Config>
class HttpCache {
/**
* Configuration for HTTP cache plugin
*/
class Config {
/** Whether the cache is shared between multiple clients */
var isShared: Boolean = false
/**
* Configure public cache storage
* @param storage Cache storage implementation
*/
fun publicStorage(storage: CacheStorage)
/**
* Configure private cache storage
* @param storage Cache storage implementation
*/
fun privateStorage(storage: CacheStorage)
}
}
/**
* Interface for cache storage implementations
*/
interface CacheStorage {
/**
* Find cached entry for URL and vary headers
* @param url Request URL
* @param vary Vary header values
* @return Cached entry or null if not found
*/
suspend fun find(url: Url, vary: Map<String, String>): HttpCacheEntry?
/**
* Find all cached entries for URL
* @param url Request URL
* @return Set of cached entries
*/
suspend fun findAll(url: Url): Set<HttpCacheEntry>
/**
* Store cache entry
* @param url Request URL
* @param data Cache entry to store
*/
suspend fun store(url: Url, data: HttpCacheEntry)
companion object {
/** Create unlimited cache storage */
fun Unlimited(): CacheStorage
/** Create disabled cache storage */
fun Disabled(): CacheStorage
}
}
/**
* HTTP cache entry
*/
data class HttpCacheEntry(
val url: Url,
val statusCode: HttpStatusCode,
val requestTime: GMTDate,
val responseTime: GMTDate,
val version: HttpProtocolVersion,
val expires: GMTDate,
val headers: Headers,
val body: ByteArray
)Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.cache.*
val client = HttpClient {
install(HttpCache) {
publicStorage(CacheStorage.Unlimited())
privateStorage(CacheStorage.Unlimited())
}
}Tracks upload and download progress for request and response bodies.
/**
* Body progress plugin for tracking upload/download progress
*/
val BodyProgress: ClientPlugin<BodyProgress.Config>
class BodyProgress {
/**
* Configuration for body progress plugin
*/
class Config {
/**
* Register a progress listener
* @param listener Progress listener to register
*/
fun register(listener: ProgressListener)
}
}
/**
* Interface for progress listeners
*/
fun interface ProgressListener {
/**
* Called when progress is made
* @param bytesSentTotal Total bytes sent so far
* @param contentLength Total content length, null if unknown
*/
fun onProgress(bytesSentTotal: Long, contentLength: Long?)
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
val client = HttpClient {
install(BodyProgress) {
register { bytesSentTotal, contentLength ->
val progress = if (contentLength != null) {
(bytesSentTotal * 100 / contentLength).toInt()
} else {
-1
}
println("Upload progress: $bytesSentTotal bytes ($progress%)")
}
}
}
// Progress will be tracked for this upload
val response = client.post("https://api.example.com/upload") {
setBody(largeFileContent)
}Provides Server-Sent Events support.
/**
* Server-Sent Events plugin
*/
val SSE: ClientPlugin<SSEConfig>
/**
* Configuration for SSE plugin
*/
class SSEConfig {
/** Reconnection time for failed connections */
var reconnectionTime: Duration? = null
/** Whether to show comment events */
var showCommentEvents: Boolean? = null
/** Whether to show retry events */
var showRetryEvents: Boolean? = null
/** Maximum reconnection attempts */
var maxReconnectionAttempts: Int = Int.MAX_VALUE
}
/**
* SSE session interface
*/
interface ClientSSESession {
/** Flow of incoming server-sent events */
val incoming: Flow<ServerSentEvent>
}
/**
* SSE session with deserialization support
*/
interface ClientSSESessionWithDeserialization {
/** Flow of incoming typed server-sent events */
val incoming: Flow<TypedServerSentEvent<String>>
/**
* Deserialize event data
* @param data Event data string
* @return Deserialized object of type T
*/
suspend inline fun <reified T> deserialize(data: String?): T?
}
/**
* Connect to Server-Sent Events endpoint
* @param host Server host
* @param port Server port
* @param path URL path
* @param request Request configuration
* @param block SSE session block
*/
suspend fun HttpClient.sse(
host: String = "localhost",
port: Int = DEFAULT_PORT,
path: String = "/",
request: HttpRequestBuilder.() -> Unit = {},
block: suspend ClientSSESession.() -> Unit
)
/**
* Connect to Server-Sent Events endpoint with deserialization
* @param request Request configuration
* @param deserialize Deserialization function
* @param block SSE session block
*/
suspend fun <T> HttpClient.sse(
request: HttpRequestBuilder.() -> Unit,
deserialize: (TypeInfo, String) -> Any?,
block: suspend ClientSSESessionWithDeserialization.() -> Unit
)Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.sse.*
import kotlinx.coroutines.flow.collect
val client = HttpClient {
install(SSE) {
reconnectionTime = 3.seconds
showCommentEvents = true
}
}
// Connect to SSE endpoint
client.sse(
host = "api.example.com",
path = "/events"
) {
incoming.collect { event ->
println("Received: ${event.data}")
}
}Provides WebSocket client support.
/**
* WebSockets plugin
*/
val WebSockets: ClientPlugin<WebSocketConfig>
/**
* Connect to WebSocket endpoint
* @param method HTTP method for handshake
* @param host Server host
* @param port Server port
* @param path URL path
* @param request Request configuration
* @param block WebSocket session block
*/
suspend fun HttpClient.webSocket(
method: HttpMethod = HttpMethod.Get,
host: String = "localhost",
port: Int = DEFAULT_PORT,
path: String = "/",
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
)
/**
* Connect to WebSocket endpoint with URL string
* @param urlString WebSocket URL
* @param request Request configuration
* @param block WebSocket session block
*/
suspend fun HttpClient.webSocket(
urlString: String,
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
)
/**
* Connect to secure WebSocket endpoint
*/
suspend fun HttpClient.wss(
host: String = "localhost",
port: Int = DEFAULT_PORT,
path: String = "/",
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
)Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.websocket.*
import io.ktor.websocket.*
val client = HttpClient {
install(WebSockets)
}
// Connect to WebSocket
client.webSocket(
host = "echo.websocket.org",
port = 80,
path = "/"
) {
// Send message
send("Hello WebSocket!")
// Receive messages
for (frame in incoming) {
when (frame) {
is Frame.Text -> {
println("Received: ${frame.readText()}")
}
is Frame.Binary -> {
println("Received binary data")
}
is Frame.Close -> {
println("Connection closed")
break
}
}
}
}Plugin system related types:
/**
* Attribute key for identifying plugins
*/
data class AttributeKey<T>(val name: String)
/**
* Exception for SSE operations
*/
class SSEClientException(
response: HttpResponse?,
cause: Throwable?,
message: String?
) : IllegalStateException()
/**
* Server-sent event data
*/
data class ServerSentEvent(
val data: String?,
val event: String?,
val id: String?,
val retry: Long?,
val comments: String?
)
/**
* Typed server-sent event
*/
data class TypedServerSentEvent<T>(
val data: T?,
val event: String?,
val id: String?,
val retry: Long?
)
/**
* Cookie data class
*/
data class Cookie(
val name: String,
val value: String,
val encoding: CookieEncoding = CookieEncoding.URI_ENCODING,
val maxAge: Int = 0,
val expires: GMTDate? = null,
val domain: String? = null,
val path: String? = null,
val secure: Boolean = false,
val httpOnly: Boolean = false,
val extensions: Map<String, String?> = emptyMap()
)Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor