Ktor HTTP client core library - asynchronous framework for creating HTTP clients in Kotlin multiplatform
—
Full-duplex WebSocket communication with extensions support, content conversion, and platform-specific optimizations for real-time applications.
Core WebSocket plugin providing client-side WebSocket functionality.
/**
* WebSocket client plugin for full-duplex communication
*/
object WebSockets : HttpClientPlugin<WebSockets.Config, WebSockets> {
override val key: AttributeKey<WebSockets> = AttributeKey("WebSocket")
/**
* WebSocket configuration
*/
class Config {
/** Ping interval in milliseconds (-1 to disable) */
var pingInterval: Long = -1L
/** Maximum frame size in bytes */
var maxFrameSize: Long = Long.MAX_VALUE
/** Content converter for serialization */
var contentConverter: WebsocketContentConverter? = null
/**
* Configure WebSocket extensions
*/
fun extensions(block: WebSocketExtensionsConfig.() -> Unit)
}
}Functions for establishing WebSocket connections with various configuration options.
/**
* Create WebSocket session with request configuration
*/
suspend fun HttpClient.webSocketSession(
block: HttpRequestBuilder.() -> Unit
): DefaultClientWebSocketSession
/**
* Create WebSocket session with URL components
*/
suspend fun HttpClient.webSocketSession(
method: HttpMethod = HttpMethod.Get,
host: String = "localhost",
port: Int = DEFAULT_PORT,
path: String = "/",
block: HttpRequestBuilder.() -> Unit = {}
): DefaultClientWebSocketSession
/**
* Create WebSocket session with URL string
*/
suspend fun HttpClient.webSocketSession(
urlString: String,
block: HttpRequestBuilder.() -> Unit = {}
): DefaultClientWebSocketSession
/**
* Use WebSocket with session handler
*/
suspend fun HttpClient.webSocket(
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
)
/**
* Use WebSocket with URL and session handler
*/
suspend fun HttpClient.webSocket(
urlString: String,
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
)
/**
* Use WebSocket with URL components
*/
suspend fun HttpClient.webSocket(
method: HttpMethod = HttpMethod.Get,
host: String = "localhost",
port: Int = DEFAULT_PORT,
path: String = "/",
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
)Usage Examples:
import io.ktor.client.plugins.websocket.*
import io.ktor.websocket.*
val client = HttpClient {
install(WebSockets) {
pingInterval = 20_000 // 20 seconds
maxFrameSize = 1024 * 1024 // 1MB
}
}
// Simple WebSocket usage
client.webSocket("wss://echo.websocket.org") {
send("Hello WebSocket!")
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 ${data.size} bytes")
}
is Frame.Close -> {
println("Connection closed")
break
}
else -> {}
}
}
}
// WebSocket session for manual control
val session = client.webSocketSession("wss://api.example.com/ws") {
headers["Authorization"] = "Bearer $token"
}
try {
session.send("Hello")
val frame = session.incoming.receive()
// Process frame
} finally {
session.close()
}Interface for WebSocket communication sessions with frame handling.
/**
* Default client WebSocket session implementation
*/
interface DefaultClientWebSocketSession : ClientWebSocketSession {
/** Incoming frames channel */
val incoming: ReceiveChannel<Frame>
/** Outgoing frames channel */
val outgoing: SendChannel<Frame>
/** WebSocket extensions */
val extensions: List<WebSocketExtension<*>>
/**
* Send text frame
*/
suspend fun send(content: String)
/**
* Send binary frame
*/
suspend fun send(content: ByteArray)
/**
* Send frame
*/
suspend fun send(frame: Frame)
/**
* Close WebSocket connection
*/
suspend fun close(reason: CloseReason = CloseReason(CloseReason.Codes.NORMAL, ""))
/**
* Flush outgoing frames
*/
fun flush()
}Usage Examples:
client.webSocket("wss://api.example.com/chat") {
// Send different types of content
send("Hello, World!") // Text frame
send(byteArrayOf(1, 2, 3, 4)) // Binary frame
send(Frame.Text("Custom text frame"))
send(Frame.Binary(true, byteArrayOf(5, 6, 7, 8)))
// Handle different frame types
for (frame in incoming) {
when (frame) {
is Frame.Text -> {
val text = frame.readText()
println("Text: $text")
// Echo back
send("Echo: $text")
}
is Frame.Binary -> {
val bytes = frame.readBytes()
println("Binary data: ${bytes.size} bytes")
}
is Frame.Ping -> {
println("Ping received")
// Pong is sent automatically
}
is Frame.Pong -> {
println("Pong received")
}
is Frame.Close -> {
val reason = frame.readReason()
println("Connection closed: ${reason?.message}")
break
}
}
}
}Engine capabilities for WebSocket support and extensions.
/**
* Basic WebSocket support capability
*/
object WebSocketCapability : HttpClientEngineCapability<Unit>
/**
* WebSocket extensions support capability
*/
object WebSocketExtensionsCapability : HttpClientEngineCapability<List<WebSocketExtensionConfig>>Usage Examples:
val client = HttpClient(CIO)
// Check WebSocket support
if (client.isSupported(WebSocketCapability)) {
println("WebSocket is supported by this engine")
// Use WebSocket functionality
client.webSocket("wss://api.example.com/ws") {
// WebSocket logic
}
} else {
println("WebSocket not supported")
}
// Check extensions support
if (client.isSupported(WebSocketExtensionsCapability)) {
println("WebSocket extensions are supported")
}Convenient short aliases for WebSocket functions.
/**
* Short alias for webSocket function
*/
suspend fun HttpClient.ws(
urlString: String,
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
) = webSocket(urlString, request, block)
/**
* Secure WebSocket connection (wss://)
*/
suspend fun HttpClient.wss(
host: String,
port: Int = 443,
path: String = "/",
request: HttpRequestBuilder.() -> Unit = {},
block: suspend DefaultClientWebSocketSession.() -> Unit
) = webSocket(HttpMethod.Get, host, port, path, request, block)Usage Examples:
// Short alias
client.ws("wss://echo.websocket.org") {
send("Hello from ws!")
val response = (incoming.receive() as Frame.Text).readText()
println("Received: $response")
}
// Secure WebSocket with components
client.wss("api.example.com", 443, "/websocket") {
headers["Authorization"] = "Bearer $token"
} {
send("Authenticated message")
// Handle frames
}Support for automatic serialization/deserialization of WebSocket messages.
/**
* WebSocket content converter interface
*/
interface WebsocketContentConverter {
/**
* Serialize object to WebSocket frame
*/
suspend fun serialize(
charset: Charset,
typeInfo: TypeInfo,
value: Any
): Frame
/**
* Deserialize WebSocket frame to object
*/
suspend fun deserialize(
charset: Charset,
typeInfo: TypeInfo,
content: Frame
): Any?
}
/**
* Send typed object (requires content converter)
*/
suspend inline fun <reified T> DefaultClientWebSocketSession.sendSerialized(data: T)
/**
* Receive typed object (requires content converter)
*/
suspend inline fun <reified T> DefaultClientWebSocketSession.receiveDeserialized(): TUsage Examples:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable
@Serializable
data class ChatMessage(val user: String, val message: String, val timestamp: Long)
val client = HttpClient {
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
}
client.webSocket("wss://chat.example.com/room/123") {
// Send typed object
val message = ChatMessage("alice", "Hello everyone!", System.currentTimeMillis())
sendSerialized(message)
// Receive typed object
val receivedMessage: ChatMessage = receiveDeserialized()
println("${receivedMessage.user}: ${receivedMessage.message}")
}WebSocket-specific exception handling and error patterns.
/**
* Base WebSocket exception class
*/
open class WebSocketException(message: String) : IllegalStateException(message)
/**
* WebSocket connection failed exception
*/
class WebSocketConnectionException(message: String, cause: Throwable? = null)
: WebSocketException(message)
/**
* WebSocket close codes and reasons
*/
object CloseReason {
object Codes {
const val NORMAL = 1000
const val GOING_AWAY = 1001
const val PROTOCOL_ERROR = 1002
const val UNSUPPORTED_DATA = 1003
const val NO_STATUS = 1005
const val ABNORMAL_CLOSURE = 1006
const val INVALID_PAYLOAD = 1007
const val POLICY_VIOLATION = 1008
const val MESSAGE_TOO_BIG = 1009
const val MANDATORY_EXTENSION = 1010
const val INTERNAL_ERROR = 1011
const val TLS_HANDSHAKE_FAILED = 1015
}
}
data class CloseReason(val code: Short, val message: String)Usage Examples:
try {
client.webSocket("wss://api.example.com/ws") {
send("Hello")
for (frame in incoming) {
when (frame) {
is Frame.Close -> {
val reason = frame.readReason()
when (reason?.code) {
CloseReason.Codes.NORMAL -> println("Normal close")
CloseReason.Codes.GOING_AWAY -> println("Server going away")
CloseReason.Codes.PROTOCOL_ERROR -> println("Protocol error")
else -> println("Close reason: ${reason?.message}")
}
break
}
// Handle other frames
else -> {}
}
}
}
} catch (e: WebSocketException) {
println("WebSocket error: ${e.message}")
} catch (e: WebSocketConnectionException) {
println("Connection failed: ${e.message}")
}
// Graceful close
client.webSocket("wss://api.example.com/ws") {
try {
// WebSocket communication
send("Hello")
} finally {
// Always close cleanly
close(CloseReason(CloseReason.Codes.NORMAL, "Client disconnecting"))
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-core-iosx64