or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

builtin-plugins.mdcaching.mdcookies.mdengine-configuration.mdforms.mdhttp-client.mdindex.mdplugin-system.mdrequest-building.mdresponse-handling.mdresponse-observation.mdutilities.mdwebsockets.md

response-handling.mddocs/

0

# Response Handling

1

2

The response handling API provides comprehensive functionality for accessing response data, headers, status information, and converting response bodies to various types with streaming support.

3

4

## Core Types

5

6

```kotlin { .api }

7

abstract class HttpResponse : HttpMessage, CoroutineScope {

8

public abstract val call: HttpClientCall

9

public abstract val status: HttpStatusCode

10

public abstract val version: HttpProtocolVersion

11

public abstract val requestTime: GMTDate

12

public abstract val responseTime: GMTDate

13

14

override fun toString(): String

15

}

16

17

class HttpStatement(

18

private val builder: HttpRequestBuilder,

19

internal val client: HttpClient

20

) {

21

suspend fun execute(): HttpResponse

22

suspend fun <T> execute(block: suspend (response: HttpResponse) -> T): T

23

suspend inline fun <reified T> body(): T

24

}

25

26

// Extension properties and functions for HttpResponse

27

val HttpResponse.request: HttpRequest

28

29

// Response body reading extensions

30

suspend fun HttpResponse.bodyAsText(fallbackCharset: Charset = Charsets.UTF_8): String

31

suspend fun HttpResponse.bodyAsChannel(): ByteReadChannel

32

suspend fun HttpResponse.bodyAsBytes(): ByteArray

33

```

34

35

## Response Properties

36

37

### Status Information

38

```kotlin

39

val response = client.get("https://api.example.com/users")

40

41

println("Status: ${response.status}") // Status: 200 OK

42

println("Status code: ${response.status.value}") // Status code: 200

43

println("Status description: ${response.status.description}") // Status description: OK

44

45

// Status checks

46

if (response.status.isSuccess()) {

47

println("Request succeeded")

48

}

49

50

when (response.status.value) {

51

200 -> println("OK")

52

404 -> println("Not Found")

53

500 -> println("Server Error")

54

}

55

```

56

57

### Headers Access

58

```kotlin

59

val response = client.get("https://api.example.com/users")

60

61

// Individual headers

62

val contentType = response.headers["Content-Type"]

63

val contentLength = response.headers["Content-Length"]?.toLongOrNull()

64

val server = response.headers["Server"]

65

66

// All headers

67

response.headers.forEach { name, values ->

68

println("$name: ${values.joinToString(", ")}")

69

}

70

71

// Typed header access

72

val etag = response.headers[HttpHeaders.ETag]

73

val lastModified = response.headers[HttpHeaders.LastModified]

74

```

75

76

### Timing Information

77

```kotlin

78

val response = client.get("https://api.example.com/users")

79

80

println("Request time: ${response.requestTime}")

81

println("Response time: ${response.responseTime}")

82

83

val duration = response.responseTime.timestamp - response.requestTime.timestamp

84

println("Request took: ${duration}ms")

85

```

86

87

### Protocol Information

88

```kotlin

89

val response = client.get("https://api.example.com/users")

90

91

println("HTTP version: ${response.version}") // HTTP/1.1, HTTP/2.0, etc.

92

```

93

94

## Reading Response Body

95

96

### Text Content

97

```kotlin { .api }

98

suspend fun HttpResponse.bodyAsText(

99

charset: Charset = Charsets.UTF_8

100

): String

101

102

suspend fun HttpResponse.bodyAsText(

103

fallbackCharset: Charset = Charsets.UTF_8

104

): String

105

```

106

107

```kotlin

108

val response = client.get("https://api.example.com/users")

109

val text = response.bodyAsText()

110

println(text)

111

112

// With specific charset

113

val textUtf8 = response.bodyAsText(Charsets.UTF_8)

114

```

115

116

### Binary Content

117

```kotlin { .api }

118

suspend fun HttpResponse.bodyAsBytes(): ByteArray

119

```

120

121

```kotlin

122

val response = client.get("https://api.example.com/image.jpg")

123

val bytes = response.bodyAsBytes()

124

File("downloaded-image.jpg").writeBytes(bytes)

125

```

126

127

### Channel Content

128

```kotlin { .api }

129

fun HttpResponse.bodyAsChannel(): ByteReadChannel

130

```

131

132

```kotlin

133

val response = client.get("https://api.example.com/large-file")

134

val channel = response.bodyAsChannel()

135

136

// Stream processing

137

while (!channel.isClosedForRead) {

138

val packet = channel.readRemaining(8192)

139

// Process packet

140

}

141

```

142

143

### Deserialized Content

144

With appropriate serialization plugins (like kotlinx.serialization):

145

146

```kotlin

147

@Serializable

148

data class User(val id: Int, val name: String, val email: String)

149

150

val response = client.get("https://api.example.com/users/123")

151

val user = response.body<User>()

152

println("User: ${user.name}")

153

```

154

155

## Response Streaming

156

157

### Streaming Large Responses

158

```kotlin

159

val response = client.get("https://api.example.com/large-dataset")

160

val channel = response.bodyAsChannel()

161

162

val outputFile = File("dataset.json")

163

outputFile.outputStream().use { output ->

164

while (!channel.isClosedForRead) {

165

val packet = channel.readRemaining(8192)

166

output.write(packet.readBytes())

167

}

168

}

169

```

170

171

### Progress Monitoring

172

With the BodyProgress plugin:

173

174

```kotlin

175

client.get("https://api.example.com/large-file") {

176

onDownload { bytesSentTotal, contentLength ->

177

val progress = (bytesSentTotal * 100 / contentLength).toInt()

178

println("Download progress: $progress%")

179

}

180

}.bodyAsBytes()

181

```

182

183

## Statement Execution

184

185

### Basic Statement Execution

186

```kotlin

187

val statement = client.prepareGet("https://api.example.com/users")

188

val response = statement.execute()

189

val text = response.bodyAsText()

190

```

191

192

### Statement with Response Processing

193

```kotlin

194

val statement = client.prepareGet("https://api.example.com/users")

195

196

val users = statement.execute { response ->

197

if (response.status.isSuccess()) {

198

response.bodyAsText()

199

} else {

200

throw RuntimeException("Request failed: ${response.status}")

201

}

202

}

203

```

204

205

### Reusable Statements

206

```kotlin

207

val userStatement = client.prepareGet {

208

url("https://api.example.com/users")

209

header("Authorization", "Bearer $token")

210

}

211

212

// Use multiple times

213

val page1 = userStatement.execute {

214

parameter("page", 1)

215

bodyAsText()

216

}

217

218

val page2 = userStatement.execute {

219

parameter("page", 2)

220

bodyAsText()

221

}

222

```

223

224

## Error Response Handling

225

226

### Status Code Checking

227

```kotlin

228

val response = client.get("https://api.example.com/users/999")

229

230

when {

231

response.status.isSuccess() -> {

232

val user = response.bodyAsText()

233

println("User: $user")

234

}

235

response.status.value == 404 -> {

236

println("User not found")

237

}

238

response.status.value in 400..499 -> {

239

val errorBody = response.bodyAsText()

240

println("Client error: $errorBody")

241

}

242

response.status.value in 500..599 -> {

243

println("Server error: ${response.status}")

244

}

245

}

246

```

247

248

### Exception Handling with Call Validator

249

```kotlin

250

client.get("https://api.example.com/users") {

251

// With HttpCallValidator plugin, non-success responses throw exceptions

252

}

253

```

254

255

## Response Validation

256

257

### Content Type Validation

258

```kotlin

259

val response = client.get("https://api.example.com/users")

260

val contentType = response.headers[HttpHeaders.ContentType]

261

262

if (contentType?.startsWith("application/json") == true) {

263

val json = response.bodyAsText()

264

// Process JSON

265

} else {

266

println("Unexpected content type: $contentType")

267

}

268

```

269

270

### Content Length Validation

271

```kotlin

272

val response = client.get("https://api.example.com/file")

273

val contentLength = response.headers[HttpHeaders.ContentLength]?.toLongOrNull()

274

val actualBytes = response.bodyAsBytes()

275

276

if (contentLength != null && actualBytes.size.toLong() != contentLength) {

277

println("Content length mismatch!")

278

}

279

```

280

281

## Response Caching

282

283

### Cache Headers

284

```kotlin

285

val response = client.get("https://api.example.com/users")

286

287

val cacheControl = response.headers[HttpHeaders.CacheControl]

288

val etag = response.headers[HttpHeaders.ETag]

289

val expires = response.headers[HttpHeaders.Expires]

290

val lastModified = response.headers[HttpHeaders.LastModified]

291

292

println("Cache Control: $cacheControl")

293

println("ETag: $etag")

294

```

295

296

### Conditional Requests

297

```kotlin

298

// Using If-None-Match with ETag

299

val etag = "W/\"abc123\""

300

val response = client.get("https://api.example.com/users") {

301

header(HttpHeaders.IfNoneMatch, etag)

302

}

303

304

if (response.status.value == 304) {

305

println("Content not modified")

306

} else {

307

val newContent = response.bodyAsText()

308

val newEtag = response.headers[HttpHeaders.ETag]

309

}

310

```

311

312

## Response Context

313

314

### Coroutine Context

315

```kotlin

316

val response = client.get("https://api.example.com/users")

317

318

// Access coroutine context

319

val job = response.coroutineContext[Job]

320

val dispatcher = response.coroutineContext[CoroutineDispatcher]

321

322

// Use response as coroutine scope

323

response.launch {

324

// Background processing of response

325

}

326

```

327

328

### Call Information

329

```kotlin

330

val response = client.get("https://api.example.com/users")

331

332

println("Request URL: ${response.call.request.url}")

333

println("Request method: ${response.call.request.method}")

334

println("Response status: ${response.call.response.status}")

335

```