0
# Response Handling
1
2
Complete HTTP response processing including content reading, status handling, headers access, and type-safe response body parsing with support for various content types.
3
4
## Capabilities
5
6
### HttpResponse
7
8
Core response handling functionality.
9
10
```kotlin { .api }
11
/**
12
* Abstract representation of HTTP response
13
*/
14
abstract class HttpResponse : HttpMessage, CoroutineScope {
15
/** HTTP status code */
16
abstract val status: HttpStatusCode
17
18
/** HTTP protocol version */
19
abstract val version: HttpProtocolVersion
20
21
/** Request timestamp */
22
abstract val requestTime: GMTDate
23
24
/** Response timestamp */
25
abstract val responseTime: GMTDate
26
27
/** Associated HTTP client call */
28
abstract val call: HttpClientCall
29
30
/** Response headers */
31
abstract override val headers: Headers
32
33
/** Response content */
34
abstract val content: ByteReadChannel
35
}
36
```
37
38
### Response Body Reading
39
40
Read response content in various formats.
41
42
```kotlin { .api }
43
/**
44
* Read response body as text
45
* @param charset - Character encoding (defaults to UTF-8)
46
*/
47
suspend fun HttpResponse.bodyAsText(
48
charset: Charset = Charsets.UTF_8
49
): String
50
51
/**
52
* Read response body with generic type
53
*/
54
suspend inline fun <reified T> HttpResponse.body(): T
55
56
/**
57
* Get response body as ByteReadChannel for streaming
58
*/
59
suspend fun HttpResponse.bodyAsChannel(): ByteReadChannel
60
61
/**
62
* Read response body with type information
63
* @param typeInfo - Type information for deserialization
64
*/
65
suspend fun <T> HttpResponse.body(typeInfo: TypeInfo): T
66
67
/**
68
* Read response body as specific type (requires content negotiation)
69
*/
70
suspend inline fun <reified T> HttpResponse.body(): T
71
```
72
73
**Usage Examples:**
74
75
```kotlin
76
val client = HttpClient()
77
78
// Read as text
79
val response = client.get("https://api.example.com/data")
80
val textContent: String = response.bodyAsText()
81
println(textContent)
82
83
// Read as byte array
84
val imageResponse = client.get("https://api.example.com/image.jpg")
85
val imageBytes: ByteArray = imageResponse.body()
86
87
// Stream content
88
val largeFileResponse = client.get("https://api.example.com/large-file")
89
val channel: ByteReadChannel = largeFileResponse.bodyAsChannel()
90
while (!channel.isClosedForRead) {
91
val packet = channel.readRemaining(8192)
92
// Process packet
93
}
94
95
// Deserialize JSON (with ContentNegotiation plugin)
96
data class User(val id: Int, val name: String, val email: String)
97
val userResponse = client.get("https://api.example.com/users/123")
98
val user: User = userResponse.body()
99
```
100
101
### Response Body Processing
102
103
Advanced body processing with various content reading strategies.
104
105
```kotlin { .api }
106
/**
107
* Read response body as byte array
108
*/
109
suspend fun HttpResponse.bodyAsBytes(): ByteArray
110
111
/**
112
* Read response body with fallback charset
113
*/
114
suspend fun HttpResponse.bodyAsText(
115
fallbackCharset: Charset = Charsets.UTF_8
116
): String
117
118
/**
119
* Check if response body can be received multiple times
120
*/
121
val HttpResponse.isClosedForReceive: Boolean
122
123
/**
124
* Response container for typed responses
125
*/
126
data class HttpResponseContainer(
127
val expectedType: TypeInfo,
128
val response: Any
129
)
130
```
131
132
**Usage Examples:**
133
134
```kotlin
135
val client = HttpClient()
136
137
// Read as different formats
138
val response = client.get("https://api.example.com/data")
139
140
// As bytes
141
val bytes: ByteArray = response.bodyAsBytes()
142
143
// As text with specific charset
144
val text = response.bodyAsText(Charsets.ISO_8859_1)
145
146
// Streaming large responses
147
val largeResponse = client.get("https://api.example.com/large-file")
148
val channel = largeResponse.bodyAsChannel()
149
val buffer = ByteArray(8192)
150
while (!channel.isClosedForRead) {
151
val bytesRead = channel.readAvailable(buffer)
152
if (bytesRead > 0) {
153
// Process buffer[0..bytesRead]
154
}
155
}
156
```
157
158
### Status Code Handling
159
160
Work with HTTP status codes and handle different response scenarios.
161
162
```kotlin { .api }
163
/**
164
* HTTP status code representation
165
*/
166
class HttpStatusCode(
167
val value: Int,
168
val description: String
169
) {
170
/** Check if status indicates success (2xx) */
171
fun isSuccess(): Boolean = value in 200..299
172
173
/** Check if status indicates client error (4xx) */
174
fun isClientError(): Boolean = value in 400..499
175
176
/** Check if status indicates server error (5xx) */
177
fun isServerError(): Boolean = value in 500..599
178
179
companion object {
180
// Success codes
181
val OK = HttpStatusCode(200, "OK")
182
val Created = HttpStatusCode(201, "Created")
183
val Accepted = HttpStatusCode(202, "Accepted")
184
val NoContent = HttpStatusCode(204, "No Content")
185
186
// Redirection codes
187
val MovedPermanently = HttpStatusCode(301, "Moved Permanently")
188
val Found = HttpStatusCode(302, "Found")
189
val NotModified = HttpStatusCode(304, "Not Modified")
190
191
// Client error codes
192
val BadRequest = HttpStatusCode(400, "Bad Request")
193
val Unauthorized = HttpStatusCode(401, "Unauthorized")
194
val Forbidden = HttpStatusCode(403, "Forbidden")
195
val NotFound = HttpStatusCode(404, "Not Found")
196
197
// Server error codes
198
val InternalServerError = HttpStatusCode(500, "Internal Server Error")
199
val BadGateway = HttpStatusCode(502, "Bad Gateway")
200
val ServiceUnavailable = HttpStatusCode(503, "Service Unavailable")
201
}
202
}
203
```
204
205
**Usage Examples:**
206
207
```kotlin
208
val response = client.get("https://api.example.com/users/123")
209
210
when {
211
response.status.isSuccess() -> {
212
val user = response.bodyAsText()
213
println("User found: $user")
214
}
215
response.status == HttpStatusCode.NotFound -> {
216
println("User not found")
217
}
218
response.status.isClientError() -> {
219
println("Client error: ${response.status}")
220
}
221
response.status.isServerError() -> {
222
println("Server error: ${response.status}")
223
}
224
}
225
```
226
227
### Header Access
228
229
Access and manipulate response headers.
230
231
```kotlin { .api }
232
/**
233
* Response headers interface
234
*/
235
interface Headers {
236
/** Get header value */
237
operator fun get(name: String): String?
238
239
/** Get all values for header */
240
fun getAll(name: String): List<String>?
241
242
/** Check if header exists */
243
fun contains(name: String): Boolean
244
245
/** Check if header contains specific value */
246
fun contains(name: String, value: String): Boolean
247
248
/** Get all header names */
249
fun names(): Set<String>
250
251
/** Check if headers are empty */
252
fun isEmpty(): Boolean
253
}
254
```
255
256
**Usage Examples:**
257
258
```kotlin
259
val response = client.get("https://api.example.com/data")
260
261
// Access specific headers
262
val contentType = response.headers["Content-Type"]
263
val contentLength = response.headers["Content-Length"]?.toLongOrNull()
264
val lastModified = response.headers["Last-Modified"]
265
266
// Check for header existence
267
if (response.headers.contains("ETag")) {
268
val etag = response.headers["ETag"]
269
println("ETag: $etag")
270
}
271
272
// Get all values for a header (e.g., Set-Cookie)
273
val cookies = response.headers.getAll("Set-Cookie")
274
cookies?.forEach { cookie ->
275
println("Cookie: $cookie")
276
}
277
278
// Iterate all headers
279
response.headers.names().forEach { headerName ->
280
val values = response.headers.getAll(headerName)
281
println("$headerName: ${values?.joinToString(", ")}")
282
}
283
```
284
285
### Response Validation
286
287
Validate responses and handle errors.
288
289
```kotlin { .api }
290
/**
291
* Exception thrown for client error responses (4xx)
292
*/
293
class ClientRequestException(
294
val response: HttpResponse,
295
val cachedResponseText: String
296
) : ResponseException(response, cachedResponseText)
297
298
/**
299
* Exception thrown for server error responses (5xx)
300
*/
301
class ServerResponseException(
302
val response: HttpResponse,
303
val cachedResponseText: String
304
) : ResponseException(response, cachedResponseText)
305
306
/**
307
* Exception thrown for redirect responses when followRedirects is false
308
*/
309
class RedirectResponseException(
310
val response: HttpResponse,
311
val cachedResponseText: String
312
) : ResponseException(response, cachedResponseText)
313
314
/**
315
* Base exception for HTTP response errors
316
*/
317
abstract class ResponseException(
318
val response: HttpResponse,
319
val cachedResponseText: String
320
) : IllegalStateException("Bad response: ${response.status}")
321
```
322
323
**Usage Examples:**
324
325
```kotlin
326
try {
327
val response = client.get("https://api.example.com/protected-resource") {
328
// Client configured with expectSuccess = true (default)
329
}
330
val data = response.bodyAsText()
331
println(data)
332
} catch (e: ClientRequestException) {
333
when (e.response.status) {
334
HttpStatusCode.Unauthorized -> println("Authentication required")
335
HttpStatusCode.Forbidden -> println("Access denied")
336
HttpStatusCode.NotFound -> println("Resource not found")
337
else -> println("Client error: ${e.response.status}")
338
}
339
} catch (e: ServerResponseException) {
340
println("Server error: ${e.response.status}")
341
} catch (e: RedirectResponseException) {
342
println("Redirect not followed: ${e.response.status}")
343
}
344
345
// Or handle manually with expectSuccess = false
346
val client = HttpClient {
347
expectSuccess = false
348
}
349
350
val response = client.get("https://api.example.com/resource")
351
if (response.status.isSuccess()) {
352
val data = response.bodyAsText()
353
// Process successful response
354
} else {
355
val errorBody = response.bodyAsText()
356
// Handle error response
357
}
358
```
359
360
### Response Extensions
361
362
Utility extensions for common response operations.
363
364
```kotlin { .api }
365
/**
366
* Safely read response body as text with error handling
367
*/
368
suspend fun HttpResponse.bodyAsTextOrNull(): String? = try {
369
bodyAsText()
370
} catch (e: Exception) {
371
null
372
}
373
374
/**
375
* Read response body with fallback
376
*/
377
suspend fun HttpResponse.bodyAsTextOrElse(
378
fallback: suspend () -> String
379
): String = try {
380
bodyAsText()
381
} catch (e: Exception) {
382
fallback()
383
}
384
385
/**
386
* Check if response has specific content type
387
*/
388
fun HttpResponse.hasContentType(contentType: ContentType): Boolean {
389
val responseContentType = headers[HttpHeaders.ContentType]
390
return responseContentType?.contains(contentType.toString()) == true
391
}
392
```
393
394
## Types
395
396
```kotlin { .api }
397
// Response types
398
data class HttpResponseData(
399
val statusCode: HttpStatusCode,
400
val requestTime: GMTDate,
401
val headers: Headers,
402
val version: HttpProtocolVersion,
403
val body: Any,
404
val callContext: CoroutineContext
405
)
406
407
// Protocol version
408
class HttpProtocolVersion(
409
val name: String,
410
val major: Int,
411
val minor: Int
412
) {
413
companion object {
414
val HTTP_1_0 = HttpProtocolVersion("HTTP", 1, 0)
415
val HTTP_1_1 = HttpProtocolVersion("HTTP", 1, 1)
416
val HTTP_2_0 = HttpProtocolVersion("HTTP", 2, 0)
417
val SPDY_3 = HttpProtocolVersion("SPDY", 3, 1)
418
val QUIC = HttpProtocolVersion("QUIC", 1, 0)
419
}
420
}
421
422
// Time types
423
class GMTDate(
424
val timestamp: Long
425
) {
426
fun toJvmDate(): java.util.Date
427
override fun toString(): String
428
}
429
430
// Type information for deserialization
431
class TypeInfo(
432
val type: KClass<*>,
433
val reifiedType: Type,
434
val kotlinType: KType? = null
435
)
436
437
// Exception types
438
sealed class HttpRequestException(
439
message: String,
440
cause: Throwable? = null
441
) : Exception(message, cause)
442
443
class DoubleReceiveException(
444
call: HttpClientCall
445
) : IllegalStateException(
446
"Response has already been received for call: $call"
447
)
448
```