Ktor HTTP client core library providing asynchronous HTTP client functionality for multiplatform applications with macOS ARM64 support.
—
Comprehensive set of built-in plugins providing essential HTTP client features including authentication, cookies, redirects, timeouts, content negotiation, and WebSocket support.
Core plugin for managing request lifecycle events.
/**
* Plugin for managing HTTP request lifecycle events
*/
object HttpRequestLifecycle : HttpClientPlugin<Unit, Unit>Plugin for tracking upload and download progress.
/**
* Plugin for tracking request/response body progress
*/
object BodyProgress : HttpClientPlugin<Unit, Unit>Plugin for saving response bodies for multiple access.
/**
* Plugin for saving response bodies to enable multiple reads
*/
object SaveBody : HttpClientPlugin<Unit, Unit>Comprehensive authentication plugin supporting multiple authentication schemes.
/**
* Authentication plugin supporting multiple auth providers
*/
object Auth : HttpClientPlugin<AuthConfig, Auth> {
/** Install bearer token authentication */
fun AuthConfig.bearer(block: BearerAuthConfig.() -> Unit)
/** Install basic authentication */
fun AuthConfig.basic(block: BasicAuthConfig.() -> Unit)
/** Install digest authentication */
fun AuthConfig.digest(block: DigestAuthConfig.() -> Unit)
/** Install OAuth authentication */
fun AuthConfig.oauth(block: OAuthConfig.() -> Unit)
}
/**
* Bearer token authentication configuration
*/
class BearerAuthConfig {
/** Load tokens for authentication */
fun loadTokens(block: suspend () -> BearerTokens?)
/** Refresh tokens when expired */
fun refreshTokens(block: suspend (BearerTokens) -> BearerTokens?)
/** Send credentials without waiting for 401 */
var sendWithoutRequest: Boolean
/** Realm for authentication */
var realm: String?
}
/**
* Bearer tokens for OAuth-style authentication
*/
data class BearerTokens(
val accessToken: String,
val refreshToken: String?
)
/**
* Basic authentication configuration
*/
class BasicAuthConfig {
/** Username for authentication */
var username: String
/** Password for authentication */
var password: String
/** Send credentials without waiting for 401 */
var sendWithoutRequest: Boolean
/** Realm for authentication */
var realm: String?
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.auth.*
import io.ktor.client.plugins.auth.providers.*
// Bearer token authentication
val bearerClient = HttpClient {
install(Auth) {
bearer {
loadTokens {
// Load tokens from storage
BearerTokens(
accessToken = "your_access_token",
refreshToken = "your_refresh_token"
)
}
refreshTokens { tokens ->
// Refresh expired tokens
val response = client.post("https://auth.example.com/refresh") {
setBody("""{"refresh_token": "${tokens.refreshToken}"}""")
}
val newTokens = response.body<TokenResponse>()
BearerTokens(newTokens.accessToken, newTokens.refreshToken)
}
sendWithoutRequest = false
realm = "api"
}
}
}
// Basic authentication
val basicClient = HttpClient {
install(Auth) {
basic {
username = "user"
password = "password"
sendWithoutRequest = true
realm = null
}
}
}
// Multiple auth providers
val multiAuthClient = HttpClient {
install(Auth) {
basic {
username = "api_user"
password = "api_password"
realm = "api"
}
bearer {
loadTokens { bearerTokens }
realm = "oauth"
}
}
}Automatic cookie handling with customizable storage.
/**
* HTTP cookies plugin for automatic cookie management
*/
object HttpCookies : HttpClientPlugin<HttpCookiesConfig, HttpCookies>
/**
* Cookie configuration
*/
class HttpCookiesConfig {
/** Cookie storage implementation */
var storage: CookiesStorage = AcceptAllCookiesStorage()
}
/**
* Cookie storage interface
*/
interface CookiesStorage {
/** Get cookies for specific URL */
suspend fun get(requestUrl: Url): List<Cookie>
/** Add cookie received from response */
suspend fun addCookie(requestUrl: Url, cookie: Cookie)
/** Close storage and cleanup resources */
fun close()
}
/**
* Cookie storage that accepts all cookies
*/
class AcceptAllCookiesStorage : CookiesStorage
/**
* Cookie representation
*/
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()
)
/** Get cookies for specific URL */
suspend fun HttpClient.cookies(url: String): List<Cookie>Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.cookies.*
// Basic cookie handling
val cookieClient = HttpClient {
install(HttpCookies) {
storage = AcceptAllCookiesStorage()
}
}
// Custom cookie storage
class CustomCookieStorage : CookiesStorage {
private val cookies = mutableMapOf<String, MutableList<Cookie>>()
override suspend fun get(requestUrl: Url): List<Cookie> {
return cookies[requestUrl.host] ?: emptyList()
}
override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
cookies.getOrPut(requestUrl.host) { mutableListOf() }.add(cookie)
}
override fun close() {
cookies.clear()
}
}
val customCookieClient = HttpClient {
install(HttpCookies) {
storage = CustomCookieStorage()
}
}
// Access cookies
val cookies = cookieClient.cookies("https://example.com")
cookies.forEach { cookie ->
println("Cookie: ${cookie.name}=${cookie.value}")
}Automatic handling of HTTP redirects.
/**
* Plugin for automatic HTTP redirect handling
*/
object HttpRedirect : HttpClientPlugin<Unit, Unit>Response validation and error handling.
/**
* Plugin for HTTP call validation and error handling
*/
object HttpCallValidator : HttpClientPlugin<Unit, Unit>Plain text content transformation.
/**
* Plugin for plain text content transformation
*/
object HttpPlainText : HttpClientPlugin<Unit, Unit>Sets default request parameters for all requests including base URL and common headers.
/**
* Plugin for setting default request parameters including base URL and headers
*/
object DefaultRequest : HttpClientPlugin<DefaultRequestConfig, Unit>
/**
* Default request configuration
*/
class DefaultRequestConfig {
/** Configure default URL */
fun url(block: URLBuilder.() -> Unit)
/** Configure default headers */
fun headers(block: HeadersBuilder.() -> Unit)
/** Set default header value */
fun header(key: String, value: String)
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.*
// Set default base URL and headers
val client = HttpClient {
install(DefaultRequest) {
url {
protocol = URLProtocol.HTTPS
host = "api.example.com"
path("v1/")
}
headers {
append("User-Agent", "MyApp/1.0")
append("Accept", "application/json")
}
}
}
// All requests will use the default configuration
val response = client.get("users/123") // Requests https://api.example.com/v1/users/123Data conversion utilities for request/response parameters.
/**
* Plugin for data conversion utilities
*/
object DataConversion : HttpClientPlugin<DataConversion.Configuration, DataConversion>Configurable timeouts for HTTP requests.
/**
* Plugin for configuring HTTP request timeouts
*/
object HttpTimeout : HttpClientPlugin<HttpTimeoutCapabilityConfiguration, Unit>
/**
* Timeout configuration
*/
class HttpTimeoutCapabilityConfiguration {
/** Request timeout in milliseconds */
var requestTimeoutMillis: Long? = null
/** Connection timeout in milliseconds */
var connectTimeoutMillis: Long? = null
/** Socket timeout in milliseconds */
var socketTimeoutMillis: Long? = null
}
/**
* Request timeout exception
*/
class HttpRequestTimeoutException(
val url: String,
val timeoutMillis: Long
) : IOException("Request timeout has expired [url=$url, timeout_ms=$timeoutMillis]")Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.timeout.*
// Global timeout configuration
val timeoutClient = HttpClient {
install(HttpTimeout) {
requestTimeoutMillis = 30000 // 30 seconds
connectTimeoutMillis = 10000 // 10 seconds
socketTimeoutMillis = 20000 // 20 seconds
}
}
// Per-request timeout
val response = timeoutClient.get ("https://slow-api.example.com") {
timeout {
requestTimeoutMillis = 60000 // 1 minute for this request
}
}HTTP response caching with cache control support.
/**
* Plugin for HTTP response caching
*/
object HttpCache : HttpClientPlugin<Unit, Unit>WebSocket connection support for real-time communication.
/**
* Plugin for WebSocket support
*/
object WebSockets : HttpClientPlugin<WebSocketConfig, Unit>
/**
* WebSocket configuration
*/
class WebSocketConfig {
/** Maximum frame size in bytes */
var maxFrameSize: Long = Long.MAX_VALUE
/** Masking for outgoing frames */
var masking: Boolean = true
/** Extensions for WebSocket protocol */
val extensions: MutableList<WebSocketExtension<*>> = mutableListOf()
}
/**
* WebSocket session interface
*/
interface ClientWebSocketSession : WebSocketSession {
/** Send text frame */
suspend fun send(data: String)
/** Send binary frame */
suspend fun send(data: ByteArray)
/** Receive incoming frames */
val incoming: ReceiveChannel<Frame>
/** Outgoing frames channel */
val outgoing: SendChannel<Frame>
/** Close WebSocket connection */
suspend fun close(reason: CloseReason = CloseReason(CloseReason.Codes.NORMAL, ""))
}
/** WebSocket connection function */
suspend fun HttpClient.webSocket(
host: String = "localhost",
port: Int = DEFAULT_PORT,
path: String = "/",
block: suspend ClientWebSocketSession.() -> Unit
)
suspend fun HttpClient.ws(
urlString: String,
block: suspend ClientWebSocketSession.() -> Unit
)Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.websocket.*
import io.ktor.websocket.*
// Install WebSocket plugin
val wsClient = HttpClient {
install(WebSockets) {
maxFrameSize = 1024 * 1024 // 1MB frames
masking = true
}
}
// WebSocket connection
wsClient.webSocket("ws://localhost:8080/websocket") {
// Send message
send("Hello WebSocket!")
// Receive messages
for (frame in incoming) {
when (frame) {
is Frame.Text -> {
val message = frame.readText()
println("Received: $message")
}
is Frame.Binary -> {
val data = frame.readBytes()
println("Received binary: ${data.size} bytes")
}
is Frame.Close -> {
println("WebSocket closed: ${frame.readReason()}")
break
}
}
}
}
// WebSocket with URL string
wsClient.ws("wss://echo.websocket.org") {
send("Echo test message")
val response = incoming.receive()
if (response is Frame.Text) {
println("Echo response: ${response.readText()}")
}
}Server-Sent Events support for streaming data.
/**
* Plugin for Server-Sent Events (SSE) support
*/
object SSE : HttpClientPlugin<Unit, Unit>
/**
* Server-Sent Event representation
*/
data class ServerSentEvent(
val data: String? = null,
val event: String? = null,
val id: String? = null,
val retry: Long? = null,
val comments: String? = null
)
/** SSE connection function */
suspend fun HttpClient.sse(
host: String = "localhost",
port: Int = DEFAULT_PORT,
path: String = "/",
block: suspend SSESession.() -> Unit
)
/**
* SSE session interface
*/
interface SSESession {
/** Incoming server-sent events */
val incoming: ReceiveChannel<ServerSentEvent>
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.sse.*
// Install SSE plugin
val sseClient = HttpClient {
install(SSE)
}
// SSE connection
sseClient.sse("localhost", 8080, "/events") {
for (event in incoming) {
when (event.event) {
"message" -> println("Message: ${event.data}")
"update" -> println("Update: ${event.data}")
"error" -> println("Error: ${event.data}")
else -> println("Event: ${event.event}, Data: ${event.data}")
}
}
}Comprehensive request/response logging with configurable levels.
/**
* Plugin for HTTP request/response logging
*/
object Logging : HttpClientPlugin<LoggingConfig, Unit>
/**
* Logging configuration
*/
class LoggingConfig {
/** Logger implementation */
var logger: Logger = Logger.DEFAULT
/** Logging level */
var level: LogLevel = LogLevel.HEADERS
/** Log request body */
var logRequestBody: Boolean = false
/** Log response body */
var logResponseBody: Boolean = false
/** Sanitize sensitive headers */
var sanitizeHeader: (String) -> String = { it }
}
/**
* Logging levels
*/
enum class LogLevel {
ALL, HEADERS, BODY, INFO, NONE
}
/**
* Logger interface
*/
interface Logger {
fun log(message: String)
companion object {
val DEFAULT: Logger = object : Logger {
override fun log(message: String) {
println(message)
}
}
val SIMPLE: Logger = DEFAULT
val EMPTY: Logger = object : Logger {
override fun log(message: String) {}
}
}
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.plugins.logging.*
// Basic logging
val loggingClient = HttpClient {
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.HEADERS
}
}
// Detailed logging
val detailedClient = HttpClient {
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
logRequestBody = true
logResponseBody = true
sanitizeHeader { header ->
when {
header.lowercase().contains("authorization") -> "***"
header.lowercase().contains("cookie") -> "***"
else -> header
}
}
}
}
// Custom logger
val customLogger = object : Logger {
override fun log(message: String) {
// Custom logging implementation
writeToLogFile(message)
}
}
val customLoggingClient = HttpClient {
install(Logging) {
logger = customLogger
level = LogLevel.INFO
}
}data class PluginInstallationConfig<TConfig : Any>(
val plugin: HttpClientPlugin<TConfig, *>,
val config: TConfig
)
interface AuthProvider {
val sendWithoutRequest: Boolean
suspend fun addRequestHeaders(request: HttpRequestBuilder)
suspend fun refreshToken(response: HttpResponse): Boolean
}
data class CloseReason(val code: Short, val message: String) {
companion object Codes {
const val NORMAL: Short = 1000
const val GOING_AWAY: Short = 1001
const val PROTOCOL_ERROR: Short = 1002
const val UNSUPPORTED_DATA: Short = 1003
const val NO_STATUS: Short = 1005
const val ABNORMAL_CLOSURE: Short = 1006
const val INVALID_DATA: Short = 1007
const val POLICY_VIOLATION: Short = 1008
const val TOO_BIG: Short = 1009
const val MANDATORY_EXTENSION: Short = 1010
const val INTERNAL_ERROR: Short = 1011
const val SERVICE_RESTART: Short = 1012
const val TRY_AGAIN_LATER: Short = 1013
const val TLS_HANDSHAKE_FAILED: Short = 1015
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-core-macosarm64