Closed feelingsonice closed 2 months ago
Thanks @feelingsonice for reporting! Updated.
New version: 0.2.0
tonic-middleware 0.2.x - will follow tonic 0.12.x versions
Breaking changes (due to tonic breaking changes)
Instead of
async fn intercept(&self, req: Request<Body>) -> Result<Request<Body>, Status>
Use
use use tonic::body::BoxBody;
...
async fn intercept(&self, req: Request<BoxBody>) -> Result<Request<BoxBody>, Status>
Instead of
async fn intercept_with_endpoint_error(
&self,
mut req: Request<Body>,
) -> Result<Request<Body>, Status>
Use
use use tonic::body::BoxBody;
...
async fn intercept_with_endpoint_error(
&self,
mut req: Request<BoxBody>,
) -> Result<Request<BoxBody>, Status>
I'm struggling to get this to work.
use tonic::body::BoxBody;
use tonic::transport::Channel;
use tonic::{async_trait, Code, Request, Status};
use tonic_middleware::RequestInterceptor;
#[derive(Clone)]
pub struct AuthInterceptor {
pub my_channel: Channel,
}
#[async_trait]
impl RequestInterceptor for AuthInterceptor {
async fn intercept(&self, req: Request<BoxBody>) -> Result<Request<BoxBody>, Status> {
Ok(req)
}
}
error[E0053]: method `intercept` has an incompatible type for trait
--> my_server/src/authorize.rs:15:36
|
15 | async fn intercept(&self, req: Request<BoxBody>) -> Result<Request<BoxBody>, Status> {
| ^^^^^^^^^^^^^^^^
| |
| expected `Request<UnsyncBoxBody<..., ...>>`, found a different `Request<UnsyncBoxBody<..., ...>>`
| help: change the parameter type to match the trait: `request::Request<http_body_util::combinators::box_body::UnsyncBoxBody<prost::bytes::Bytes, tonic::Status>>`
|
= note: expected signature `fn(&'life0 AuthInterceptor, request::Request<http_body_util::combinators::box_body::UnsyncBoxBody<prost::bytes::Bytes, tonic::Status>>) -> Pin<Box<(dyn Future<Output = Result<request::Request<http_body_util::combinators::box_body::UnsyncBoxBody<prost::bytes::Bytes, tonic::Status>>, tonic::Status>> + Send + 'async_trait)>>`
found signature `fn(&'life0 AuthInterceptor, tonic::Request<http_body_util::combinators::box_body::UnsyncBoxBody<prost::bytes::Bytes, tonic::Status>>) -> Pin<Box<(dyn Future<Output = Result<tonic::Request<http_body_util::combinators::box_body::UnsyncBoxBody<prost::bytes::Bytes, tonic::Status>>, tonic::Status>> + Send + 'async_trait)>>`
For more information about this error, try `rustc --explain E0053`.
@drauschenbach could you post your cargo.toml?
[workspace.dependencies]
anyhow = "1.0.86"
async-stream = "0.3.5"
env_logger = { version = "0.11.3", features = ["unstable-kv"] }
itertools = "0.13.0"
log = { version = "0.4.21", features = ["kv_unstable_serde", "kv_unstable"] }
pbjson = "0.7.0"
pbjson-types = "0.7.0"
prost = "0.13.1"
prost-build = "0.13.1"
prost-types = "0.13.1"
rustls = "0.23.11"
serde = { version = "1.0.202", features = ["derive"] }
serde_json = "1.0.116"
structopt = "0.3.26"
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
tokio-stream = "0.1.15"
tonic = { version = "0.12.0", features = ["tls", "tls-roots"] }
tonic-middleware = "0.2.0"
@drauschenbach You need to use
use tonic::codegen::http::Request;
instead of use tonic::Request;
use tonic::body::BoxBody;
use tonic::transport::Channel;
use tonic::{async_trait, Status};
use tonic_middleware::RequestInterceptor;
use tonic::codegen::http::Request;
#[derive(Clone)]
pub struct AuthInterceptor {
pub my_channel: Channel,
}
#[async_trait]
impl RequestInterceptor for AuthInterceptor {
async fn intercept(&self, req: Request<BoxBody>) -> Result<Request<BoxBody>, Status> {
Ok(req)
}
}
tonic::codegen::http::Request
doesn't have a metadata accessor (that I can find?), so is unusable for gRPC middleware.
@drauschenbach Pls check example, instead of metadata, you can use
// Set user id in header, so it can be used in grpc services through tonic::Request::metadata()
let user_id_header_value = HeaderValue::from_str(&user_id.to_string())
.map_err(|_e| Status::internal("Failed to convert user_id to header value"))?;
req.headers_mut().insert("user_id", user_id_header_value);
Ok(req)
Unfortunately, due to nature of tonic implementation, there is no way to directly use/consume tonic::Request
in interceptors in async manner (unless directly modifying tonic source code, at least I did not found)
Full example is here: https://github.com/teimuraz/tonic-middleware/blob/main/example/src/server.rs#L67
Though there are examples and integration tests, probably I need to update README to make it more explicit and clear
using http::Request
from the http
crate version 1.1 works for me
using
http::Request
from thehttp
crate version 1.1 works for me
Yeah, tonic::codegen::http::Request
is just basically re-export of http::Request
Updated README with proper imports. Hopefully, this will help reduce ambiguity https://github.com/teimuraz/tonic-middleware?tab=readme-ov-file#note
I'm trying to access gRPC metadata, not mutate it.
async fn intercept(&self, req: Request<BoxBody>) -> Result<Request<BoxBody>, Status> {
// Parse metadata for a bearer token
let bearer_token = match req.metadata().get("authorization") {
Some(authorization) => {
let authorization = authorization
.to_str()
.map_err(|e| Status::new(Code::InvalidArgument, e.to_string()))?;
match authorization.strip_prefix("Bearer ") {
Some(token) => token.to_string(),
_ => {
return Err(Status::unauthenticated(
"Unsupported authorization mechanism; use Bearer",
))
}
}
}
_ => return Err(Status::unauthenticated("An authorization header is required")),
};
// Lookup a matching API Key
...
Is gRPC metadata nothing more than another name for http headers?
@drauschenbach I think they're the same in the context of tonic
.
You can get the bearer token with something like:
match request.headers().get("authorization").map(|v| v.to_str()) {
Some(Ok(raw)) => {
let token_str = raw.strip_prefix("Bearer ").ok_or(Status::invalid_argument(
"Authorization header missing 'Bearer ' prefix",
))?;
...
Is gRPC metadata nothing more than another name for http headers?
Basically yes, metadata is wrapper around headers. From tonic source code:
#[derive(Clone, Debug, Default)]
pub struct MetadataMap {
headers: http::HeaderMap,
}
Getting some breaking changes when updating to tonic 0.12.