HTTP Client Engines

Ktor HTTP Client has a common interface but allows to specify an engine that processes the network request. Different engines have different configurations, dependencies and supporting features.

Table of contents:

Default engine

By calling to the HttpClient method without specifying an engine, it uses a default engine.

  1. val client = HttpClient()

In the case of the JVM, the default engine is resolved with a ServiceLoader, getting the first one available sorted in alphabetical order.Thus depends on the artifacts you have included.

For native, the engine detected during static linkage. Please provide one of the native engines in artifacts.

For js, it uses the predefined one.

Configuring engines

Ktor HttpClient lets you configure the parameters of each engine by calling:

  1. HttpClient(MyHttpEngine) {
  2. engine {
  3. // this: MyHttpEngineConfig
  4. }
  5. }

Every engine config has some common properties that can be set:

  • The threadsCount property is a recommendation to use by an engine. It can be ignored if an engine doesn’t require such amount of threads.
  • The pipelining is experimental flag to enable HTTP pipelining.
  1. val client = HttpClient(MyHttpEngine) {
  2. engine {
  3. threadsCount = 4
  4. pipelining = true
  5. }
  6. }

JVM

Apache

Apache is the most configurable HTTP client about right now. It supports HTTP/1.1 and HTTP/2. It is the only one that supports following redirects and allows you to configure timeouts, proxies among other things it is supported by org.apache.httpcomponents:httpasyncclient.

A sample configuration would look like:

  1. val client = HttpClient(Apache) {
  2. engine {
  3. /**
  4. * Apache embedded http redirect, default = false. Obsolete by `HttpRedirect` feature.
  5. * It uses the default number of redirects defined by Apache's HttpClient that is 50.
  6. */
  7. followRedirects = true
  8. /**
  9. * Timeouts.
  10. * Use `0` to specify infinite.
  11. * Negative value mean to use the system's default value.
  12. */
  13. /**
  14. * Max time between TCP packets - default 10 seconds.
  15. */
  16. socketTimeout = 10_000
  17. /**
  18. * Max time to establish an HTTP connection - default 10 seconds.
  19. */
  20. connectTimeout = 10_000
  21. /**
  22. * Max time for the connection manager to start a request - 20 seconds.
  23. */
  24. connectionRequestTimeout = 20_000
  25. customizeClient {
  26. // this: HttpAsyncClientBuilder
  27. setProxy(HttpHost("127.0.0.1", 8080))
  28. // Maximum number of socket connections.
  29. setMaxConnTotal(1000)
  30. // Maximum number of requests for a specific endpoint route.
  31. setMaxConnPerRoute(100)
  32. // ...
  33. }
  34. customizeRequest {
  35. // this: RequestConfig.Builder from Apache.
  36. }
  37. }
  38. }

This engine is defined in the class io.ktor.client.engine.apache.Apache in the artifact io.ktor:ktor-client-apache:$ktor_version. It includes the org.apache.httpcomponents:httpasyncclient transitive dependency.

  1. dependencies {
  2. implementation "io.ktor:ktor-client-apache:$ktor_version"
  3. }
  1. dependencies {
  2. implementation("io.ktor:ktor-client-apache:$ktor_version")
  3. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-apache</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. </dependencies>
  11. </project>

CIO

CIO (Coroutine-based I/O) is a Ktor implementation with no additional dependencies and is fully asynchronous.It only supports HTTP/1.x for now.

CIO provides maxConnectionsCount and a endpointConfig for configuring.

A sample configuration would look like:

  1. val client = HttpClient(CIO) {
  2. engine {
  3. /**
  4. * Maximum number of socket connections.
  5. */
  6. maxConnectionsCount = 1000
  7. /**
  8. * Endpoint specific settings.
  9. */
  10. endpoint {
  11. /**
  12. * Maximum number of requests for a specific endpoint route.
  13. */
  14. maxConnectionsPerRoute = 100
  15. /**
  16. * Max size of scheduled requests per connection(pipeline queue size).
  17. */
  18. pipelineMaxSize = 20
  19. /**
  20. * Max number of milliseconds to keep iddle connection alive.
  21. */
  22. keepAliveTime = 5000
  23. /**
  24. * Number of milliseconds to wait trying to connect to the server.
  25. */
  26. connectTimeout = 5000
  27. /**
  28. * Maximum number of attempts for retrying a connection.
  29. */
  30. connectRetryAttempts = 5
  31. }
  32. /**
  33. * Https specific settings.
  34. */
  35. https {
  36. /**
  37. * Custom server name for TLS server name extension.
  38. * See also: https://en.wikipedia.org/wiki/Server_Name_Indication
  39. */
  40. serverName = "api.ktor.io"
  41. /**
  42. * List of allowed [CipherSuite]s.
  43. */
  44. cipherSuites = CIOCipherSuites.SupportedSuites
  45. /**
  46. * Custom [X509TrustManager] to verify server authority.
  47. *
  48. * Use system by default.
  49. */
  50. trustManager = myCustomTrustManager
  51. /**
  52. * [SecureRandom] to use in encryption.
  53. */
  54. random = mySecureRandom
  55. }
  56. }
  57. }

This engine is defined in the class io.ktor.client.engine.cio.CIO in the artifact io.ktor:ktor-client-cio:$ktor_version.

  1. dependencies {
  2. implementation "io.ktor:ktor-client-cio:$ktor_version"
  3. }
  1. dependencies {
  2. implementation("io.ktor:ktor-client-cio:$ktor_version")
  3. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-cio</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. </dependencies>
  11. </project>

Jetty

Jetty provides an additional sslContextFactory for configuring. It only supports HTTP/2 for now.

A sample configuration would look like:

  1. val client = HttpClient(Jetty) {
  2. engine {
  3. sslContextFactory = SslContextFactory()
  4. }
  5. }

This engine is defined in the class io.ktor.client.engine.jetty.Jetty in the artifact io.ktor:ktor-client-jetty:$ktor_version. It includes the org.eclipse.jetty.http2:http2-client transitive dependency.

  1. dependencies {
  2. implementation "io.ktor:ktor-client-jetty:$ktor_version"
  3. }
  1. dependencies {
  2. implementation("io.ktor:ktor-client-jetty:$ktor_version")
  3. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-jetty</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. </dependencies>
  11. </project>

JVM and Android

OkHttp

There is a engine based on OkHttp:

  1. val client = HttpClient(OkHttp) {
  2. engine {
  3. // https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.Builder.html
  4. config { // this: OkHttpClient.Builder ->
  5. // ...
  6. followRedirects(true)
  7. // ...
  8. }
  9. // https://square.github.io/okhttp/3.x/okhttp/okhttp3/Interceptor.html
  10. addInterceptor(interceptor)
  11. addNetworkInterceptor(interceptor)
  12. /**
  13. * Set okhttp client instance to use instead of creating one.
  14. */
  15. preconfigured = okHttpClientInstance
  16. }
  17. }

This engine is defined in the class io.ktor.client.engine.okhttp.OkHttp in the artifact io.ktor:ktor-client-okhttp:$ktor_version. It includes the com.squareup.okhttp3:okhttp transitive dependency.

  1. dependencies {
  2. implementation "io.ktor:ktor-client-okhttp:$ktor_version"
  3. }
  1. dependencies {
  2. implementation("io.ktor:ktor-client-okhttp:$ktor_version")
  3. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-okhttp</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. </dependencies>
  11. </project>

Android

The Android engine doesn’t have additional dependencies and uses a ThreadPool with a normal HttpURLConnection,to perform the requests. And can be configured like this:

  1. val client = HttpClient(Android) {
  2. engine {
  3. connectTimeout = 100_000
  4. socketTimeout = 100_000
  5. /**
  6. * Proxy address to use.
  7. */
  8. proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("localhost", serverPort))
  9. }
  10. }

This engine is defined in the class io.ktor.client.engine.android.Android in the artifact io.ktor:ktor-client-android:$ktor_version.

  1. dependencies {
  2. implementation "io.ktor:ktor-client-android:$ktor_version"
  3. }
  1. dependencies {
  2. implementation("io.ktor:ktor-client-android:$ktor_version")
  3. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-android</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. </dependencies>
  11. </project>

iOS

The iOS engine uses the asynchronous NSURLSession internally. And have no additional configuration.

  1. val client = HttpClient(Ios) {
  2. /**
  3. * Configure native NSUrlRequest.
  4. */
  5. configureRequest { // this: NSMutableURLRequest
  6. setAllowsCellularAccess(true)
  7. // ...
  8. }
  9. }

This engine is defined in the class io.ktor.client.engine.ios.Ios in the artifact io.ktor:ktor-client-ios:$ktor_version.

  1. dependencies {
  2. implementation "io.ktor:ktor-client-ios:$ktor_version"
  3. }
  1. dependencies {
  2. implementation("io.ktor:ktor-client-ios:$ktor_version")
  3. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-ios</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. </dependencies>
  11. </project>

Js (JavaScript)

The Js engine, uses the fetch API internally(and node-fetch for node.js runtime).

Js engine has no custom configuration.

  1. val client = HttpClient(Js) {
  2. }

You can also call the JsClient() function to get the Js engine singleton.

This engine is defined in the class io.ktor.client.engine.js.Js in the artifact io.ktor:ktor-client-js:$ktor_version.

  1. dependencies {
  2. implementation "io.ktor:ktor-client-js:$ktor_version"
  3. }
  1. dependencies {
  2. implementation("io.ktor:ktor-client-js:$ktor_version")
  3. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-js</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. </dependencies>
  11. </project>

Curl

There is an engine based on Curl:

  1. val client = HttpClient(Curl)

Supported platforms: linux_x64, macos_x64, mingw_x64. Please note that to use the engine you must have the installed curl library at least version 7.63

This engine is defined in the class io.ktor.client.engine.curl.Curl in the artifact io.ktor:ktor-client-curl:$ktor_version.

  1. dependencies {
  2. implementation "io.ktor:ktor-client-curl:$ktor_version"
  3. }
  1. dependencies {
  2. implementation("io.ktor:ktor-client-curl:$ktor_version")
  3. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-curl</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. </dependencies>
  11. </project>

MockEngine

The MockEngine is the common engine for testing. See also MockEngine for testing.