我正在尝试使用塔式中间件层功能在 grpc 之上(通过 tonic)实现身份验证层。为此,我需要获取请求的主体,包括发送到服务器的 protobuf 有效负载,使用 HMAC 对其进行身份验证,然后在元数据/标头中设置身份验证 HMAC。
但是,我在通过 API 检索请求正文时遇到了一些问题,这似乎没有等待整个请求被概括为小型和大型流式请求。后者在实际向服务器发出请求之前需要在内存中缓冲一个大的主体。由于我可以控制要拦截的请求,并且我知道我的请求足够小,可以在内存中缓冲而没有太多开销,因此这种概括增加了一些我不知道如何处理的复杂性。
我的图层目前看起来像这样:
use http::Request;
use std::task::{Context, Poll};
use tonic::body::BoxBody;
use tonic::codegen::Body;
use tower::{Layer, Service};
pub struct AuthLayer {
hmac_key: Vec<u8>,
}
impl AuthLayer {
pub fn new(hmac_key: Vec<u8>) -> Self {
AuthLayer { hmac_key: hmac_key }
}
}
impl<S> Layer<S> for AuthLayer {
type Service = AuthService<S>;
fn layer(&self, inner: S) -> Self::Service {
AuthService {
hmac_key: self.hmac_key.clone(),
inner,
}
}
}
// This service implements the Log behavior
pub struct AuthService<S> {
hmac_key: Vec<u8>,
inner: S,
}
impl<S> Service<Request<BoxBody>> for AuthService<S>
where
S: Service<Request<BoxBody>>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, request: Request<BoxBody>) -> Self::Future {
let _body = dbg!(request.body().data());
// TODO Compute an HMAC over _body
// TODO Set HMAC in metadata / headers
self.inner.call(request)
}
}
它失败是因为request
是不可变的,因此我们不能调用data()
身体。由于请求很小,我可以克隆整个请求,缓冲正文,然后在其上计算 HMAC,然后再将请求转发给inner
服务,但我尝试的一切都失败了。但是,理想情况下,可以就地缓冲并转发原件。
我怎样才能得到身体http::Request
?