在塔层检索请求 body 以签署 GRPC 请求

Retrieving the request body in a tower layer to sign GRPC requests

我正在尝试使用塔中间件层功能在 grpc 之上(通过 tonic)实现一个身份验证层。为此,我需要获取请求的 body,包括发送到服务器的 protobuf 负载,使用 HMAC 对其进行身份验证,然后在元数据 / [= 中设置身份验证 HMAC 33=].

但是我在通过 API 检索请求 body 时遇到了一些问题,这似乎没有等待整个请求被概括为小型和大型流媒体请求。后者在实际向服务器发出请求之前需要在内存中缓冲一个大的 body。由于我可以控制要拦截的请求,而且我知道我的请求足够小,可以在内存中进行缓冲而不会产生太多开销,因此这种概括增加了一些我不知道如何处理的复杂性。

我的图层目前看起来像这样:

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 是不可变的,因此我们不能在 body 上调用 data()。由于请求很小,我可以克隆整个请求,缓冲 body 然后计算 HMAC,然后将请求转发到 inner 服务,但我尝试的一切都失败了.然而,理想情况下,可以就地缓冲并转发原始文件。

如何从 http::Request 中获取 body?

方法签名按值接受请求:

    fn call(&mut self, request: Request<BoxBody>) -> Self::Future 

因此您可以重新绑定它以使其可变。通过重新绑定它,您 将数据移动 到一个新变量(具有相同的名称),该变量是可变的:

let mut request = request; 
let request_body = request.body_mut(); 
let body = request_body.data();

或者您可以直接在方法上添加 mut

    fn call(&mut self, mut request: Request<BoxBody>) -> Self::Future 

这不会改变签名,因为调用者不关心你是否修改它,因为它是按值传递的。

how is upgrading the non-mut borrow to a mut borrow safe?

它是安全的(也是可能的),因为它是按值传递的,而不是按引用传递的。作为该值的唯一所有者,您可以使其可变或不可变