or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-configuration.mdcookie-management.mdengine-configuration.mdform-handling.mdhttp-caching.mdindex.mdplugin-system.mdrequest-building.mdresponse-handling.mdwebsocket-support.md

http-caching.mddocs/

0

# HTTP Caching

1

2

Response caching with configurable storage backends, cache control support, and HTTP-compliant caching behavior for improved performance.

3

4

## Capabilities

5

6

### HttpCache Plugin

7

8

Core plugin for HTTP response caching with public and private storage options.

9

10

```kotlin { .api }

11

/**

12

* HTTP response caching plugin

13

*/

14

class HttpCache internal constructor(

15

private val publicStorage: CacheStorage,

16

private val privateStorage: CacheStorage,

17

private val isSharedClient: Boolean

18

) {

19

/**

20

* Cache configuration

21

*/

22

class Config {

23

/** Whether client is shared between multiple users */

24

var isShared: Boolean = false

25

26

/**

27

* Configure public cache storage

28

*/

29

fun publicStorage(storage: CacheStorage)

30

31

/**

32

* Configure private cache storage

33

*/

34

fun privateStorage(storage: CacheStorage)

35

}

36

37

companion object : HttpClientPlugin<Config, HttpCache> {

38

override val key: AttributeKey<HttpCache> = AttributeKey("HttpCache")

39

40

/** Event fired when response is served from cache */

41

val HttpResponseFromCache: EventDefinition<HttpResponse>

42

}

43

}

44

```

45

46

**Usage Examples:**

47

48

```kotlin

49

import io.ktor.client.plugins.cache.*

50

51

val client = HttpClient {

52

install(HttpCache) {

53

// Configure as shared client

54

isShared = false

55

56

// Use unlimited cache storage

57

publicStorage(CacheStorage.Unlimited())

58

privateStorage(CacheStorage.Unlimited())

59

}

60

61

// Monitor cache hits

62

monitor.subscribe(HttpCache.HttpResponseFromCache) { response ->

63

println("Cache hit for: ${response.request.url}")

64

}

65

}

66

67

// First request - fetches from server and caches

68

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

69

println("First request: ${response1.status}")

70

71

// Second request - served from cache if cacheable

72

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

73

println("Second request: ${response2.status}")

74

```

75

76

### CacheStorage Interface

77

78

Modern cache storage interface for storing and retrieving cached responses.

79

80

```kotlin { .api }

81

/**

82

* Cache storage interface for HTTP responses

83

*/

84

interface CacheStorage {

85

/**

86

* Store cached response data

87

*/

88

suspend fun store(url: Url, data: CachedResponseData)

89

90

/**

91

* Find cached response with vary key matching

92

*/

93

suspend fun find(url: Url, varyKeys: Map<String, String>): CachedResponseData?

94

95

/**

96

* Find all cached responses for URL

97

*/

98

suspend fun findAll(url: Url): Set<CachedResponseData>

99

100

companion object {

101

/** Create unlimited in-memory cache storage */

102

fun Unlimited(): CacheStorage

103

104

/** Disabled cache storage (no-op) */

105

val Disabled: CacheStorage

106

}

107

}

108

```

109

110

### Built-in Cache Storage Implementations

111

112

Pre-built storage implementations for different caching strategies.

113

114

```kotlin { .api }

115

/**

116

* Unlimited in-memory cache storage

117

*/

118

class UnlimitedStorage : CacheStorage {

119

override suspend fun store(url: Url, data: CachedResponseData)

120

override suspend fun find(url: Url, varyKeys: Map<String, String>): CachedResponseData?

121

override suspend fun findAll(url: Url): Set<CachedResponseData>

122

}

123

124

/**

125

* Disabled cache storage (no-op implementation)

126

*/

127

object DisabledStorage : CacheStorage {

128

override suspend fun store(url: Url, data: CachedResponseData) {}

129

override suspend fun find(url: Url, varyKeys: Map<String, String>): CachedResponseData? = null

130

override suspend fun findAll(url: Url): Set<CachedResponseData> = emptySet()

131

}

132

```

133

134

**Usage Examples:**

135

136

```kotlin

137

// Unlimited caching

138

val unlimitedClient = HttpClient {

139

install(HttpCache) {

140

publicStorage(CacheStorage.Unlimited())

141

privateStorage(CacheStorage.Unlimited())

142

}

143

}

144

145

// Disabled caching

146

val noCacheClient = HttpClient {

147

install(HttpCache) {

148

publicStorage(CacheStorage.Disabled)

149

privateStorage(CacheStorage.Disabled)

150

}

151

}

152

153

// Custom cache storage with size limit

154

class LimitedCacheStorage(private val maxEntries: Int) : CacheStorage {

155

private val cache = LinkedHashMap<String, CachedResponseData>()

156

157

override suspend fun store(url: Url, data: CachedResponseData) {

158

val key = url.toString()

159

if (cache.size >= maxEntries && key !in cache) {

160

// Remove oldest entry

161

cache.remove(cache.keys.first())

162

}

163

cache[key] = data

164

}

165

166

override suspend fun find(url: Url, varyKeys: Map<String, String>): CachedResponseData? {

167

return cache[url.toString()]

168

}

169

170

override suspend fun findAll(url: Url): Set<CachedResponseData> {

171

return cache[url.toString()]?.let { setOf(it) } ?: emptySet()

172

}

173

}

174

175

val limitedClient = HttpClient {

176

install(HttpCache) {

177

publicStorage(LimitedCacheStorage(maxEntries = 100))

178

privateStorage(LimitedCacheStorage(maxEntries = 50))

179

}

180

}

181

```

182

183

### CachedResponseData Class

184

185

Data class representing cached HTTP response with metadata.

186

187

```kotlin { .api }

188

/**

189

* Cached response data with HTTP metadata

190

*/

191

data class CachedResponseData(

192

/** Original request URL */

193

val url: Url,

194

195

/** Response status code */

196

val statusCode: HttpStatusCode,

197

198

/** Request timestamp */

199

val requestTime: GMTDate,

200

201

/** Response timestamp */

202

val responseTime: GMTDate,

203

204

/** HTTP protocol version */

205

val version: HttpProtocolVersion,

206

207

/** Cache expiration time */

208

val expires: GMTDate,

209

210

/** Response headers */

211

val headers: Headers,

212

213

/** Vary header matching keys */

214

val varyKeys: Map<String, String>,

215

216

/** Response body content */

217

val body: ByteArray

218

)

219

```

220

221

**Usage Examples:**

222

223

```kotlin

224

// Create cached response data manually

225

val cachedData = CachedResponseData(

226

url = Url("https://api.example.com/data"),

227

statusCode = HttpStatusCode.OK,

228

requestTime = GMTDate(),

229

responseTime = GMTDate(),

230

version = HttpProtocolVersion.HTTP_1_1,

231

expires = GMTDate() + 3600_000, // 1 hour from now

232

headers = headersOf(

233

HttpHeaders.ContentType, "application/json",

234

HttpHeaders.CacheControl, "public, max-age=3600"

235

),

236

varyKeys = mapOf(

237

"Accept-Language" to "en-US",

238

"Accept-Encoding" to "gzip"

239

),

240

body = """{"message": "cached data"}""".toByteArray()

241

)

242

243

// Store in cache manually

244

val storage = CacheStorage.Unlimited()

245

storage.store(cachedData.url, cachedData)

246

247

// Retrieve from cache

248

val retrieved = storage.find(

249

url = Url("https://api.example.com/data"),

250

varyKeys = mapOf(

251

"Accept-Language" to "en-US",

252

"Accept-Encoding" to "gzip"

253

)

254

)

255

256

if (retrieved != null) {

257

println("Found cached data: ${String(retrieved.body)}")

258

println("Expires: ${retrieved.expires}")

259

}

260

```

261

262

### Cache Control Headers

263

264

Support for HTTP cache control directives and validation.

265

266

```kotlin { .api }

267

/**

268

* Cache storage extensions for response handling

269

*/

270

suspend fun CacheStorage.store(response: HttpResponse): CachedResponseData

271

suspend fun CacheStorage.store(

272

response: HttpResponse,

273

varyKeys: Map<String, String>

274

): CachedResponseData

275

suspend fun CacheStorage.store(

276

response: HttpResponse,

277

varyKeys: Map<String, String>,

278

isShared: Boolean

279

): CachedResponseData

280

```

281

282

**Usage Examples:**

283

284

```kotlin

285

val client = HttpClient {

286

install(HttpCache) {

287

publicStorage(CacheStorage.Unlimited())

288

}

289

}

290

291

// Responses with cache headers are automatically cached

292

val response1 = client.get("https://api.example.com/public-data") {

293

headers {

294

// Request headers that might affect caching

295

append(HttpHeaders.AcceptLanguage, "en-US")

296

append(HttpHeaders.AcceptEncoding, "gzip")

297

}

298

}

299

300

// Check if response was cached

301

val cacheControl = response1.headers[HttpHeaders.CacheControl]

302

println("Cache-Control: $cacheControl")

303

304

// Conditional requests with cache validation

305

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

306

headers {

307

// Add conditional headers for cache validation

308

append(HttpHeaders.IfModifiedSince, lastModified)

309

append(HttpHeaders.IfNoneMatch, etag)

310

}

311

}

312

313

// Handle 304 Not Modified responses

314

if (response2.status == HttpStatusCode.NotModified) {

315

println("Content not modified, using cached version")

316

}

317

```

318

319

### Cache Events and Monitoring

320

321

Events for monitoring cache behavior and performance.

322

323

```kotlin { .api }

324

/**

325

* Event fired when response is served from cache

326

*/

327

val HttpCache.HttpResponseFromCache: EventDefinition<HttpResponse>

328

```

329

330

**Usage Examples:**

331

332

```kotlin

333

val client = HttpClient {

334

install(HttpCache) {

335

publicStorage(CacheStorage.Unlimited())

336

}

337

338

// Monitor cache performance

339

monitor.subscribe(HttpCache.HttpResponseFromCache) { response ->

340

println("Cache HIT: ${response.request.url}")

341

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

342

println("Cache headers: ${response.headers[HttpHeaders.CacheControl]}")

343

}

344

}

345

346

// Track cache statistics

347

var cacheHits = 0

348

var totalRequests = 0

349

350

client.monitor.subscribe(HttpRequestCreated) { request ->

351

totalRequests++

352

}

353

354

client.monitor.subscribe(HttpCache.HttpResponseFromCache) { response ->

355

cacheHits++

356

val hitRate = (cacheHits.toFloat() / totalRequests) * 100

357

println("Cache hit rate: ${String.format("%.1f%%", hitRate)}")

358

}

359

360

// Make requests to see cache behavior

361

repeat(5) {

362

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

363

println("Request $it: ${response.status}")

364

}

365

```

366

367

### Cache Exceptions

368

369

Exception handling for cache-related errors.

370

371

```kotlin { .api }

372

/**

373

* Exception thrown when cache is in invalid state

374

*/

375

class InvalidCacheStateException(message: String) : IllegalStateException(message)

376

```

377

378

**Usage Examples:**

379

380

```kotlin

381

try {

382

val client = HttpClient {

383

install(HttpCache) {

384

publicStorage(CustomCacheStorage())

385

}

386

}

387

388

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

389

} catch (e: InvalidCacheStateException) {

390

println("Cache error: ${e.message}")

391

// Handle cache corruption or invalid state

392

} catch (e: Exception) {

393

println("Other error: ${e.message}")

394

}

395

```

396

397

### Advanced Caching Patterns

398

399

Advanced usage patterns for complex caching scenarios.

400

401

```kotlin { .api }

402

// Custom cache storage with persistence

403

class PersistentCacheStorage(private val cacheDir: File) : CacheStorage {

404

override suspend fun store(url: Url, data: CachedResponseData) {

405

// Store to filesystem or database

406

}

407

408

override suspend fun find(url: Url, varyKeys: Map<String, String>): CachedResponseData? {

409

// Load from persistent storage

410

return null

411

}

412

413

override suspend fun findAll(url: Url): Set<CachedResponseData> {

414

// Load all variants from storage

415

return emptySet()

416

}

417

}

418

```

419

420

**Usage Examples:**

421

422

```kotlin

423

// Multi-tier caching with memory + disk

424

class TieredCacheStorage(

425

private val memoryCache: CacheStorage,

426

private val diskCache: CacheStorage

427

) : CacheStorage {

428

429

override suspend fun store(url: Url, data: CachedResponseData) {

430

// Store in both memory and disk

431

memoryCache.store(url, data)

432

diskCache.store(url, data)

433

}

434

435

override suspend fun find(url: Url, varyKeys: Map<String, String>): CachedResponseData? {

436

// Try memory first, then disk

437

return memoryCache.find(url, varyKeys)

438

?: diskCache.find(url, varyKeys)?.also {

439

// Promote to memory cache

440

memoryCache.store(url, it)

441

}

442

}

443

444

override suspend fun findAll(url: Url): Set<CachedResponseData> {

445

val memory = memoryCache.findAll(url)

446

val disk = diskCache.findAll(url)

447

return memory + disk

448

}

449

}

450

451

val client = HttpClient {

452

install(HttpCache) {

453

publicStorage(TieredCacheStorage(

454

memoryCache = CacheStorage.Unlimited(),

455

diskCache = PersistentCacheStorage(File("cache"))

456

))

457

}

458

}

459

```