Client engines
The Ktor HTTP client is multiplatform and runs on JVM, Android, JavaScript (including WebAssembly), and Native targets. Each platform requires a specific engine to process network requests. For example, you can use Apache
or Jetty
for JVM applications, OkHttp
or Android
for Android, Curl
for desktop applications targeting Kotlin/Native. Every engine differs slightly in features and configuration, so you can choose the one that best meets your platform and use-case needs.
Supported platforms
The table below lists the platforms supported by each engine:
Supported Android/Java versions
Client engines targeting JVM or both JVM and Android support the following Android/Java versions:
Engine | Android version | Java version |
---|---|---|
| 8+ | |
| 11+ | |
| 11+ | |
| 7.0+ * | 8+ |
| 1.x+ | 8+ |
| 5.0+ | 8+ |
* To use the CIO engine on older Android versions, you need to enable Java 8 API desugaring.
Add an engine dependency
In addition to the ktor-client-core
artifact, the Ktor client requires a dependency for a specific engine. Each supported platform has a set of available engines, described in the corresponding sections:
Specify an engine
To use a specific engine, pass the engine class to the HttpClient
constructor. The following example creates a client with the CIO
engine:
Default engine
If you omit the engine argument, the client will choose an engine automatically based on the dependencies in your build script.
This is especially useful in multiplatform projects. For example, for a project targeting both Android and iOS, you can add the Android dependency to the androidMain
source set and the Darwin dependency to the iosMain
source set. The appropriate engine is selected at run time upon HttpClient
creation.
Configure an engine
To configure an engine, use the engine {}
function. All engines can be configured using the common options from HttpClientEngineConfig
:
In the next sections, you can learn how to configure specific engines for different platforms.
JVM
The JVM target supports the Apache5
, Java
, and Jetty
engines.
Apache5
The Apache5
engine supports both HTTP/1.1 and HTTP/2, with HTTP/2 enabled by default. This is the recommended Apache-based engine for new projects.
Add the
ktor-client-apache5
dependency:implementation("io.ktor:ktor-client-apache5:$ktor_version")implementation "io.ktor:ktor-client-apache5:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-apache5-jvm</artifactId> <version>${ktor_version}</version> </dependency>Pass the
Apache5
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.apache5.* val client = HttpClient(Apache5)Use the
engine {}
block to access and set properties fromApache5EngineConfig
:import io.ktor.client.* import io.ktor.client.engine.apache5.* import org.apache.hc.core5.http.* val client = HttpClient(Apache5) { engine { // this: Apache5EngineConfig followRedirects = true socketTimeout = 10_000 connectTimeout = 10_000 connectionRequestTimeout = 20_000 customizeClient { // this: HttpAsyncClientBuilder setProxy(HttpHost("127.0.0.1", 8080)) // ... } customizeRequest { // this: RequestConfig.Builder } } }
Java
The Java
engine uses the Java HTTP Client introduced in Java 11. To use it, follow the steps below:
Add the
ktor-client-java
dependency:implementation("io.ktor:ktor-client-java:$ktor_version")implementation "io.ktor:ktor-client-java:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-java-jvm</artifactId> <version>${ktor_version}</version> </dependency>Pass the Java class as an argument to the
HttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.java.* val client = HttpClient(Java)To configure the engine, set properties from
JavaHttpConfig
in theengine {}
block:import io.ktor.client.* import io.ktor.client.engine.* import io.ktor.client.engine.java.* val client = HttpClient(Java) { engine { // this: [[[JavaHttpConfig|https://api.ktor.io/ktor-client/ktor-client-java/io.ktor.client.engine.java/-java-http-config/index.html]]] threadsCount = 8 pipelining = true proxy = ProxyBuilder.http("http://proxy-server.com/") protocolVersion = java.net.http.HttpClient.Version.HTTP_2 } }
Jetty
The Jetty
engine supports only HTTP/2 and can be configured in the following way:
Add the
ktor-client-jetty-jakarta
dependency:implementation("io.ktor:ktor-client-jetty-jakarta:$ktor_version")implementation "io.ktor:ktor-client-jetty-jakarta:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-jetty-jakarta-jvm</artifactId> <version>${ktor_version}</version> </dependency>Pass the
Jetty
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.jetty.jakarta.* val client = HttpClient(Jetty)To configure the engine, set properties from
JettyEngineConfig
in theengine {}
block:import io.ktor.client.* import io.ktor.client.engine.jetty.jakarta.* import org.eclipse.jetty.util.ssl.SslContextFactory val client = HttpClient(Jetty) { engine { // this: [[[JettyEngineConfig|https://api.ktor.io/ktor-client/ktor-client-jetty-jakarta/io.ktor.client.engine.jetty.jakarta/-jetty-engine-config/index.html]]] sslContextFactory = SslContextFactory.Client() clientCacheSize = 12 } }
JVM and Android
In this section, we'll take a look at engines available for JVM/Android and their configurations.
Android
The Android
engine targets Android and can be configured in the following way:
Add the
ktor-client-android
dependency:implementation("io.ktor:ktor-client-android:$ktor_version")implementation "io.ktor:ktor-client-android:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-android-jvm</artifactId> <version>${ktor_version}</version> </dependency>Pass the
Android
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.android.* val client = HttpClient(Android)To configure an engine, set properties from
AndroidEngineConfig
in theengine {}
block:import io.ktor.client.* import io.ktor.client.engine.android.* import java.net.Proxy import java.net.InetSocketAddress val client = HttpClient(Android) { engine { // this: [[[AndroidEngineConfig|https://api.ktor.io/ktor-client/ktor-client-android/io.ktor.client.engine.android/-android-engine-config/index.html]]] connectTimeout = 100_000 socketTimeout = 100_000 proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("localhost", 8080)) } }
OkHttp
The OkHttp
engine is based on OkHttp and can be configured in the following way:
Add the
ktor-client-okhttp
dependency:implementation("io.ktor:ktor-client-okhttp:$ktor_version")implementation "io.ktor:ktor-client-okhttp:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-okhttp-jvm</artifactId> <version>${ktor_version}</version> </dependency>Pass the
OkHttp
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.okhttp.* val client = HttpClient(OkHttp)To configure an engine, set properties from
OkHttpConfig
in theengine {}
block:import io.ktor.client.* import io.ktor.client.engine.okhttp.* val client = HttpClient(OkHttp) { engine { // this: [[[OkHttpConfig|https://api.ktor.io/ktor-client/ktor-client-okhttp/io.ktor.client.engine.okhttp/-ok-http-config/index.html]]] config { // this: OkHttpClient.Builder followRedirects(true) // ... } addInterceptor(interceptor) addNetworkInterceptor(interceptor) preconfigured = okHttpClientInstance } }
Native
Ktor provides the Darwin
, WinHttp
, and Curl
engines for Kotlin/Native targets.
Darwin
The Darwin
engine targets Darwin-based operating systems, such as macOS, iOS, tvOS, and watchOS. It uses NSURLSession
under the hood. To use the Darwin
engine, follow the steps below:
Add the
ktor-client-darwin
dependency:implementation("io.ktor:ktor-client-darwin:$ktor_version")implementation "io.ktor:ktor-client-darwin:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-darwin-macosx64</artifactId> <version>${ktor_version}</version> </dependency>Pass the
Darwin
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.darwin.* val client = HttpClient(Darwin)Configure the engine in the
engine {}
block usingDarwinClientEngineConfig
. For example, you can customize requests withconfigureRequest
or sessions withconfigureSession
:val client = HttpClient(Darwin) { engine { configureRequest { setAllowsCellularAccess(true) } } }For the full example, see client-engine-darwin.
WinHttp
The WinHttp
engine targets Windows-based operating systems. To use the WinHttp
engine, follow the steps below:
Add the
ktor-client-winhttp
dependency:implementation("io.ktor:ktor-client-winhttp:$ktor_version")implementation "io.ktor:ktor-client-winhttp:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-winhttp-mingwx64</artifactId> <version>${ktor_version}</version> </dependency>Pass the
WinHttp
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.winhttp.* val client = HttpClient(WinHttp)Configure the engine in the
engine {}
block usingWinHttpClientEngineConfig
. For example, you can use theprotocolVersion
property to change the HTTP version:val client = HttpClient(WinHttp) { engine { protocolVersion = HttpProtocolVersion.HTTP_1_1 } }For the full example, see client-engine-winhttp.
Curl
For desktop platforms, Ktor provides the Curl
engine. It is supported on linuxX64
, linuxArm64
, macosX64
, macosArm64
, and mingwX64
. To use the Curl
engine, follow the steps below:
Add the
ktor-client-curl
dependency:implementation("io.ktor:ktor-client-curl:$ktor_version")implementation "io.ktor:ktor-client-curl:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-curl-macosx64</artifactId> <version>${ktor_version}</version> </dependency>Pass the
Curl
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.curl.* val client = HttpClient(Curl)Configure the engine in the
engine {}
block usingCurlClientEngineConfig
. For example, disable SSL verification for testing purposes:val client = HttpClient(Curl) { engine { sslVerify = false } }For the full example, see client-engine-curl.
JVM, Android, Native, JS and WasmJs
CIO
The CIO engine is a fully asynchronous coroutine-based engine available on JVM, Android, Native, JavaScript, and WebAssembly JavaScript (WasmJs) platforms. It currently supports HTTP/1.x only. To use it, follow the steps below:
Add the
ktor-client-cio
dependency:implementation("io.ktor:ktor-client-cio:$ktor_version")implementation "io.ktor:ktor-client-cio:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-cio-jvm</artifactId> <version>${ktor_version}</version> </dependency>Pass the
CIO
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.cio.* val client = HttpClient(CIO)Configure the engine in the
engine {}
block usingCIOEngineConfig
:import io.ktor.client.* import io.ktor.client.engine.cio.* import io.ktor.network.tls.* val client = HttpClient(CIO) { engine { // this: [[[CIOEngineConfig|https://api.ktor.io/ktor-client/ktor-client-cio/io.ktor.client.engine.cio/-c-i-o-engine-config/index.html]]] maxConnectionsCount = 1000 endpoint { // this: [[[EndpointConfig|https://api.ktor.io/ktor-client/ktor-client-cio/io.ktor.client.engine.cio/-endpoint-config/index.html]]] maxConnectionsPerRoute = 100 pipelineMaxSize = 20 keepAliveTime = 5000 connectTimeout = 5000 connectAttempts = 5 } https { // this: [[[TLSConfigBuilder|https://api.ktor.io/ktor-network/ktor-network-tls/io.ktor.network.tls/-t-l-s-config-builder/index.html]]] serverName = "api.ktor.io" cipherSuites = CIOCipherSuites.SupportedSuites trustManager = myCustomTrustManager random = mySecureRandom addKeyStore(myKeyStore, myKeyStorePassword) } } }
JavaScript
The Js
engine can be used for JavaScript projects. It uses fetch API for browser applications and node-fetch
for Node.js. To use it, follow the steps below:
Add the
ktor-client-js
dependency:implementation("io.ktor:ktor-client-js:$ktor_version")implementation "io.ktor:ktor-client-js:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-js</artifactId> <version>${ktor_version}</version> </dependency>Pass the
Js
class as an argument to theHttpClient
constructor:import io.ktor.client.* import io.ktor.client.engine.js.* val client = HttpClient(Js)You can also call the
JsClient()
function to get theJs
engine singleton:import io.ktor.client.engine.js.* val client = JsClient()
For the full example, see client-engine-js.
Limitations
HTTP/2 and WebSockets
Not all engines support the HTTP/2 protocol. If an engine supports HTTP/2, you can enable it in the engine's configuration. For example, with the Java engine.
The table below shows whether a specific engine supports HTTP/2 and WebSockets:
Engine | HTTP/2 | WebSockets |
---|---|---|
| ✅️ | ✖️ |
| ✅ | ✅️ |
| ✅ | ✖️ |
| ✖️ | ✅ |
| ✖️ | ✖️ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
Security
SSL must be configured per engine. Each engine provides its own SSL configuration options.
Proxy support
Some engines don't support proxies. For the complete list, see the proxy documentation.
Logging
The Logging plugin provides different logger types depending on the target platforms.
Timeout
The HttpTimeout plugin has some limitations on certain engines. For the complete list, see Timeout limitations.
Example: How to configure an engine in a multiplatform mobile project
When building a multiplatform project, you can use expected and actual declarations to select and configure engines for each target platform. This allows you to share most client configuration in common code while applying engine-specific options in platform code. We'll demonstrate how to achieve this using a project created in the Creating a cross-platform mobile application tutorial:
Open the shared/src/commonMain/kotlin/com/example/kmmktor/Platform.kt file and add a top-level
httpClient()
function that accepts a configuration block and returns anHttpClient
:expect fun httpClient(config: HttpClientConfig<*>.() -> Unit = {}): HttpClientOpen shared/src/androidMain/kotlin/com/example/kmmktor/Platform.kt and add an actual declaration of the
httpClient()
function for the Android module:import io.ktor.client.* import io.ktor.client.engine.okhttp.* import java.util.concurrent.TimeUnit actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(OkHttp) { config(this) engine { config { retryOnConnectionFailure(true) connectTimeout(0, TimeUnit.SECONDS) } } }Open shared/src/iosMain/kotlin/com/example/kmmktor/Platform.kt and add an actual declaration of the
httpClient()
function for the iOS module:import io.ktor.client.* import io.ktor.client.engine.darwin.* actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Darwin) { config(this) engine { configureRequest { setAllowsCellularAccess(true) } } }You can now call
httpClient()
in shared code without worrying about which engine is used.To use the client in shared code, open shared/src/commonMain/kotlin/com/example/kmmktor/Greeting.kt and replace the
HttpClient()
constructor with thehttpClient()
function call:private val client = httpClient()