or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-configuration.mdcontent-handling.mdengine-architecture.mdevents-monitoring.mdhttp-statement.mdindex.mdplugin-system.mdrequest-building.mdresponse-handling.mdwebsocket-support.md

response-handling.mddocs/

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

```