0
# HTTP Response Handling
1
2
Response processing, body consumption, and content type handling for working with HTTP responses and consuming response data.
3
4
## Capabilities
5
6
### HttpResponse Class
7
8
Abstract base class representing an HTTP response with status, headers, and content.
9
10
```kotlin { .api }
11
/**
12
* Abstract HTTP response representing the server's response to a request
13
*/
14
abstract class HttpResponse : HttpMessage, CoroutineScope {
15
/** The call that generated this response */
16
abstract val call: HttpClientCall
17
18
/** HTTP status code and description */
19
abstract val status: HttpStatusCode
20
21
/** HTTP protocol version */
22
abstract val version: HttpProtocolVersion
23
24
/** Timestamp when the request was sent */
25
abstract val requestTime: GMTDate
26
27
/** Timestamp when the response was received */
28
abstract val responseTime: GMTDate
29
30
/** Raw response content as a byte channel */
31
abstract val rawContent: ByteReadChannel
32
}
33
```
34
35
### Response Body Consumption
36
37
Extension functions for consuming response body content in various formats.
38
39
```kotlin { .api }
40
/**
41
* Get response body as text with optional charset fallback
42
* @param fallbackCharset Charset to use if not specified in response
43
* @return Response body as string
44
*/
45
suspend fun HttpResponse.bodyAsText(
46
fallbackCharset: Charset = Charsets.UTF_8
47
): String
48
49
/**
50
* Get response body as raw byte channel
51
* @return ByteReadChannel for streaming response content
52
*/
53
suspend fun HttpResponse.bodyAsChannel(): ByteReadChannel
54
55
/**
56
* Get response body as byte array
57
* @return Response body as ByteArray
58
*/
59
suspend fun HttpResponse.bodyAsBytes(): ByteArray
60
61
/**
62
* Get response body as typed object using content negotiation
63
* @return Deserialized response body of type T
64
*/
65
suspend inline fun <reified T> HttpResponse.body(): T
66
67
/**
68
* Get response body as typed object with explicit type information
69
* @param typeInfo Type information for deserialization
70
* @return Deserialized response body
71
*/
72
suspend fun <T> HttpResponse.body(typeInfo: TypeInfo): T
73
```
74
75
**Usage Examples:**
76
77
```kotlin
78
import io.ktor.client.*
79
import io.ktor.client.request.*
80
import io.ktor.client.statement.*
81
82
val client = HttpClient()
83
84
// Get response as text
85
val response = client.get("https://api.example.com/users")
86
val textContent = response.bodyAsText()
87
88
// Get response as bytes
89
val imageResponse = client.get("https://example.com/image.png")
90
val imageBytes = imageResponse.bodyAsBytes()
91
92
// Get response as typed object (requires serialization plugin)
93
val usersResponse = client.get("https://api.example.com/users")
94
val users: List<User> = usersResponse.body()
95
96
// Stream response content
97
val streamResponse = client.get("https://example.com/large-file")
98
val channel = streamResponse.bodyAsChannel()
99
while (!channel.isClosedForRead) {
100
val data = channel.readBuffer(8192)
101
// Process data chunk
102
}
103
```
104
105
### HttpStatement Class
106
107
Deferred HTTP request execution that allows for custom response handling.
108
109
```kotlin { .api }
110
/**
111
* Represents a prepared HTTP request that can be executed multiple times
112
*/
113
class HttpStatement(
114
private val builder: HttpRequestBuilder,
115
internal val client: HttpClient
116
) {
117
/**
118
* Execute the request with custom response handling
119
* @param block Response handling block
120
* @return Result of the response handling block
121
*/
122
suspend fun <T> execute(
123
block: suspend (response: HttpResponse) -> T
124
): T
125
126
/**
127
* Execute the request and return the response
128
* @return HttpResponse from the server
129
*/
130
suspend fun execute(): HttpResponse
131
132
/**
133
* Execute the request and get typed response body
134
* @return Deserialized response body of type T
135
*/
136
suspend inline fun <reified T> body(): T
137
138
/**
139
* Execute the request and get typed response body with custom handling
140
* @param block Custom handling block for the typed response
141
* @return Result of the handling block
142
*/
143
suspend inline fun <reified T, R> body(
144
crossinline block: suspend (response: T) -> R
145
): R
146
}
147
```
148
149
**Usage Examples:**
150
151
```kotlin
152
import io.ktor.client.*
153
import io.ktor.client.request.*
154
import io.ktor.client.statement.*
155
156
val client = HttpClient()
157
158
// Prepare statement for later execution
159
val statement = client.prepareGet("https://api.example.com/users")
160
161
// Execute with custom response handling
162
val result = statement.execute { response ->
163
when {
164
response.status.isSuccess() -> response.bodyAsText()
165
response.status.value == 404 -> "Not found"
166
else -> "Error: ${response.status}"
167
}
168
}
169
170
// Execute and get typed body
171
val users: List<User> = statement.body()
172
173
// Execute with typed body and custom handling
174
val userCount = statement.body<List<User>> { users ->
175
users.size
176
}
177
```
178
179
### HttpClientCall Class
180
181
Represents a complete HTTP request-response cycle with access to both request and response.
182
183
```kotlin { .api }
184
/**
185
* Represents a complete HTTP call including request and response
186
*/
187
open class HttpClientCall(
188
val client: HttpClient
189
) : CoroutineScope {
190
/** Attributes for storing call metadata */
191
val attributes: Attributes
192
193
/** The request that was sent */
194
lateinit var request: HttpRequest
195
196
/** The response that was received */
197
lateinit var response: HttpResponse
198
199
/**
200
* Get response body with nullable handling
201
* @param info Type information for deserialization
202
* @return Deserialized body or null
203
*/
204
suspend fun bodyNullable(info: TypeInfo): Any?
205
206
/**
207
* Get response body with type information
208
* @param info Type information for deserialization
209
* @return Deserialized body
210
*/
211
suspend fun body(info: TypeInfo): Any
212
}
213
```
214
215
### Call Body Extension Functions
216
217
Extension functions for getting typed response bodies from HttpClientCall instances.
218
219
```kotlin { .api }
220
/**
221
* Get typed response body from call
222
* @return Deserialized response body of type T
223
*/
224
suspend inline fun <reified T> HttpClientCall.body(): T
225
226
/**
227
* Get typed response body from response
228
* @return Deserialized response body of type T
229
*/
230
suspend inline fun <reified T> HttpResponse.body(): T
231
```
232
233
**Usage Examples:**
234
235
```kotlin
236
import io.ktor.client.*
237
import io.ktor.client.call.*
238
import io.ktor.client.request.*
239
240
val client = HttpClient()
241
242
val response = client.get("https://api.example.com/user/123")
243
244
// Access the call
245
val call = response.call
246
247
// Get typed body from call
248
val user: User = call.body()
249
250
// Access request information
251
println("Request URL: ${call.request.url}")
252
println("Response Status: ${call.response.status}")
253
```
254
255
### Response Utility Functions
256
257
Utility functions for working with response content types and character sets.
258
259
```kotlin { .api }
260
/**
261
* Get the charset from response Content-Type header
262
* @return Charset if specified, null otherwise
263
*/
264
fun HttpResponse.charset(): Charset?
265
266
/**
267
* Get the content type from response headers
268
* @return ContentType if specified, null otherwise
269
*/
270
fun HttpMessage.contentType(): ContentType?
271
```
272
273
**Usage Examples:**
274
275
```kotlin
276
import io.ktor.client.*
277
import io.ktor.client.request.*
278
import io.ktor.client.statement.*
279
import io.ktor.http.*
280
281
val response = client.get("https://api.example.com/data")
282
283
// Check content type
284
val contentType = response.contentType()
285
when {
286
contentType?.match(ContentType.Application.Json) == true -> {
287
val jsonData = response.bodyAsText()
288
// Process JSON
289
}
290
contentType?.match(ContentType.Text.Plain) == true -> {
291
val textData = response.bodyAsText()
292
// Process plain text
293
}
294
}
295
296
// Use specific charset if available
297
val charset = response.charset() ?: Charsets.UTF_8
298
val text = response.bodyAsText(charset)
299
```
300
301
## Exception Handling
302
303
Response-related exceptions that may be thrown during response processing:
304
305
```kotlin { .api }
306
/**
307
* Thrown when attempting to receive response body multiple times
308
*/
309
class DoubleReceiveException(call: HttpClientCall) : IllegalStateException()
310
311
/**
312
* Thrown when no type transformer is found for response deserialization
313
*/
314
class NoTransformationFoundException(
315
response: HttpResponse,
316
from: KClass<*>,
317
to: KClass<*>
318
) : UnsupportedOperationException()
319
320
/**
321
* Exception in response receive pipeline
322
*/
323
class ReceivePipelineException(
324
request: HttpClientCall,
325
info: TypeInfo,
326
override val cause: Throwable
327
) : IllegalStateException()
328
329
/**
330
* Base class for HTTP response exceptions
331
*/
332
open class ResponseException(
333
response: HttpResponse,
334
cachedResponseText: String
335
) : IllegalStateException()
336
337
/**
338
* Exception for HTTP redirect responses
339
*/
340
class RedirectResponseException(
341
response: HttpResponse,
342
cachedResponseText: String
343
) : ResponseException(response, cachedResponseText)
344
345
/**
346
* Exception for HTTP client error responses (4xx)
347
*/
348
class ClientRequestException(
349
response: HttpResponse,
350
cachedResponseText: String
351
) : ResponseException(response, cachedResponseText)
352
353
/**
354
* Exception for HTTP server error responses (5xx)
355
*/
356
class ServerResponseException(
357
response: HttpResponse,
358
cachedResponseText: String
359
) : ResponseException(response, cachedResponseText)
360
```
361
362
**Usage Examples:**
363
364
```kotlin
365
import io.ktor.client.*
366
import io.ktor.client.request.*
367
import io.ktor.client.statement.*
368
import io.ktor.client.plugins.*
369
370
val client = HttpClient {
371
// Configure to not throw on non-2xx responses
372
expectSuccess = false
373
}
374
375
try {
376
val response = client.get("https://api.example.com/might-fail")
377
val content = response.bodyAsText()
378
379
// Try to read body again - throws DoubleReceiveException
380
val duplicateContent = response.bodyAsText() // ERROR!
381
382
} catch (e: ClientRequestException) {
383
// Handle 4xx responses
384
println("Client error: ${e.response.status}")
385
} catch (e: ServerResponseException) {
386
// Handle 5xx responses
387
println("Server error: ${e.response.status}")
388
} catch (e: DoubleReceiveException) {
389
// Handle duplicate receive attempts
390
println("Response body already consumed")
391
}
392
```
393
394
## Types
395
396
Response handling related types:
397
398
```kotlin { .api }
399
/**
400
* HTTP status code with value and description
401
*/
402
data class HttpStatusCode(val value: Int, val description: String) {
403
companion object {
404
val Continue = HttpStatusCode(100, "Continue")
405
val OK = HttpStatusCode(200, "OK")
406
val Created = HttpStatusCode(201, "Created")
407
val NoContent = HttpStatusCode(204, "No Content")
408
val NotModified = HttpStatusCode(304, "Not Modified")
409
val BadRequest = HttpStatusCode(400, "Bad Request")
410
val Unauthorized = HttpStatusCode(401, "Unauthorized")
411
val Forbidden = HttpStatusCode(403, "Forbidden")
412
val NotFound = HttpStatusCode(404, "Not Found")
413
val InternalServerError = HttpStatusCode(500, "Internal Server Error")
414
val BadGateway = HttpStatusCode(502, "Bad Gateway")
415
}
416
417
fun isSuccess(): Boolean = value in 200..299
418
fun isInformational(): Boolean = value in 100..199
419
fun isRedirect(): Boolean = value in 300..399
420
fun isClientError(): Boolean = value in 400..499
421
fun isServerError(): Boolean = value in 500..599
422
}
423
424
/**
425
* HTTP protocol version
426
*/
427
data class HttpProtocolVersion(
428
val name: String,
429
val major: Int,
430
val minor: Int
431
) {
432
companion object {
433
val HTTP_1_0 = HttpProtocolVersion("HTTP", 1, 0)
434
val HTTP_1_1 = HttpProtocolVersion("HTTP", 1, 1)
435
val HTTP_2_0 = HttpProtocolVersion("HTTP", 2, 0)
436
val SPDY_3 = HttpProtocolVersion("SPDY", 3, 0)
437
val QUIC = HttpProtocolVersion("QUIC", 1, 0)
438
}
439
}
440
441
/**
442
* Type information for response deserialization
443
*/
444
interface TypeInfo {
445
val type: KClass<*>
446
val reifiedType: KType
447
val kotlinType: KType?
448
}
449
450
/**
451
* GMT date representation
452
*/
453
data class GMTDate(val timestamp: Long) {
454
companion object {
455
fun now(): GMTDate
456
}
457
}
458
```