0
# Message Handling
1
2
Client-specific WebSocket session interfaces and message serialization capabilities that integrate with Ktor's HTTP client call lifecycle and provide automatic content conversion.
3
4
## Capabilities
5
6
### Client WebSocket Session Interface
7
8
Core interface for client-side WebSocket sessions that provides access to the associated HTTP client call.
9
10
```kotlin { .api }
11
/**
12
* Client specific WebSocketSession with access to HTTP call context
13
*/
14
interface ClientWebSocketSession : WebSocketSession {
15
/**
16
* HttpClientCall associated with this WebSocket session
17
*/
18
val call: HttpClientCall
19
}
20
```
21
22
### Default Client WebSocket Session
23
24
Default implementation of client WebSocket session that delegates to DefaultWebSocketSession while providing HTTP call access.
25
26
```kotlin { .api }
27
/**
28
* Default implementation of ClientWebSocketSession
29
*/
30
class DefaultClientWebSocketSession(
31
override val call: HttpClientCall,
32
delegate: DefaultWebSocketSession
33
) : ClientWebSocketSession, DefaultWebSocketSession by delegate
34
```
35
36
**Usage Examples:**
37
38
```kotlin
39
import io.ktor.client.*
40
import io.ktor.client.plugins.websocket.*
41
import io.ktor.websocket.*
42
43
val client = HttpClient {
44
install(WebSockets)
45
}
46
47
client.webSocket("ws://localhost:8080/websocket") { session ->
48
// session is DefaultClientWebSocketSession
49
50
// Access HTTP call information
51
val requestUrl = session.call.request.url
52
val userAgent = session.call.request.headers["User-Agent"]
53
54
println("Connected to: $requestUrl")
55
println("User-Agent: $userAgent")
56
57
// Use standard WebSocket operations
58
session.send("Hello from ${userAgent}")
59
60
for (frame in session.incoming) {
61
when (frame) {
62
is Frame.Text -> println("Received: ${frame.readText()}")
63
is Frame.Close -> break
64
else -> {}
65
}
66
}
67
}
68
```
69
70
### Content Converter Access
71
72
Access to the WebSocket content converter configured in the plugin for automatic serialization/deserialization.
73
74
```kotlin { .api }
75
/**
76
* Extension property to access the content converter from WebSockets plugin
77
*/
78
val DefaultClientWebSocketSession.converter: WebsocketContentConverter?
79
```
80
81
**Usage Examples:**
82
83
```kotlin
84
client.webSocket("ws://localhost:8080/api") {
85
val converter = this.converter
86
if (converter != null) {
87
println("Content converter available: ${converter::class.simpleName}")
88
} else {
89
println("No content converter configured")
90
}
91
}
92
```
93
94
### Message Serialization
95
96
Send serialized data using the configured content converter.
97
98
```kotlin { .api }
99
/**
100
* Serializes data to a frame and enqueues it using TypeInfo
101
*/
102
suspend fun DefaultClientWebSocketSession.sendSerialized(
103
data: Any?,
104
typeInfo: TypeInfo
105
)
106
107
/**
108
* Serializes data to a frame and enqueues it using reified type
109
*/
110
suspend inline fun <reified T> DefaultClientWebSocketSession.sendSerialized(data: T)
111
```
112
113
**Usage Examples:**
114
115
```kotlin
116
import io.ktor.util.reflect.*
117
118
data class Message(val text: String, val timestamp: Long)
119
data class User(val id: Int, val name: String)
120
121
val client = HttpClient {
122
install(WebSockets) {
123
contentConverter = JsonWebsocketContentConverter()
124
}
125
}
126
127
client.webSocket("ws://localhost:8080/chat") {
128
// Send with reified type (recommended)
129
sendSerialized(Message("Hello", System.currentTimeMillis()))
130
131
// Send with explicit TypeInfo
132
val user = User(123, "Alice")
133
sendSerialized(user, typeInfo<User>())
134
135
// Handle responses...
136
}
137
```
138
139
### Message Deserialization
140
141
Receive and deserialize frames using the configured content converter.
142
143
```kotlin { .api }
144
/**
145
* Dequeues a frame and deserializes it using TypeInfo
146
*/
147
suspend fun <T> DefaultClientWebSocketSession.receiveDeserialized(typeInfo: TypeInfo): T
148
149
/**
150
* Dequeues a frame and deserializes it using reified type
151
*/
152
suspend inline fun <reified T> DefaultClientWebSocketSession.receiveDeserialized(): T
153
```
154
155
**Usage Examples:**
156
157
```kotlin
158
client.webSocket("ws://localhost:8080/api") {
159
// Send a request
160
sendSerialized(ApiRequest("getUserList"))
161
162
// Receive typed response (recommended)
163
val userList: List<User> = receiveDeserialized()
164
println("Received ${userList.size} users")
165
166
// Receive with explicit TypeInfo
167
val response: ApiResponse = receiveDeserialized(typeInfo<ApiResponse>())
168
println("API response: ${response.status}")
169
}
170
```
171
172
### Error Handling for Serialization
173
174
Serialization and deserialization operations can throw specific exceptions.
175
176
```kotlin { .api }
177
/**
178
* Base exception for WebSocket content conversion errors
179
*/
180
open class WebsocketContentConvertException(
181
message: String,
182
cause: Throwable? = null
183
) : ContentConvertException(message, cause)
184
185
/**
186
* Exception thrown when no content converter is found
187
*/
188
class WebsocketConverterNotFoundException(
189
message: String,
190
cause: Throwable? = null
191
) : WebsocketContentConvertException(message, cause)
192
193
/**
194
* Exception thrown when deserialization fails
195
*/
196
class WebsocketDeserializeException(
197
message: String,
198
cause: Throwable? = null,
199
val frame: Frame
200
) : WebsocketContentConvertException(message, cause)
201
```
202
203
**Usage Examples:**
204
205
```kotlin
206
client.webSocket("ws://localhost:8080/api") {
207
try {
208
// This will throw if no converter is configured
209
sendSerialized(MyData("test"))
210
211
val response: MyResponse = receiveDeserialized()
212
213
} catch (e: WebsocketConverterNotFoundException) {
214
println("No content converter configured: ${e.message}")
215
216
} catch (e: WebsocketDeserializeException) {
217
println("Failed to deserialize message: ${e.message}")
218
e.frame?.let { frame ->
219
println("Problematic frame type: ${frame::class.simpleName}")
220
}
221
222
} catch (e: ClosedReceiveChannelException) {
223
println("WebSocket connection closed")
224
}
225
}
226
```
227
228
### Raw Frame Access
229
230
Direct access to WebSocket frames alongside serialization capabilities.
231
232
**Usage Examples:**
233
234
```kotlin
235
client.webSocket("ws://localhost:8080/mixed") {
236
// Send serialized data
237
sendSerialized(MyMessage("Hello"))
238
239
// Send raw frame
240
send("Raw text message")
241
242
// Handle mixed message types
243
for (frame in incoming) {
244
when (frame) {
245
is Frame.Text -> {
246
val text = frame.readText()
247
if (text.startsWith("{")) {
248
// Assume JSON, deserialize manually
249
try {
250
val data: MyMessage = converter?.deserialize(
251
frame,
252
typeInfo<MyMessage>()
253
) ?: error("No converter")
254
println("Structured: $data")
255
} catch (e: Exception) {
256
println("Raw text: $text")
257
}
258
} else {
259
println("Raw text: $text")
260
}
261
}
262
is Frame.Binary -> {
263
// Handle binary frames
264
val bytes = frame.readBytes()
265
println("Binary data: ${bytes.size} bytes")
266
}
267
is Frame.Close -> break
268
else -> {}
269
}
270
}
271
}
272
```
273
274
### Session Management with HTTP Context
275
276
Leverage HTTP call context for WebSocket session management.
277
278
**Usage Examples:**
279
280
```kotlin
281
client.webSocket("ws://localhost:8080/websocket") {
282
// Access request information
283
val originalUrl = call.request.url
284
val requestHeaders = call.request.headers
285
286
// Access response information (from upgrade response)
287
val responseStatus = call.response.status
288
val responseHeaders = call.response.headers
289
290
println("Upgraded from: $originalUrl")
291
println("Response status: $responseStatus")
292
293
// Check if specific headers were set during upgrade
294
val serverProtocol = responseHeaders["Sec-WebSocket-Protocol"]
295
if (serverProtocol != null) {
296
println("Server selected protocol: $serverProtocol")
297
}
298
299
// Use this information for session logic
300
send("Connected with protocol: ${serverProtocol ?: "default"}")
301
}
302
```