Ktor HTTP client core library - asynchronous framework for creating HTTP clients in Kotlin multiplatform
npx @tessl/cli install tessl/maven-io-ktor--ktor-client-core-iosx64@2.3.0Ktor HTTP Client Core is a multiplatform asynchronous HTTP client library for Kotlin that enables developers to make HTTP requests and handle responses across JVM, JavaScript, and Native platforms. The library features a plugin-based architecture, type-safe request building through DSL syntax, full coroutine support, and extensive configuration options for timeouts, redirects, cookies, caching, and WebSocket connections.
dependencies {
implementation("io.ktor:ktor-client-core:2.3.13")
// Also add a specific engine implementation
implementation("io.ktor:ktor-client-cio:2.3.13") // For JVM
}import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.client.engine.cio.*import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.client.engine.cio.*
// Create HTTP client
val client = HttpClient(CIO) {
// Optional configuration
}
// Make GET request
val response: HttpResponse = client.get("https://api.example.com/users")
val content: String = response.bodyAsText()
// Make POST request with JSON
val postResponse = client.post("https://api.example.com/users") {
contentType(ContentType.Application.Json)
setBody("""{"name": "John", "email": "john@example.com"}""")
}
// Clean up
client.close()Ktor HTTP Client Core is built around several key components:
Core functionality for creating and configuring HTTP clients with engine selection and plugin installation.
// Primary client factory function (platform-specific)
expect fun HttpClient(
block: HttpClientConfig<*>.() -> Unit = {}
): HttpClient
// Client factory with engine
fun <T : HttpClientEngineConfig> HttpClient(
engineFactory: HttpClientEngineFactory<T>,
block: HttpClientConfig<T>.() -> Unit = {}
): HttpClient
// Client with existing engine
fun HttpClient(
engine: HttpClientEngine,
block: HttpClientConfig<*>.() -> Unit
): HttpClient
class HttpClient(
val engine: HttpClientEngine,
private val userConfig: HttpClientConfig<out HttpClientEngineConfig> = HttpClientConfig()
) : CoroutineScope, Closeable {
val requestPipeline: HttpRequestPipeline
val responsePipeline: HttpResponsePipeline
val sendPipeline: HttpSendPipeline
val receivePipeline: HttpReceivePipeline
val attributes: Attributes
val engineConfig: HttpClientEngineConfig
val monitor: Events
fun isSupported(capability: HttpClientEngineCapability<*>): Boolean
fun config(block: HttpClientConfig<*>.() -> Unit): HttpClient
override fun close()
}Type-safe DSL for building and executing HTTP requests with all standard HTTP methods.
// Generic request functions
suspend fun HttpClient.request(
builder: HttpRequestBuilder = HttpRequestBuilder()
): HttpResponse
suspend fun HttpClient.request(
block: HttpRequestBuilder.() -> Unit
): HttpResponse
suspend fun HttpClient.request(
urlString: String,
block: HttpRequestBuilder.() -> Unit = {}
): HttpResponse
// HTTP method convenience functions
suspend fun HttpClient.get(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse
suspend fun HttpClient.post(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse
suspend fun HttpClient.put(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse
suspend fun HttpClient.delete(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse
suspend fun HttpClient.patch(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse
suspend fun HttpClient.head(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse
suspend fun HttpClient.options(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse
class HttpRequestBuilder {
var url: URLBuilder
var method: HttpMethod
var headers: HeadersBuilder
var body: Any
var attributes: Attributes
fun url(block: URLBuilder.() -> Unit)
fun build(): HttpRequestData
fun takeFrom(builder: HttpRequestBuilder): HttpRequestBuilder
}
// Progress tracking extensions
typealias ProgressListener = suspend (bytesSentTotal: Long, contentLength: Long) -> Unit
fun HttpRequestBuilder.onUpload(listener: ProgressListener?)
fun HttpRequestBuilder.onDownload(listener: ProgressListener?)
// Timeout configuration extension
fun HttpRequestBuilder.timeout(block: HttpTimeout.HttpTimeoutCapabilityConfiguration.() -> Unit)
// Retry configuration extension
fun HttpRequestBuilder.retry(block: HttpRequestRetry.Configuration.() -> Unit)Comprehensive response handling with typed body access and streaming capabilities.
// Response classes
abstract class HttpResponse : HttpMessage, CoroutineScope {
abstract val call: HttpClientCall
abstract val status: HttpStatusCode
abstract val version: HttpProtocolVersion
abstract val requestTime: GMTDate
abstract val responseTime: GMTDate
abstract val content: ByteReadChannel
}
// Response body access
suspend fun HttpResponse.bodyAsText(fallbackCharset: Charset = Charsets.UTF_8): String
suspend fun HttpResponse.bodyAsChannel(): ByteReadChannel
suspend inline fun <reified T> HttpResponse.body(): T
// Prepared statements
class HttpStatement(
private val builder: HttpRequestBuilder,
private val client: HttpClient
) {
suspend fun <T> execute(block: suspend (response: HttpResponse) -> T): T
suspend fun execute(): HttpResponse
suspend inline fun <reified T> body(): T
}
// Client call representation
class HttpClientCall(
val client: HttpClient
) : CoroutineScope {
val attributes: Attributes
val request: HttpRequest
val response: HttpResponse
suspend inline fun <reified T> body(): T
suspend fun bodyNullable(info: TypeInfo): Any?
}Extensible plugin architecture for adding cross-cutting concerns like authentication, caching, and logging.
interface HttpClientPlugin<out TConfig : Any, TPlugin : Any> {
val key: AttributeKey<TPlugin>
fun prepare(block: TConfig.() -> Unit = {}): TPlugin
fun install(plugin: TPlugin, scope: HttpClient)
}
// Plugin access
fun <B : Any, F : Any> HttpClient.pluginOrNull(plugin: HttpClientPlugin<B, F>): F?
fun <B : Any, F : Any> HttpClient.plugin(plugin: HttpClientPlugin<B, F>): F
// Configuration in client
class HttpClientConfig<T : HttpClientEngineConfig> {
fun <TBuilder : Any, TPlugin : Any> install(
plugin: HttpClientPlugin<TBuilder, TPlugin>,
configure: TBuilder.() -> Unit = {}
)
fun install(key: String, block: HttpClient.() -> Unit)
}Platform-specific engine abstraction providing the underlying HTTP implementation.
interface HttpClientEngine : CoroutineScope, Closeable {
val dispatcher: CoroutineDispatcher
val config: HttpClientEngineConfig
val supportedCapabilities: Set<HttpClientEngineCapability<*>>
}
interface HttpClientEngineFactory<out T : HttpClientEngineConfig> {
fun create(block: T.() -> Unit = {}): HttpClientEngine
}
open class HttpClientEngineConfig {
var threadsCount: Int = 4 // Deprecated
var pipelining: Boolean = false
var proxy: ProxyConfig? = null
}Full-duplex WebSocket communication with extensions support and content conversion.
object WebSockets : HttpClientPlugin<WebSockets.Config, WebSockets> {
class Config {
var pingInterval: Long = -1L
var maxFrameSize: Long = Long.MAX_VALUE
var contentConverter: WebsocketContentConverter? = null
fun extensions(block: WebSocketExtensionsConfig.() -> Unit)
}
}
// WebSocket connection functions
suspend fun HttpClient.webSocketSession(
block: HttpRequestBuilder.() -> Unit
): DefaultClientWebSocketSession
suspend fun HttpClient.webSocket(
urlString: String,
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
)
suspend fun HttpClient.ws(
urlString: String,
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
)Support for URL-encoded forms and multipart form data with file uploads.
// Form content classes
class FormDataContent(val formData: Parameters) : OutgoingContent.ByteArrayContent() {
override val contentLength: Long
override val contentType: ContentType
override fun bytes(): ByteArray
}
class MultiPartFormDataContent(
parts: List<PartData>,
boundary: String,
contentType: ContentType = ContentType.MultiPart.FormData.withParameter("boundary", boundary)
) : OutgoingContent.WriteChannelContent() {
val boundary: String
override val contentType: ContentType
override val contentLength: Long?
}
// Form submission functions
suspend fun HttpClient.submitForm(
formParameters: Parameters,
encodeInQuery: Boolean = false,
block: HttpRequestBuilder.() -> Unit = {}
): HttpResponse
suspend fun HttpClient.submitFormWithBinaryData(
formData: List<PartData>,
block: HttpRequestBuilder.() -> Unit = {}
): HttpResponseCookie storage and management with configurable storage backends.
class HttpCookies private constructor(
private val storage: CookiesStorage
) : Closeable {
class Config {
var storage: CookiesStorage = AcceptAllCookiesStorage()
fun default(block: suspend CookiesStorage.() -> Unit)
}
companion object : HttpClientPlugin<Config, HttpCookies> {
override val key: AttributeKey<HttpCookies> = AttributeKey("HttpCookies")
}
suspend fun get(requestUrl: Url): List<Cookie>
}
interface CookiesStorage : Closeable {
suspend fun get(requestUrl: Url): List<Cookie>
suspend fun addCookie(requestUrl: Url, cookie: Cookie)
}
class AcceptAllCookiesStorage : CookiesStorage
class ConstantCookiesStorage(cookies: List<Cookie>) : CookiesStorageResponse caching with configurable storage backends and cache control support.
class HttpCache internal constructor(
private val publicStorage: CacheStorage,
private val privateStorage: CacheStorage,
private val isSharedClient: Boolean
) {
class Config {
var isShared: Boolean = false
fun publicStorage(storage: CacheStorage)
fun privateStorage(storage: CacheStorage)
}
companion object : HttpClientPlugin<Config, HttpCache> {
override val key: AttributeKey<HttpCache> = AttributeKey("HttpCache")
val HttpResponseFromCache: EventDefinition<HttpResponse>
}
}
interface CacheStorage {
suspend fun store(url: Url, data: CachedResponseData)
suspend fun find(url: Url, varyKeys: Map<String, String>): CachedResponseData?
suspend fun findAll(url: Url): Set<CachedResponseData>
companion object {
fun Unlimited(): CacheStorage
val Disabled: CacheStorage
}
}
data class CachedResponseData(
val url: Url,
val statusCode: HttpStatusCode,
val requestTime: GMTDate,
val responseTime: GMTDate,
val version: HttpProtocolVersion,
val expires: GMTDate,
val headers: Headers,
val varyKeys: Map<String, String>,
val body: ByteArray
)// Request types
interface HttpRequest : HttpMessage {
val call: HttpClientCall
val method: HttpMethod
val url: Url
val attributes: Attributes
val content: OutgoingContent
}
data class HttpRequestData(
val url: Url,
val method: HttpMethod,
val headers: Headers,
val body: OutgoingContent,
val executionContext: Job,
val attributes: Attributes
)
data class HttpResponseData(
val statusCode: HttpStatusCode,
val requestTime: GMTDate,
val headers: Headers,
val version: HttpProtocolVersion,
val body: ByteReadChannel,
val callContext: CoroutineContext
)
// Pipeline types
class HttpRequestPipeline(developmentMode: Boolean = false)
class HttpResponsePipeline(developmentMode: Boolean = false)
class HttpSendPipeline(developmentMode: Boolean = false)
class HttpReceivePipeline(developmentMode: Boolean = false)
// Engine capability
interface HttpClientEngineCapability<T>
// Proxy configuration
expect class ProxyConfig
enum class ProxyType {
SOCKS, HTTP, UNKNOWN
}
expect object ProxyBuilder {
fun http(url: Url): ProxyConfig
fun http(urlString: String): ProxyConfig
fun socks(host: String, port: Int): ProxyConfig
}// Request/Response exceptions
class DoubleReceiveException(message: String) : IllegalStateException(message)
class ReceivePipelineException(message: String, cause: Throwable) : IllegalStateException(message, cause)
class NoTransformationFoundException(from: KType, to: KType) : UnsupportedOperationException()
// Timeout exceptions
class HttpRequestTimeoutException(url: String, timeoutMillis: Long?) : IOException()
fun ConnectTimeoutException(request: HttpRequestData, cause: Throwable? = null): ConnectTimeoutException
fun ConnectTimeoutException(url: String, timeout: Long?, cause: Throwable? = null): ConnectTimeoutException
fun SocketTimeoutException(request: HttpRequestData, cause: Throwable? = null): SocketTimeoutException
// Plugin validation exceptions
class ResponseException(response: HttpResponse, cachedResponseText: String) : IllegalStateException()
class RedirectResponseException(response: HttpResponse, message: String) : ResponseException(response, message)
class ClientRequestException(response: HttpResponse, message: String) : ResponseException(response, message)
class ServerResponseException(response: HttpResponse, message: String) : ResponseException(response, message)
// Retry exceptions
class SendCountExceedException(message: String) : IllegalStateException(message)
// WebSocket exceptions
open class WebSocketException(message: String) : IllegalStateException(message)
// Cache exceptions
class InvalidCacheStateException(message: String) : IllegalStateException(message)val HttpRequestCreated: EventDefinition<HttpRequestBuilder>
val HttpRequestIsReadyForSending: EventDefinition<HttpRequestBuilder>
val HttpResponseReceived: EventDefinition<HttpResponse>
val HttpResponseReceiveFailed: EventDefinition<HttpResponseReceiveFail>
val HttpResponseCancelled: EventDefinition<HttpResponse>
val HttpRequestRetryEvent: EventDefinition<HttpRequestRetry.RetryEventData>
data class HttpResponseReceiveFail(
val response: HttpResponse,
val cause: Throwable
)