Kotlin WebSocket capability


What's inside this article ⌄
  • WebSocket capability not supported
  • Engine WebSocket capability error
  • Fix WebSocket support error
  • WebSocket engine compatibility

The problem

When developing a Kotlin Multiplatform/Ktor application that uses WebSockets for Android (or for other platform!), we might run into this exception:

Connection error: java.lang.IllegalArgumentException: Engine doesn't support WebSocketCapability
  at io.ktor.client.engine.HttpClientEngine$DefaultImpls.checkExtensions(HttpClientEngine.kt:105)
  at io.ktor.client.engine.HttpClientEngine$DefaultImpls.access$checkExtensions(HttpClientEngine.kt:24)
  at io.ktor.client.engine.HttpClientEngine$install$1.invokeSuspend(HttpClientEngine.kt:68)
  at io.ktor.client.engine.HttpClientEngine$install$1.invoke(Unknown Source:15)
  at io.ktor.client.engine.HttpClientEngine$install$1.invoke(Unknown Source:4)

Reason

TL;DR:

  • Most likely you miss the http engine dependency for the android part (or other platform).
  • Or you need to switch http engine for one that supports WebSockets. Useful table to compare engines is here.

Solution

okhttp is one of the engines that works both with Android and WebSockets. If you target another platform, look at the link from above.

kotlin {
  sourceSets {
    androidMain.dependencies {
       implementation("io.ktor:ktor-client-okhttp:${ktor}}")
    }
  }
}

To be able to understand the root of the problem, we need to look at how http client works in Kotlin Multiplatform application. Consider this:

  • Shared code. Usually it should contain http client interfaces only, without implementation/http engine. Dependency to include to the shared code: io.ktor:ktor-client-core:${ktor}.
  • Specific platform. It should contain implementation dependency for the http engine.
    • Can be okhttp for Android module: io.ktor:ktor-client-okhttp:${ktor}.
    • Can be cio for Dekstop module: io.ktor:ktor-client-cio:${ktor}.
    • Can be darwin for IOS module: io.ktor:ktor-client-darwin:${ktor}.

In the shared code we can have something like this:

private val client = HttpClient {
    install(WebSockets)
}

In this code, we just initiate basic http client and add WebSocket support. However, the actual client (engine) depends on the platform! And it depends on the library we use. The code from above uses just high-level interfaces.

So when you encounter a case when you have this high-level code in the app, but doesn’t include http engine dependency, it throws an error “Engine doesn’t support WebSocketCapability”.


Appendix

For the general knowledge, shared code for http client initialization is very limited in its capabilities.

For example, we can’t set up SSL custom configuration, since it’s platform-dependent, therefore, if we need to have ability to configure custom SSL, we need to use expected/actual mechanism.

Look at the great example here.