Skip to main content

encryption/integrations/
ecat.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! e-cat 集成。
4//!
5//! e-cat 的 HTTP 传输层是 axum,中间件层是 **tower 的 `Layer`/`Service`**,
6//! 用 `tower::ServiceBuilder` 组装。它没有自己的「中间件 trait」,所以这里
7//! 就是一个标准的 tower 层:把 [`Guard`] 塞进请求扩展,处理器用
8//! `Extension<Guard>` 取出来。
9//!
10//! ```no_run
11//! # mod demo {
12//! use encryption::guard::Guard;
13//! use encryption::integrations::ecat::GuardLayer;
14//! use tower::ServiceBuilder;
15//!
16//! # fn main() {
17//! let guard: Guard = todo!();
18//! let middleware = ServiceBuilder::new().layer(GuardLayer::new(guard));
19//! # let _ = middleware;
20//! # }
21//! # }
22//! ```
23//!
24//! # 为什么这不只是「给 e-cat 用的」
25//!
26//! 它是一个不依赖 e-cat 的普通 tower 层 —— e-cat 只是把它接进来的那个框架。
27//! 与 e-cat 自身的 `ecat-middleware::ValidateLayer` 是同一个形状,可以并排
28//! 放进同一个 `ServiceBuilder`。
29//!
30//! # 为什么不必装箱
31//!
32//! 这一层不改写响应、不改写错误,只是往请求里塞一个 `Extension`,所以
33//! `type Future = S::Future` —— 直接透传内层的 future,没有 `Pin<Box<dyn ...>>`,
34//! 也没有 `futures` 依赖。
35
36use std::task::{Context, Poll};
37
38use http::Request;
39use tower::{Layer, Service};
40
41use crate::guard::Guard;
42
43/// 把 [`Guard`] 注入每个请求扩展的 tower 层。
44#[derive(Clone, Debug)]
45pub struct GuardLayer {
46    guard: Guard,
47}
48
49impl GuardLayer {
50    /// 用一把共享守卫构造。
51    pub fn new(guard: Guard) -> Self {
52        Self { guard }
53    }
54}
55
56impl<S> Layer<S> for GuardLayer {
57    type Service = GuardService<S>;
58
59    fn layer(&self, inner: S) -> Self::Service {
60        GuardService {
61            inner,
62            guard: self.guard.clone(),
63        }
64    }
65}
66
67/// [`GuardLayer`] 产出的服务。
68#[derive(Clone, Debug)]
69pub struct GuardService<S> {
70    inner: S,
71    guard: Guard,
72}
73
74impl<S, B> Service<Request<B>> for GuardService<S>
75where
76    S: Service<Request<B>>,
77{
78    type Response = S::Response;
79    type Error = S::Error;
80    /// 透传内层的 future —— 本层不做任何异步改写。
81    type Future = S::Future;
82
83    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
84        self.inner.poll_ready(cx)
85    }
86
87    fn call(&mut self, mut req: Request<B>) -> Self::Future {
88        // 处理器侧用 `Extension<Guard>` 或直接 `Guard`(若也用了 axum 适配层)取用
89        req.extensions_mut().insert(self.guard.clone());
90        self.inner.call(req)
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use std::convert::Infallible;
98    use tower::service_fn;
99
100    fn guard() -> Guard {
101        Guard::from_master_key(&[7u8; 32], "aes-256-gcm").unwrap()
102    }
103
104    /// 内层服务:从扩展里把守卫取出来,回报算法数量。
105    /// 模拟一个真实的处理器 —— 拿不到守卫就直接炸,测试才有意义。
106    async fn echo_identifier_count(req: Request<()>) -> Result<String, Infallible> {
107        let g = req
108            .extensions()
109            .get::<Guard>()
110            .expect("守卫应当已被注入")
111            .clone();
112        Ok(g.identifiers().len().to_string())
113    }
114
115    #[tokio::test]
116    async fn injects_the_guard_into_extensions() {
117        let mut svc = GuardLayer::new(guard()).layer(service_fn(echo_identifier_count));
118        let res = svc.call(Request::new(())).await.unwrap();
119        assert_eq!(res, "5");
120    }
121
122    /// 注进去的与外面持有的是同一把密钥 —— 密文互通。
123    #[tokio::test]
124    async fn injected_guard_shares_the_key() {
125        let state = guard();
126        let token = state.encrypt("共享".as_bytes()).unwrap();
127
128        let inner = service_fn(move |req: Request<()>| {
129            let token = token.clone();
130            async move {
131                let g = req.extensions().get::<Guard>().unwrap().clone();
132                Ok::<_, Infallible>(
133                    g.decrypt(&token)
134                        .map(|plain| String::from_utf8_lossy(&plain).into_owned())
135                        .unwrap_or_else(|_| "解不开".into()),
136                )
137            }
138        });
139
140        let mut svc = GuardLayer::new(state).layer(inner);
141        let res = svc.call(Request::new(())).await.unwrap();
142        assert_eq!(res, "共享");
143    }
144
145    /// 多个请求走同一个服务,注入照常。
146    #[tokio::test]
147    async fn works_across_multiple_requests() {
148        let mut svc = GuardLayer::new(guard()).layer(service_fn(echo_identifier_count));
149        for _ in 0..3 {
150            assert_eq!(svc.call(Request::new(())).await.unwrap(), "5");
151        }
152    }
153
154    /// 与 e-cat 自己的中间件并排组装(同一个 ServiceBuilder 里)。
155    #[tokio::test]
156    async fn composes_with_ecat_service_builder() {
157        use tower::ServiceBuilder;
158
159        let mut svc = ServiceBuilder::new()
160            .layer(GuardLayer::new(guard()))
161            .service(service_fn(echo_identifier_count));
162
163        assert_eq!(svc.call(Request::new(())).await.unwrap(), "5");
164    }
165}