http_cache_tags

Crates.iohttp_cache_tags
lib.rshttp_cache_tags
version0.1.0-alpha.5
created_at2025-07-24 16:57:37.822684+00
updated_at2025-07-30 13:14:35.525442+00
descriptionAn experimental cache tagging library for Rust web frameworks
homepage
repositoryhttps://gitlab.com/dkluhzeb/http_cache_tags
max_upload_size
id1766302
size105,744
(dkluhzeb)

documentation

https://docs.rs/http_cache_tags

README

Experimental: Alpha Release

This crate is in early development. APIs are unstable and may change without notice. Not recommended for production use yet.

HTTP Cache Tags: Framework Integrations for Actix and Axum

This crate provides seamless integration of HTTP cache tagging and validation features into popular Rust web frameworks, including Actix-web and Axum.
It builds on the core [http_cache_tags_core] library to deliver middleware, extractors, API handlers, and runtime components that enable tag-based cache invalidation and metadata resolution.


Features

  • Axum Integration (axum): Middleware, extractors, and runtime for the Axum framework.
  • Actix Integration (actix): Middleware, extractors, and runtime for Actix-web framework.
  • Middleware for injecting cache metadata headers like ETag and Last-Modified.
  • Extractors for validated JSON payloads and cache metadata.
  • API controllers for cache invalidation and validation routes.
  • Support for custom cache stores and seeders.
  • Optional features mirroring the core crate (etag, last_modified, redis, etc.).

By default, the axum integration and last_modified support are enabled.
At least one server integration (axum or actix) and one validation header feature (etag or last_modified) must be activated for proper operation.

Feature Description
axum Enable Axum framework middleware and extractors
actix Enable Actix-web middleware and extractors
config_file Load configuration from TOML file
last_modified Support Last-Modified timestamps
etag Generate and handle ETags
redis Redis backend for cache metadata persistence

Getting Started

1. Define Your Cache Config

The cache config maps URL routes to cache tags, and configures invalidation endpoints.

use http_cache_tags::axum::prelude::*;

let config = CacheConfig::builder()
    .invalidation_api_route("/_invalidate")
    .invalidation_api_secret("123abc")
    .add_route_mapping("/blog/*", vec!["blog"])
    .add_route_mapping("/media/*", vec!["media"])
    .add_ignore_mapping("/blog/i-am-no-blog", vec!["blog"])
    .redis_uri("redis://127.0.0.1/")
    .build();

2. Build a Runtime

The CacheRuntime composes the cache config with stores, API controllers, and middleware components.

use http_cache_tags::axum::prelude::*;

let config = CacheConfig::builder().build();

let runtime = CacheRuntime::builder()
    .config(config)
    // Optionally add a custom seeder to pre-populate cache tags
    // .seeder(Box::new(MyCustomSeeder {}))
    .build();

Example Setup

Axum Example

use http_cache_tags::axum::prelude::*;
use axum::{Router, routing::get};
use std::{net::SocketAddr, sync::Arc};

#[tokio::main]
async fn main() {
    let config = CacheConfig::builder()
        .invalidation_api_route("/_invalidate")
        .add_route_mapping("/blog/*", vec!["blog"])
        .build();

    let runtime = CacheRuntime::builder().config(config).build();

    let router = Router::new()
        .route("/blog/{slug}", get(blog_handler));

    let app = runtime.attach_to(router);

    let addr = SocketAddr::from(([0, 0, 0, 0], 3000));

    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();

    axum::serve(listener, app).await.unwrap();
}

async fn blog_handler(CacheMeta(meta): CacheMeta) -> String {
    format!("Tags: {:?}, Last Modified: {:?}", meta.tags, meta.last_modified)
}

Actix Example

use actix_web::{App, HttpServer, web, HttpResponse};
use http_cache_tags::actix::prelude::*;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let config = CacheConfig::builder()
        .invalidation_api_route("/_invalidate")
        .add_route_mapping("/blog/*", vec!["blog"])
        .build();

    let runtime = CacheRuntime::builder().config(config).build();

    let server = HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(runtime.extractor()))
            .wrap(runtime.cache_meta_middleware())
            .wrap(runtime.cache_validation_middleware())
            .service(runtime.invalidation_scope().unwrap())
            .route("/blog/{slug}", web::get().to(blog_handler))
    });

    server.bind(("0.0.0.0", 3000))?.run().await
}

async fn blog_handler(meta: CacheMeta) -> HttpResponse {
    HttpResponse::Ok().body(format!(
        "Tags: {:?}, Last Modified: {:?}",
        meta.tags, meta.last_modified
    ))
}

Custom Cache Store Seeder Example

Implement CacheStoreSeeder to prepopulate the cache store with tags or metadata on startup, useful for warming caches or integrating external systems.

use http_cache_tags::axum::prelude::*;
use std::{collections::HashSet, future::Future, pin::Pin};

struct MyCustomSeeder;

impl CacheStoreSeeder for MyCustomSeeder {
    fn seed<'a>(
        &'a self,
        store: &'a (dyn CacheStore),
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
        Box::pin(async move {
            // if `etag` feature is enabled
            // store.delete_etag("home");
            // store.set_etag("home", "v1");

            // if `last_modified` is enabled
            // store.delete_last_modified("home");
            // store.set_last_modified("home", chrono::Utc::now());
        })
    }
}

API Usage Examples

This crate supports HTTP cache tagging and invalidation with detailed control over cache metadata such as ETag and Last-Modified headers.

Below are typical HTTP request examples demonstrating how to interact with the cache system:

Conditional GET with If-Modified-Since

GET /blog/test HTTP/1.1
If-Modified-Since: Sun, 20 Jul 2025 23:12:31 +0000

Conditional GET with If-None-Match (ETag)

GET /blog/test HTTP/1.1
If-None-Match: W/"82f493c74aaf8d7557ee619db325d6948f9cfb19"

Invalidate with update of last_modified and etag (with auth header)

POST /_invalidate HTTP/1.1
Content-Type: application/json
Authorization: Bearer my-secret

{
  "tags": [
    { "tag": "blog", "last_modified": true, "etag": true }
  ]
}

Remove all cache metadata for a tag

POST /_invalidate HTTP/1.1
Content-Type: application/json

{
  "tags": [
    { "tag": "blog", "last_modified": null, "etag": null }
  ]
}

Set explicit last_modified timestamp

POST /_invalidate HTTP/1.1
Content-Type: application/json

{
  "tags": [
    { "tag": "blog", "last_modified": "2024-01-01T12:00:00Z" }
  ]
}

Remove last_modified timestamp only

POST /_invalidate HTTP/1.1
Content-Type: application/json

{
  "tags": [
    { "tag": "blog", "last_modified": null }
  ]
}

Trigger ETag regeneration only

POST /_invalidate HTTP/1.1
Content-Type: application/json

{
  "tags": [
    { "tag": "blog", "etag": true }
  ]
}

Remove ETag completely

POST /_invalidate HTTP/1.1
Content-Type: application/json

{
  "tags": [
    { "tag": "blog", "etag": null }
  ]
}

Invalidate multiple tags with different operations

POST /_invalidate HTTP/1.1
Content-Type: application/json

{
  "tags": [
    { "tag": "user:42", "etag": true },
    { "tag": "product:99", "last_modified": true },
    { "tag": "stale:tag", "last_modified": null, "etag": null }
  ]
}

For more examples, see the examples/ folder containing HTTP request files demonstrating common usage patterns and advanced configurations.

Commit count: 0

cargo fmt