Ktor client WebSocket plugin for iOS x64 target providing WebSocket client capabilities for iOS simulator applications using Kotlin Multiplatform.
—
WebSocket session types and lifecycle management for maintaining connection state and handling communication context.
Core interfaces and classes for managing WebSocket sessions.
/**
* Client-specific WebSocket session interface extending base WebSocketSession
* with client call context
*/
interface ClientWebSocketSession : WebSocketSession {
/** HttpClientCall associated with this WebSocket session */
val call: HttpClientCall
}
/**
* Default implementation of client WebSocket session combining
* ClientWebSocketSession interface with DefaultWebSocketSession behavior
* @param call The associated HTTP client call
* @param delegate The underlying default WebSocket session
*/
class DefaultClientWebSocketSession(
override val call: HttpClientCall,
delegate: DefaultWebSocketSession
) : ClientWebSocketSession, DefaultWebSocketSession by delegateFunctions that return WebSocket sessions directly for manual lifecycle management.
/**
* Creates and returns a WebSocket session using request builder configuration
* @param block Configuration block for the HTTP request
* @return DefaultClientWebSocketSession for manual management
*/
suspend fun HttpClient.webSocketSession(
block: HttpRequestBuilder.() -> Unit
): DefaultClientWebSocketSession
/**
* Creates and returns a WebSocket session using method, host, port, and path
* @param method HTTP method for the WebSocket handshake (default: GET)
* @param host Target hostname
* @param port Target port number
* @param path Target path
* @param block Additional request configuration
* @return DefaultClientWebSocketSession for manual management
*/
suspend fun HttpClient.webSocketSession(
method: HttpMethod = HttpMethod.Get,
host: String? = null,
port: Int? = null,
path: String? = null,
block: HttpRequestBuilder.() -> Unit = {}
): DefaultClientWebSocketSession
/**
* Creates and returns a WebSocket session using URL string
* @param urlString WebSocket URL (ws:// or wss://)
* @param block Additional request configuration
* @return DefaultClientWebSocketSession for manual management
*/
suspend fun HttpClient.webSocketSession(
urlString: String,
block: HttpRequestBuilder.() -> Unit = {}
): DefaultClientWebSocketSessionUsage Examples:
// Create session with manual lifecycle management
val session = client.webSocketSession("ws://echo.websocket.org")
try {
// Use the session
session.send("Hello!")
val frame = session.incoming.receive()
when (frame) {
is Frame.Text -> println("Received: ${frame.readText()}")
else -> {}
}
} finally {
// Manual cleanup required
session.close()
}
// Create session with request configuration
val session = client.webSocketSession {
url("ws://example.com/ws")
header("Authorization", "Bearer token")
}
// Use session...
session.close()Access to connection context and metadata through session properties.
/**
* Access to the HTTP client call associated with the WebSocket session
* Provides request/response context and metadata
*/
val ClientWebSocketSession.call: HttpClientCallUsage Examples:
client.webSocket("ws://example.com") {
// Access call information
println("Connected to: ${call.request.url}")
println("Response status: ${call.response.status}")
println("Response headers: ${call.response.headers}")
// Access client information
println("Client engine: ${call.client.engine}")
// Send messages using session context
send("Hello from ${call.request.url.host}!")
}Automatic Management (Recommended):
// Connection and cleanup handled automatically
client.webSocket("ws://example.com") {
// Session is active here
send("Message")
// Session is automatically closed when block exits
}Manual Management:
// Manual lifecycle control
val session = client.webSocketSession("ws://example.com")
try {
session.send("Message")
// Handle messages...
} finally {
session.close() // Must close manually
}WebSocket sessions inherit full functionality from DefaultWebSocketSession:
client.webSocket("ws://example.com") {
// Connection state
println("Is closed: ${closeReason.isActive}")
// Frame size configuration
maxFrameSize = 1024 * 1024 // 1MB
// Ping configuration
pingIntervalMillis = 30000 // 30 seconds
// Send different frame types
send("Text message")
send(Frame.Binary(true, byteArrayOf(1, 2, 3)))
send(Frame.Ping(byteArrayOf()))
// Receive frames
for (frame in incoming) {
when (frame) {
is Frame.Text -> println("Text: ${frame.readText()}")
is Frame.Binary -> println("Binary: ${frame.data.size} bytes")
is Frame.Ping -> send(Frame.Pong(frame.data))
is Frame.Pong -> println("Pong received")
is Frame.Close -> {
println("Close: ${frame.readReason()}")
break
}
}
}
}Monitor connection state and handle disconnections:
client.webSocket("ws://example.com") {
// Launch a coroutine to monitor connection
launch {
try {
// Wait for close reason
val reason = closeReason.await()
println("Connection closed: $reason")
} catch (e: Exception) {
println("Connection error: ${e.message}")
}
}
// Main message processing
try {
for (frame in incoming) {
// Process frames...
}
} catch (e: ClosedReceiveChannelException) {
println("Connection was closed")
}
}Access WebSocket extensions through the session:
client.webSocket("ws://example.com") {
// Access WebSocket extensions (if supported by engine)
val extensions = call.attributes.getOrNull(extensionsKey)
extensions?.forEach { extension ->
println("Active extension: ${extension.factory.key}")
}
}Implement custom session behavior by working with the underlying session:
internal class CustomClientWebSocketSession(
override val call: HttpClientCall,
private val delegate: WebSocketSession
) : ClientWebSocketSession, WebSocketSession by delegate {
suspend fun sendWithTimestamp(message: String) {
val timestamped = "${System.currentTimeMillis()}: $message"
send(timestamped)
}
}Debug WebSocket sessions using call context:
client.webSocket("ws://example.com") {
println("=== WebSocket Session Debug Info ===")
println("URL: ${call.request.url}")
println("Method: ${call.request.method}")
println("Headers: ${call.request.headers.entries()}")
println("Response Status: ${call.response.status}")
println("Response Headers: ${call.response.headers.entries()}")
println("===================================")
// Continue with normal WebSocket operations...
}Session-related errors and their handling:
try {
val session = client.webSocketSession("ws://invalid.com")
// Use session...
} catch (e: WebSocketException) {
println("WebSocket handshake failed: ${e.message}")
} catch (e: ConnectTimeoutException) {
println("Connection timeout")
} catch (e: Exception) {
println("Other connection error: ${e.message}")
}Session cleanup on errors:
var session: DefaultClientWebSocketSession? = null
try {
session = client.webSocketSession("ws://example.com")
// Use session...
} catch (e: Exception) {
println("Error occurred: ${e.message}")
} finally {
session?.close()
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-websockets-iosx64