or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-management.mdengine-system.mdform-data.mdhttp-utilities.mdindex.mdplugin-system.mdrequest-operations.mdresponse-handling.mdrouting-system.mdserver-framework.md

response-handling.mddocs/

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

```