tower-http-cache 0.6.0

Tower-compatible caching layer with pluggable backends (in-memory, Redis, and more)
Documentation
[package]
name = "tower-http-cache"
version = "0.6.0"
edition = "2024"
# 1.85 covers the default feature set and everything except `redis-backend`,
# which requires 1.88 -- redis 1.6 declares `rust-version = "1.88"` outright,
# and it also reaches `url` -> `idna` -> `icu_*`, which require the same. There
# is no Cargo.lock in this repo, so a consumer resolving fresh gets those
# versions too. Both floors are enforced by separate CI jobs.
rust-version = "1.85"
description = "Tower-compatible caching layer with pluggable backends (in-memory, Redis, and more)"
license = "MIT OR Apache-2.0"
repository = "https://github.com/sadco-io/tower-http-cache"
documentation = "https://docs.rs/tower-http-cache"
readme = "README.md"
exclude = ["._*", "**/.DS_Store", ".cursor/"]
keywords = ["tower", "cache", "middleware", "http"]
categories = ["web-programming", "caching"]
authors = ["Daniel Curtis <drc@danielryancurtis.com>"]

[dependencies]
tower = { version = "0.5.3", default-features = false, features = ["util"] }
http = { version = "1.5", default-features = false }
http-body = "1.1"
http-body-util = "0.1.5"
bytes = "1.12"
tokio = { version = "1.53", features = ["sync", "rt", "time", "macros"] }
# Optional: only the JSON-shaped surfaces need these -- the codec (backend
# serialization), `CacheEvent` (structured JSON logging) and the admin API.
# The core in-memory cache path does not.
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
moka = { version = "0.12.16", optional = true, features = ["future"] }
redis = { version = "1.6.0", features = ["aio", "tokio-comp", "connection-manager"], optional = true }
tokio-util = { version = "0.7.19", optional = true }
metrics = { version = "0.24", optional = true }
pin-project-lite = "0.2"
thiserror = "2.0"
dashmap = "6.2"
# Wire format for cached entries. `default-features = false` is required: the
# default feature set pulls `heapless` for embedded targets.
postcard = { version = "1.1.3", default-features = false, features = ["use-std"], optional = true }
tracing = { version = "0.1", optional = true }
flate2 = { version = "1.1", optional = true, default-features = false, features = ["rust_backend"] }
uuid = { version = "1.25", features = ["v4", "serde"] }
sha2 = "0.11"
hex = "0.4"
axum = { version = "0.8.9", optional = true }

[features]
default = ["in-memory", "serde", "legacy-bincode1-read"]
in-memory = ["moka"]
redis-backend = ["redis", "tokio-util", "serde"]
serde = ["dep:serde", "dep:serde_json", "dep:postcard"]
metrics = ["dep:metrics"]
tracing = ["dep:tracing"]
compression = ["dep:flate2"]
admin-api = ["dep:axum", "serde"]
# Reads cache entries written by tower-http-cache 0.5.x. On by default in 0.6.0
# so upgrades do not cold-start production caches. The reader is hand-written
# against the bincode 1 layout and pulls no dependency, so leaving it on costs
# only dead code. Deprecated: this feature and the module behind it are removed
# in 0.7.0. Entries are self-expiring, so once every 0.5.x-written entry has
# aged past its TTL plus its stale window you can turn it off. Disabling it is
# safe at any time; it only costs a cold cache.
legacy-bincode1-read = []

[dev-dependencies]
tokio = { version = "1.53", features = ["full"] }
# Test-only: `tests/integration_cache.rs` builds a chunked body with
# `futures_util::stream::unfold`. The library itself no longer needs it --
# `CacheService::Future` uses a local `Pin<Box<dyn Future + Send>>` alias.
futures-util = { version = "0.3.34", default-features = false, features = ["alloc"] }
tower = { version = "0.5.3", features = ["util"] }
http-body-util = "0.1.5"
axum = "0.8.9"
redis = { version = "1.6.0", features = ["aio", "tokio-comp", "connection-manager"] }
tracing-subscriber = { version = "0.3.23", features = ["fmt", "env-filter"] }
criterion = { version = "0.8", features = ["html_reports"] }
# No-op recorder for the `metrics` bench in cache_benchmarks.rs.
metrics-util = { version = "0.20", default-features = false, features = ["debugging"] }
# `tests/wire_compat.rs` exercises `CacheEntry`'s derived serde impls under a
# non-self-describing format directly, without going through the codec.
postcard = { version = "1.1.3", default-features = false, features = ["use-std"] }

# Every integration test and example is built on a concrete backend, so each
# declares the feature that provides it. Without these, `cargo test` on a
# reduced feature set fails to resolve the backend type.
[[test]]
name = "auto_refresh"
required-features = ["in-memory"]

[[test]]
name = "cache_stale"
required-features = ["in-memory"]

[[test]]
name = "integration_cache"
required-features = ["in-memory"]

[[test]]
name = "streaming"
required-features = ["in-memory"]

[[test]]
name = "tags"
required-features = ["in-memory"]

[[test]]
name = "redis_example"
required-features = ["redis-backend"]

# Byte-level wire format tests. No server needed: the codec, the envelope and
# the legacy readers all operate on plain slices.
[[test]]
name = "wire_compat"
required-features = ["serde"]

[[example]]
name = "chunk_cache_demo"
required-features = ["in-memory"]

[[example]]
name = "v0_3_features"
required-features = ["in-memory", "serde"]

[[example]]
name = "axum_redis"
required-features = ["redis-backend"]

[[example]]
name = "redis_smoke"
required-features = ["redis-backend"]

[[bench]]
name = "cache_benchmarks"
harness = false
# `bench_codec_and_compression` uses the codec, which lives behind `serde`.
required-features = ["in-memory", "serde"]