1 个不稳定版本

0.1.0 2023年10月7日

#19#tower-middleware

MIT/Apache

46KB
916

tower-etag-cache

实现基于ETag的HTTP缓存的tower中间件。

快速入门

提供const-lru支持的CacheProvider实现,可直接使用。

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())
}

ConstLruProvider将响应体的base64编码的blake3哈希值作为ETag。

它通过SimpleEtagCacheKey来键入条目,这是一个由请求URI + 排序后的请求头值集合组成的结构,包括AcceptAccept-LanguageAccept-Encoding请求头。这使得它根据这些头信息来改变ETag。

由于当前实现将整个响应体加载到内存中以便计算ETag,因此ConstLruProvider不适用于像大文件这样的大响应。

工作原理

使用内部tower服务 + 实现CacheProvider特质的任何类型创建EtagCache tower服务和EtagCacheLayer tower层。

CacheProvider

  • 包含2个tower服务
    • 1个在传入的HTTP请求上运行,以查找ETag,检查请求的If-None-Match是否与缓存中的ETag匹配
    • 1 在出站HTTP响应上运行,以计算和保存响应的ETag
  • 有一个关联的缓存键类型,用于标识缓存条目
  • 有一个关联的转换响应体类型,它会在ETag计算和保存程序后,将出站HTTP响应体转换为该类型
pub 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;
}

当一个HTTP请求到来时,

  • 如果服务的passthrough_predicate指示请求应该被传递,则未经修改的请求将被直接传递到内部服务,并将响应直接返回给客户端。
  • 否则,CacheProvider 的第一个ETag查找服务将在请求上运行。
  • 如果服务返回缓存命中,则向客户端返回一个带有相关头部的空HTTP 304响应。
  • 否则,内部服务将在未经修改的请求上运行。
  • 如果服务器的passthrough_predicate指示响应应该被传递,则未经修改的响应将返回给客户端。
  • 否则,CacheProvider 的第二个ETag计算和保存服务将在内部服务返回的HTTP响应上运行。
  • 服务转换响应体,并修改响应头以包含保存的ETag和其他相关头,并将其返回给客户端。

PassthroughPredicate

PassthroughPredicate 特性控制何时请求和响应应忽略缓存层。

提供的 DefaultPredicate 可用于与 EtagCacheLayer::with_default_predicate 一起使用,并具有以下行为

请求

  • 只有 GETHEAD 方法会通过缓存层运行

响应

  • 只有 HTTP 2XX 响应会被缓存,不包括 204 No Content
  • 只有那些还没有 ETag 头部的响应会被缓存
  • 只有那些具有缺失、无效或非零 Content-Length 头部的响应会被缓存

依赖

~0.8–3.5MB
~67K SLoC