Crates.io | tower-etag-cache |
lib.rs | tower-etag-cache |
version | 0.1.0 |
source | src |
created_at | 2023-10-07 09:49:46.464187 |
updated_at | 2023-10-07 09:49:46.464187 |
description | A tower middleware for implementing ETag-based HTTP caching |
homepage | https://github.com/billythedummy/tower-etag-cache |
repository | https://github.com/billythedummy/tower-etag-cache.git |
max_upload_size | |
id | 996204 |
size | 45,947 |
A tower middleware for implementing ETag-based HTTP caching.
The const-lru-provider
feature provides a const-lru-backed CacheProvider
implementation that's ready to be used.
use axum::{error_handling::HandleErrorLayer, http::StatusCode, BoxError, Router};
use tower_etag_cache::{const_lru_provider::ConstLruProvider, EtagCacheLayer};
use tower_http::services::{ServeDir, ServeFile};
#[tokio::main]
pub async fn main() {
let app = Router::new()
.fallback_service(ServeDir::new("app").fallback(ServeFile::new("app/404.html")))
.layer(
ServiceBuilder::new()
.layer(HandleErrorLayer::new(handle_etag_cache_layer_err))
.layer(EtagCacheLayer::with_default_predicate(
ConstLruProvider::<_, _, 255, u8>::init(5),
)),
);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
async fn handle_etag_cache_layer_err<T: Into<BoxError>>(err: T) -> (StatusCode, String) {
(StatusCode::INTERNAL_SERVER_ERROR, err.into().to_string())
}
The ConstLruProvider
calculates ETag as the base64-encoded blake3 hash of response bodies.
It keys entries by SimpleEtagCacheKey
, a struct comprising the request URI + sorted Vec
collections of header values for the Accept
, Accept-Language
, and Accept-Encoding
request headers. This causes it to vary ETags based on these headers.
Since the current implementation loads the entire response body into memory to calculate the ETag, ConstLruProvider
is not suitable for extremely large responses such as large files.
The EtagCache
tower service and EtagCacheLayer
tower layer is created with an inner tower service + any type that implements the CacheProvider
trait.
If-None-Match
matches an ETag in the cachepub trait CacheProvider<ReqBody, ResBody>:
Service<http::Request<ReqBody>, Response = CacheGetResponse<ReqBody, Self::Key>> // runs on request
+ Service<(Self::Key, http::Response<ResBody>), Response = http::Response<Self::TResBody>> // runs on response
{
type Key;
type TResBody;
}
When a http request comes in,
CacheProvider
's first ETag lookup service runs on the request.CacheProvider
's second ETag calculating and saving service runs on the http response returned by the inner service.The PassthroughPredicate
trait controls when requests and responses should ignore the caching layer.
The provided DefaultPredicate
is available for use with EtagCacheLayer::with_default_predicate
and has the following behaviour:
requests:
GET
and HEAD
methods are ran through the caching layerresponses:
HTTP 2XX
responses, excluding 204 No Content
, are cachedETag
header are cachedContent-Length
header are cached