0
# WebSocket Support
1
2
Full WebSocket client implementation with session management, message handling, connection lifecycle, and support for both text and binary messaging with automatic connection management.
3
4
## Capabilities
5
6
### WebSockets Plugin
7
8
Core WebSocket plugin for HTTP client.
9
10
```kotlin { .api }
11
/**
12
* WebSocket plugin for HTTP client
13
*/
14
class WebSockets private constructor() {
15
companion object Plugin : HttpClientPlugin<Config, WebSockets> {
16
override val key: AttributeKey<WebSockets>
17
}
18
19
/**
20
* WebSocket plugin configuration
21
*/
22
class Config {
23
/** Ping interval in milliseconds */
24
var pingInterval: Long? = null
25
26
/** Connection timeout in milliseconds */
27
var timeout: Long = 15000
28
29
/** Maximum frame size */
30
var maxFrameSize: Long = Long.MAX_VALUE
31
32
/** Content converter for message serialization */
33
var contentConverter: WebsocketContentConverter? = null
34
}
35
}
36
```
37
38
### WebSocket Connection
39
40
Establish WebSocket connections with various configuration options.
41
42
```kotlin { .api }
43
/**
44
* Establish WebSocket connection
45
* @param urlString - WebSocket URL (ws:// or wss://)
46
* @param block - WebSocket session handling block
47
*/
48
suspend fun HttpClient.webSocket(
49
urlString: String,
50
block: suspend ClientWebSocketSession.() -> Unit
51
)
52
53
suspend fun HttpClient.webSocket(
54
method: HttpMethod = HttpMethod.Get,
55
host: String = "localhost",
56
port: Int = DEFAULT_PORT,
57
path: String = "/",
58
block: suspend ClientWebSocketSession.() -> Unit
59
)
60
61
suspend fun HttpClient.webSocket(
62
block: HttpRequestBuilder.() -> Unit,
63
sessionBlock: suspend ClientWebSocketSession.() -> Unit
64
)
65
66
/**
67
* Establish secure WebSocket connection (wss://)
68
*/
69
suspend fun HttpClient.wss(
70
urlString: String,
71
block: suspend ClientWebSocketSession.() -> Unit
72
)
73
74
/**
75
* Establish insecure WebSocket connection (ws://)
76
*/
77
suspend fun HttpClient.ws(
78
urlString: String,
79
block: suspend ClientWebSocketSession.() -> Unit
80
)
81
```
82
83
**Usage Examples:**
84
85
```kotlin
86
import io.ktor.client.plugins.websocket.*
87
import io.ktor.websocket.*
88
89
val client = HttpClient {
90
install(WebSockets) {
91
pingInterval = 20000
92
timeout = 15000
93
maxFrameSize = Long.MAX_VALUE
94
}
95
}
96
97
// Simple WebSocket connection
98
client.webSocket("wss://echo.websocket.org") {
99
// Send text message
100
send("Hello, WebSocket!")
101
102
// Receive messages
103
for (frame in incoming) {
104
when (frame) {
105
is Frame.Text -> {
106
val text = frame.readText()
107
println("Received: $text")
108
}
109
is Frame.Binary -> {
110
val bytes = frame.readBytes()
111
println("Received binary: ${bytes.size} bytes")
112
}
113
is Frame.Close -> {
114
println("Connection closed: ${frame.readReason()}")
115
break
116
}
117
}
118
}
119
}
120
121
// WebSocket with custom headers
122
client.webSocket({
123
url("wss://api.example.com/socket")
124
header("Authorization", "Bearer token123")
125
parameter("room", "chat-room-1")
126
}) { session ->
127
// Handle WebSocket session
128
session.send("JOIN room")
129
130
for (frame in incoming) {
131
// Process incoming frames
132
}
133
}
134
```
135
136
### ClientWebSocketSession
137
138
WebSocket session interface for client-side connections.
139
140
```kotlin { .api }
141
/**
142
* Client-side WebSocket session
143
*/
144
interface ClientWebSocketSession : WebSocketSession {
145
/** Associated HTTP client call */
146
val call: HttpClientCall
147
}
148
149
/**
150
* Default implementation of client WebSocket session
151
*/
152
class DefaultClientWebSocketSession(
153
private val call: HttpClientCall,
154
delegate: WebSocketSession
155
) : ClientWebSocketSession, WebSocketSession by delegate
156
157
/**
158
* Base WebSocket session interface
159
*/
160
interface WebSocketSession : CoroutineScope {
161
/** Incoming frames channel */
162
val incoming: ReceiveChannel<Frame>
163
164
/** Outgoing frames channel */
165
val outgoing: SendChannel<Frame>
166
167
/** WebSocket extensions */
168
val extensions: List<WebSocketExtension<*>>
169
170
/** Session close reason */
171
val closeReason: Deferred<CloseReason?>
172
173
/** Send text frame */
174
suspend fun send(content: String)
175
176
/** Send binary frame */
177
suspend fun send(content: ByteArray)
178
179
/** Send frame */
180
suspend fun send(frame: Frame)
181
182
/** Flush outgoing frames */
183
suspend fun flush()
184
185
/** Close the WebSocket connection */
186
suspend fun close(reason: CloseReason? = null)
187
188
/** Terminate the WebSocket connection immediately */
189
fun terminate()
190
}
191
```
192
193
### WebSocket Frames
194
195
Handle different types of WebSocket frames.
196
197
```kotlin { .api }
198
/**
199
* WebSocket frame types
200
*/
201
sealed class Frame {
202
/** Text frame containing UTF-8 text */
203
class Text(
204
val data: ByteArray,
205
val fin: Boolean = true,
206
val rsv1: Boolean = false,
207
val rsv2: Boolean = false,
208
val rsv3: Boolean = false
209
) : Frame() {
210
/** Read frame content as text */
211
fun readText(): String
212
213
/** Copy frame data */
214
fun copy(): ByteArray
215
}
216
217
/** Binary frame containing raw bytes */
218
class Binary(
219
val data: ByteArray,
220
val fin: Boolean = true,
221
val rsv1: Boolean = false,
222
val rsv2: Boolean = false,
223
val rsv3: Boolean = false
224
) : Frame() {
225
/** Read frame content as bytes */
226
fun readBytes(): ByteArray
227
228
/** Copy frame data */
229
fun copy(): ByteArray
230
}
231
232
/** Close frame with optional reason */
233
class Close(
234
val data: ByteArray = byteArrayOf()
235
) : Frame() {
236
/** Read close reason */
237
fun readReason(): CloseReason?
238
}
239
240
/** Ping frame for keep-alive */
241
class Ping(
242
val data: ByteArray
243
) : Frame()
244
245
/** Pong frame in response to ping */
246
class Pong(
247
val data: ByteArray
248
) : Frame()
249
}
250
251
/**
252
* WebSocket close reason
253
*/
254
data class CloseReason(
255
val code: Short,
256
val message: String
257
) {
258
companion object {
259
val NORMAL = CloseReason(1000, "Normal closure")
260
val GOING_AWAY = CloseReason(1001, "Going away")
261
val PROTOCOL_ERROR = CloseReason(1002, "Protocol error")
262
val CANNOT_ACCEPT = CloseReason(1003, "Cannot accept")
263
val NOT_CONSISTENT = CloseReason(1007, "Not consistent")
264
val VIOLATED_POLICY = CloseReason(1008, "Violated policy")
265
val TOO_BIG = CloseReason(1009, "Too big")
266
val NO_EXTENSION = CloseReason(1010, "No extension")
267
val INTERNAL_ERROR = CloseReason(1011, "Internal error")
268
val SERVICE_RESTART = CloseReason(1012, "Service restart")
269
val TRY_AGAIN_LATER = CloseReason(1013, "Try again later")
270
val TLS_HANDSHAKE_FAILED = CloseReason(1015, "TLS handshake failed")
271
}
272
}
273
```
274
275
**Usage Examples:**
276
277
```kotlin
278
client.webSocket("wss://api.example.com/chat") {
279
// Send different types of frames
280
send("Hello, chat!") // Text frame
281
send(byteArrayOf(1, 2, 3, 4)) // Binary frame
282
283
// Send custom frames
284
send(Frame.Ping(byteArrayOf()))
285
286
// Handle incoming frames
287
for (frame in incoming) {
288
when (frame) {
289
is Frame.Text -> {
290
val message = frame.readText()
291
println("Chat message: $message")
292
293
// Echo back
294
send("Echo: $message")
295
}
296
297
is Frame.Binary -> {
298
val data = frame.readBytes()
299
println("Binary data: ${data.size} bytes")
300
301
// Process binary data
302
processBinaryData(data)
303
}
304
305
is Frame.Close -> {
306
val reason = frame.readReason()
307
println("Connection closed: ${reason?.message}")
308
break
309
}
310
311
is Frame.Ping -> {
312
// Respond to ping with pong
313
send(Frame.Pong(frame.data))
314
}
315
316
is Frame.Pong -> {
317
println("Received pong")
318
}
319
}
320
}
321
}
322
```
323
324
### WebSocket Session Management
325
326
Advanced session management and connection handling.
327
328
```kotlin { .api }
329
/**
330
* Get WebSocket session from HTTP call
331
*/
332
suspend fun HttpClient.webSocketSession(
333
block: HttpRequestBuilder.() -> Unit = {}
334
): ClientWebSocketSession
335
336
suspend fun HttpClient.webSocketSession(
337
urlString: String,
338
block: HttpRequestBuilder.() -> Unit = {}
339
): ClientWebSocketSession
340
341
/**
342
* WebSocket session utilities
343
*/
344
suspend fun ClientWebSocketSession.receiveDeserialized<T>(): T
345
suspend fun ClientWebSocketSession.sendSerialized(data: Any)
346
```
347
348
**Usage Examples:**
349
350
```kotlin
351
// Get session without immediate processing
352
val session = client.webSocketSession("wss://api.example.com/socket") {
353
header("Authorization", "Bearer token")
354
}
355
356
try {
357
// Use session
358
session.send("Hello")
359
360
val response = session.incoming.receive()
361
when (response) {
362
is Frame.Text -> println(response.readText())
363
else -> println("Unexpected frame type")
364
}
365
} finally {
366
// Close session
367
session.close(CloseReason.NORMAL)
368
}
369
370
// With content negotiation
371
session.sendSerialized(ChatMessage("user1", "Hello everyone!"))
372
val message: ChatMessage = session.receiveDeserialized()
373
```
374
375
### WebSocket Extensions
376
377
Support for WebSocket protocol extensions.
378
379
```kotlin { .api }
380
/**
381
* WebSocket extension interface
382
*/
383
interface WebSocketExtension<ConfigType : Any> {
384
/** Extension name */
385
val name: String
386
387
/** Extension configuration */
388
val config: ConfigType
389
390
/** Process outgoing frame */
391
fun processOutgoingFrame(frame: Frame): Frame
392
393
/** Process incoming frame */
394
fun processIncomingFrame(frame: Frame): Frame
395
}
396
397
/**
398
* Common WebSocket extensions
399
*/
400
object WebSocketExtensions {
401
/** Deflate extension for compression */
402
val deflate: WebSocketExtension<DeflateConfig>
403
404
/** Per-message deflate extension */
405
val perMessageDeflate: WebSocketExtension<PerMessageDeflateConfig>
406
}
407
```
408
409
### Error Handling
410
411
Handle WebSocket-specific errors and exceptions.
412
413
```kotlin { .api }
414
/**
415
* WebSocket exception for connection errors
416
*/
417
class WebSocketException(
418
message: String,
419
cause: Throwable? = null
420
) : Exception(message, cause)
421
422
/**
423
* Exception for upgrade failures
424
*/
425
class WebSocketUpgradeException(
426
val response: HttpResponse
427
) : WebSocketException("WebSocket upgrade failed: ${response.status}")
428
429
/**
430
* Exception for protocol violations
431
*/
432
class WebSocketProtocolException(
433
message: String
434
) : WebSocketException(message)
435
```
436
437
**Usage Examples:**
438
439
```kotlin
440
try {
441
client.webSocket("wss://invalid-endpoint.example.com") {
442
send("Hello")
443
// Handle session
444
}
445
} catch (e: WebSocketUpgradeException) {
446
println("Failed to upgrade to WebSocket: ${e.response.status}")
447
} catch (e: WebSocketProtocolException) {
448
println("Protocol error: ${e.message}")
449
} catch (e: WebSocketException) {
450
println("WebSocket error: ${e.message}")
451
}
452
```
453
454
## Types
455
456
```kotlin { .api }
457
// WebSocket session types
458
interface WebSocketSession : CoroutineScope {
459
val coroutineContext: CoroutineContext
460
val incoming: ReceiveChannel<Frame>
461
val outgoing: SendChannel<Frame>
462
val extensions: List<WebSocketExtension<*>>
463
val closeReason: Deferred<CloseReason?>
464
}
465
466
// Delegating session implementation
467
class DelegatingClientWebSocketSession(
468
val delegate: WebSocketSession,
469
override val call: HttpClientCall
470
) : ClientWebSocketSession, WebSocketSession by delegate
471
472
// Content conversion types
473
interface WebsocketContentConverter {
474
suspend fun serialize(
475
charset: Charset,
476
typeInfo: TypeInfo,
477
value: Any?
478
): String
479
480
suspend fun deserialize(
481
charset: Charset,
482
typeInfo: TypeInfo,
483
content: String
484
): Any?
485
}
486
487
// Extension configuration types
488
data class DeflateConfig(
489
val serverMaxWindowBits: Int = 15,
490
val clientMaxWindowBits: Int = 15,
491
val serverNoContextTakeover: Boolean = false,
492
val clientNoContextTakeover: Boolean = false
493
)
494
495
data class PerMessageDeflateConfig(
496
val serverMaxWindowBits: Int = 15,
497
val clientMaxWindowBits: Int = 15,
498
val serverNoContextTakeover: Boolean = false,
499
val clientNoContextTakeover: Boolean = false,
500
val compressIfBiggerThan: Int = 1024
501
)
502
```