or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdencoders.mdindex.mdrequest-compression.mdresponse-decompression.md

response-decompression.mddocs/

0

# Response Decompression

1

2

Automatically decompress incoming HTTP response bodies based on Content-Encoding headers with transparent processing and multi-layer decompression support.

3

4

## Response Decompression API

5

6

```kotlin { .api }

7

/**

8

* List of ContentEncoder names that were used to decode response body.

9

*/

10

val HttpResponse.appliedDecoders: List<String>

11

```

12

13

## Automatic Response Decompression

14

15

### Basic Decompression

16

17

```kotlin

18

import io.ktor.client.*

19

import io.ktor.client.plugins.compression.*

20

import io.ktor.client.statement.*

21

22

val client = HttpClient {

23

install(ContentEncoding) {

24

// Default mode: DecompressResponse

25

gzip()

26

deflate()

27

identity()

28

}

29

}

30

31

// Automatic decompression based on Content-Encoding header

32

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

33

val content = response.body<String>() // Automatically decompressed

34

35

// Check which decoders were applied

36

val usedDecoders = response.appliedDecoders

37

println("Applied decoders: $usedDecoders") // e.g., ["gzip"]

38

```

39

40

### Accept-Encoding Header Management

41

42

```kotlin

43

// Client automatically sets Accept-Encoding header

44

val response = client.get("/compressed-endpoint")

45

46

// Request includes: Accept-Encoding: gzip,deflate,identity

47

// Server responds with: Content-Encoding: gzip

48

// Response body is automatically decompressed

49

```

50

51

## Multi-Layer Decompression

52

53

### Decompression Order

54

55

```kotlin

56

// Server response with: Content-Encoding: gzip, deflate

57

val response = client.get("/multi-compressed")

58

59

// Decompression applied in reverse order:

60

// 1. gzip decompression applied first

61

// 2. deflate decompression applied second

62

// 3. Original content returned

63

64

val decoders = response.appliedDecoders

65

println(decoders) // ["gzip", "deflate"] - in application order

66

```

67

68

### Complex Encoding Chains

69

70

```kotlin

71

// Server applies: deflate → gzip → brotli (if supported)

72

// Response header: Content-Encoding: deflate, gzip, br

73

74

val response = client.get("/complex-compression")

75

val content = response.body<String>()

76

77

// Decompression chain: br → gzip → deflate (reverse order)

78

val appliedDecoders = response.appliedDecoders

79

println("Decompression chain: $appliedDecoders")

80

```

81

82

## Decompression Modes

83

84

### DecompressResponse Mode (Default)

85

86

```kotlin

87

val client = HttpClient {

88

install(ContentEncoding) {

89

mode = ContentEncodingConfig.Mode.DecompressResponse

90

gzip()

91

deflate()

92

}

93

}

94

95

// Only decompresses responses, never compresses requests

96

val response = client.get("/data")

97

val decompressed = response.body<String>()

98

```

99

100

### All Mode (Bidirectional)

101

102

```kotlin

103

val client = HttpClient {

104

install(ContentEncoding) {

105

mode = ContentEncodingConfig.Mode.All

106

gzip()

107

deflate()

108

}

109

}

110

111

// Both compresses requests AND decompresses responses

112

val response = client.post("/upload") {

113

compress("gzip") // Request compression available

114

setBody(data)

115

}

116

val result = response.body<String>() // Response decompression automatic

117

```

118

119

### CompressRequest Mode

120

121

```kotlin

122

val client = HttpClient {

123

install(ContentEncoding) {

124

mode = ContentEncodingConfig.Mode.CompressRequest

125

gzip()

126

}

127

}

128

129

// Only compresses requests, does NOT decompress responses

130

val response = client.get("/data")

131

// Response is NOT automatically decompressed even if Content-Encoding header present

132

```

133

134

## Response Processing Pipeline

135

136

### Header Processing

137

138

```kotlin

139

// Response headers before decompression:

140

// Content-Encoding: gzip

141

// Content-Length: 1234 (compressed size)

142

143

val response = client.get("/compressed")

144

145

// Response headers after decompression:

146

// Content-Encoding: (removed)

147

// Content-Length: (removed - unknown after decompression)

148

// Other headers preserved

149

150

val content = response.body<String>() // Decompressed content

151

```

152

153

### Content-Length Handling

154

155

```kotlin

156

val response = client.get("/gzipped-content")

157

158

// Original compressed length (if available)

159

val originalLength = response.headers[HttpHeaders.ContentLength]?.toLong()

160

161

// Content-Length header removed after decompression

162

val finalLength = response.headers[HttpHeaders.ContentLength] // null

163

164

// Actual decompressed content

165

val content = response.body<String>()

166

val actualLength = content.length

167

```

168

169

## Error Handling

170

171

### Unsupported Encodings

172

173

```kotlin { .api }

174

class UnsupportedContentEncodingException(encoding: String) :

175

IllegalStateException("Content-Encoding: $encoding unsupported.")

176

```

177

178

**Handling Unknown Encodings:**

179

```kotlin

180

try {

181

val response = client.get("/unknown-encoding")

182

val content = response.body<String>()

183

} catch (e: UnsupportedContentEncodingException) {

184

println("Server used unsupported encoding: ${e.message}")

185

186

// Fallback: handle raw compressed content

187

val rawContent = response.body<ByteArray>()

188

// Custom decompression logic here

189

}

190

```

191

192

### Partial Encoding Support

193

194

```kotlin

195

val client = HttpClient {

196

install(ContentEncoding) {

197

gzip() // Supported

198

deflate() // Supported

199

// brotli not configured - will cause UnsupportedContentEncodingException

200

}

201

}

202

203

// Server responds with: Content-Encoding: br, gzip

204

try {

205

val response = client.get("/brotli-compressed")

206

val content = response.body<String>()

207

} catch (e: UnsupportedContentEncodingException) {

208

println("Brotli encoding not supported")

209

}

210

```

211

212

### Decompression Errors

213

214

```kotlin

215

suspend fun robustDownload(url: String): String? {

216

return try {

217

val response = client.get(url)

218

response.body<String>()

219

} catch (e: UnsupportedContentEncodingException) {

220

println("Encoding not supported: ${e.message}")

221

null

222

} catch (e: Exception) {

223

println("Decompression failed: ${e.message}")

224

null

225

}

226

}

227

```

228

229

## Advanced Response Decompression

230

231

### Content Type Awareness

232

233

```kotlin

234

suspend fun downloadWithContentTypeHandling(url: String) {

235

val response = client.get(url)

236

val contentType = response.contentType()

237

238

when (contentType?.contentType) {

239

"text" -> {

240

val text = response.body<String>()

241

println("Decompressed text: ${text.take(100)}...")

242

}

243

"application/json" -> {

244

val json = response.body<JsonObject>()

245

println("Decompressed JSON keys: ${json.keys}")

246

}

247

"application/octet-stream" -> {

248

val bytes = response.body<ByteArray>()

249

println("Decompressed binary data: ${bytes.size} bytes")

250

}

251

}

252

253

println("Applied decoders: ${response.appliedDecoders}")

254

}

255

```

256

257

### Streaming Decompression

258

259

```kotlin

260

import io.ktor.utils.io.*

261

262

suspend fun streamingDecompression(url: String) {

263

val response = client.get(url)

264

val channel = response.body<ByteReadChannel>()

265

266

// Content is decompressed as it's read from the channel

267

val buffer = ByteArray(8192)

268

while (!channel.isClosedForRead) {

269

val read = channel.readAvailable(buffer)

270

if (read > 0) {

271

// Process decompressed data chunk

272

processChunk(buffer, 0, read)

273

}

274

}

275

276

println("Streaming decompression used: ${response.appliedDecoders}")

277

}

278

279

fun processChunk(data: ByteArray, offset: Int, length: Int) {

280

// Process decompressed data chunk

281

println("Processed ${length} decompressed bytes")

282

}

283

```

284

285

### Conditional Decompression

286

287

```kotlin

288

suspend fun conditionalDecompression(url: String, autoDecompress: Boolean) {

289

val client = if (autoDecompress) {

290

HttpClient {

291

install(ContentEncoding) {

292

gzip()

293

deflate()

294

}

295

}

296

} else {

297

HttpClient() // No content encoding plugin

298

}

299

300

val response = client.get(url)

301

302

if (autoDecompress) {

303

val content = response.body<String>() // Automatically decompressed

304

println("Auto-decompressed content: ${content.take(100)}")

305

} else {

306

val rawContent = response.body<ByteArray>() // Raw compressed content

307

println("Raw content size: ${rawContent.size} bytes")

308

309

// Manual decompression if needed

310

val encoding = response.headers[HttpHeaders.ContentEncoding]

311

if (encoding == "gzip") {

312

val decompressed = manualGzipDecompression(rawContent)

313

println("Manually decompressed: ${decompressed.take(100)}")

314

}

315

}

316

}

317

318

fun manualGzipDecompression(compressed: ByteArray): String {

319

// Implement manual gzip decompression

320

return "Manually decompressed content"

321

}

322

```

323

324

## Response Validation

325

326

### Decoder Chain Validation

327

328

```kotlin

329

suspend fun validateDecompression(url: String, expectedEncodings: List<String>) {

330

val response = client.get(url)

331

val content = response.body<String>()

332

val appliedDecoders = response.appliedDecoders

333

334

if (appliedDecoders == expectedEncodings) {

335

println("✓ Expected decompression chain applied: $appliedDecoders")

336

} else {

337

println("✗ Unexpected decompression chain:")

338

println(" Expected: $expectedEncodings")

339

println(" Actual: $appliedDecoders")

340

}

341

}

342

```

343

344

### Content Integrity Verification

345

346

```kotlin

347

suspend fun verifyDecompressedContent(url: String, expectedChecksum: String) {

348

val response = client.get(url)

349

val content = response.body<ByteArray>()

350

val actualChecksum = content.sha256()

351

352

if (actualChecksum == expectedChecksum) {

353

println("✓ Content integrity verified after decompression")

354

println(" Decoders used: ${response.appliedDecoders}")

355

} else {

356

println("✗ Content integrity check failed")

357

println(" Expected: $expectedChecksum")

358

println(" Actual: $actualChecksum")

359

}

360

}

361

362

fun ByteArray.sha256(): String {

363

// Implement SHA-256 checksum calculation

364

return "calculated-checksum"

365

}

366

```

367

368

## Performance Optimization

369

370

### Decoder Selection Strategy

371

372

```kotlin

373

val client = HttpClient {

374

install(ContentEncoding) {

375

// Optimize for decompression speed

376

deflate(1.0f) // Faster decompression

377

gzip(0.8f) // Slower but better compression

378

identity(0.1f) // Fallback

379

}

380

}

381

```

382

383

### Memory-Efficient Decompression

384

385

```kotlin

386

suspend fun memoryEfficientDownload(url: String): String {

387

val response = client.get(url)

388

389

// Stream-based decompression to minimize memory usage

390

val channel = response.body<ByteReadChannel>()

391

val result = StringBuilder()

392

val buffer = ByteArray(4096)

393

394

while (!channel.isClosedForRead) {

395

val read = channel.readAvailable(buffer)

396

if (read > 0) {

397

result.append(String(buffer, 0, read))

398

}

399

}

400

401

println("Memory-efficient decompression: ${response.appliedDecoders}")

402

return result.toString()

403

}

404

```

405

406

## Integration Examples

407

408

### API Client with Response Validation

409

410

```kotlin

411

class ApiClient(private val client: HttpClient) {

412

413

suspend fun fetchData(endpoint: String): ApiResponse? {

414

return try {

415

val response = client.get(endpoint)

416

val data = response.body<ApiResponse>()

417

418

// Log decompression info

419

if (response.appliedDecoders.isNotEmpty()) {

420

println("Response decompressed with: ${response.appliedDecoders}")

421

}

422

423

data

424

} catch (e: UnsupportedContentEncodingException) {

425

println("Unsupported encoding: ${e.message}")

426

null

427

}

428

}

429

}

430

431

data class ApiResponse(val data: String, val status: String)

432

```

433

434

### File Download Service

435

436

```kotlin

437

class DownloadService(private val client: HttpClient) {

438

439

suspend fun downloadFile(url: String): ByteArray? {

440

return try {

441

val response = client.get(url)

442

val content = response.body<ByteArray>()

443

444

println("Downloaded ${content.size} bytes")

445

if (response.appliedDecoders.isNotEmpty()) {

446

println("Decompressed using: ${response.appliedDecoders}")

447

}

448

449

content

450

} catch (e: Exception) {

451

println("Download failed: ${e.message}")

452

null

453

}

454

}

455

456

suspend fun downloadText(url: String): String? {

457

return try {

458

val response = client.get(url)

459

val text = response.body<String>()

460

461

val decoders = response.appliedDecoders

462

if (decoders.isNotEmpty()) {

463

println("Text decompressed with: $decoders")

464

}

465

466

text

467

} catch (e: Exception) {

468

println("Text download failed: ${e.message}")

469

null

470

}

471

}

472

}

473

```

474

475

## Complete Example

476

477

```kotlin

478

import io.ktor.client.*

479

import io.ktor.client.plugins.compression.*

480

import io.ktor.client.request.*

481

import io.ktor.client.statement.*

482

483

suspend fun main() {

484

val client = HttpClient {

485

install(ContentEncoding) {

486

mode = ContentEncodingConfig.Mode.DecompressResponse

487

gzip()

488

deflate()

489

identity()

490

}

491

}

492

493

// Simple automatic decompression

494

val response1 = client.get("https://httpbin.org/gzip")

495

val content1 = response1.body<String>()

496

println("Gzip decompressed content: ${content1.take(100)}")

497

println("Applied decoders: ${response1.appliedDecoders}")

498

499

// Multi-layer decompression

500

val response2 = client.get("https://example.com/multi-compressed")

501

val content2 = response2.body<String>()

502

println("Multi-layer decompressed: ${content2.take(100)}")

503

println("Decoder chain: ${response2.appliedDecoders}")

504

505

// Error handling for unsupported encodings

506

try {

507

val response3 = client.get("https://example.com/brotli-compressed")

508

val content3 = response3.body<String>()

509

println("Content: $content3")

510

} catch (e: UnsupportedContentEncodingException) {

511

println("Unsupported encoding: ${e.message}")

512

}

513

514

// Streaming decompression for large files

515

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

516

val channel = response4.body<ByteReadChannel>()

517

var totalBytes = 0

518

val buffer = ByteArray(8192)

519

520

while (!channel.isClosedForRead) {

521

val read = channel.readAvailable(buffer)

522

if (read > 0) {

523

totalBytes += read

524

// Process chunk of decompressed data

525

}

526

}

527

528

println("Streamed $totalBytes decompressed bytes")

529

println("Streaming decoders: ${response4.appliedDecoders}")

530

531

client.close()

532

}

533

```