Skip to content

tonic: inject an GrpcMethod extension value to request #1202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions interop/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ pub struct EchoHeadersSvc<S> {

impl<S: NamedService> NamedService for EchoHeadersSvc<S> {
const NAME: &'static str = S::NAME;
fn grpc_method(path: &str) -> Option<tonic::GrpcMethod> {
S::grpc_method(path)
}
}

impl<S> EchoHeadersSvc<S> {
Expand Down
3 changes: 3 additions & 0 deletions tests/integration_tests/tests/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,7 @@ where

impl<S: NamedService> NamedService for InterceptedService<S> {
const NAME: &'static str = S::NAME;
fn grpc_method(path: &str) -> Option<tonic::GrpcMethod> {
S::grpc_method(path)
}
}
15 changes: 13 additions & 2 deletions tests/integration_tests/tests/interceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use tonic::{

#[tokio::test]
async fn interceptor_retrieves_grpc_method() {
use test_server::Test;
use test_server::{Test, TEST_SERVICE_NAME};

struct Svc;

Expand All @@ -20,7 +20,18 @@ async fn interceptor_retrieves_grpc_method() {
}
}

let svc = test_server::TestServer::new(Svc);
fn server_intercept(req: Request<()>) -> Result<Request<()>, Status> {
println!("Intercepting server request: {:?}", req);

let gm = req.extensions().get::<GrpcMethod>().unwrap();
assert_eq!(gm.service(), "test.Test");
assert_eq!(gm.service(), TEST_SERVICE_NAME);
assert_eq!(gm.method(), "UnaryCall");
assert_eq!(gm.method(), Svc::UNARY_CALL);

Ok(req)
}
let svc = test_server::TestServer::with_interceptor(Svc, server_intercept);

let (tx, rx) = oneshot::channel();
// Start the server now, second call should succeed
Expand Down
35 changes: 32 additions & 3 deletions tonic-build/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ pub(crate) fn generate_internal<T: Service>(
);

let server_service = quote::format_ident!("{}Server", service.name());
let server_service_name = quote::format_ident!(
"{}_SERVICE_NAME",
naive_snake_case(service.name()).to_uppercase()
);
let server_trait = quote::format_ident!("{}", service.name());
let server_mod = quote::format_ident!("{}_server", naive_snake_case(service.name()));
let generated_trait = generate_trait(
Expand All @@ -71,7 +75,7 @@ pub(crate) fn generate_internal<T: Service>(
generate_doc_comments(service.comment())
};

let named = generate_named(&server_service, &server_trait, &service_name);
let named = generate_named(service, &server_service, &server_trait, &service_name);
let mod_attributes = attributes.for_mod(package);
let struct_attributes = attributes.for_struct(&service_name);

Expand Down Expand Up @@ -124,6 +128,8 @@ pub(crate) fn generate_internal<T: Service>(
)]
use tonic::codegen::*;

pub const #server_service_name : &str = #service_name;

#generated_trait

#service_doc
Expand Down Expand Up @@ -271,7 +277,9 @@ fn generate_trait_methods<T: Service>(
let mut stream = TokenStream::new();

for method in service.methods() {
let method_name = method.identifier();
let name = quote::format_ident!("{}", method.name());
let upper_name = quote::format_ident!("{}", method.name().to_uppercase());

let (req_message, res_message) =
method.request_response_name(proto_path, compile_well_known_types);
Expand All @@ -283,6 +291,9 @@ fn generate_trait_methods<T: Service>(
generate_doc_comments(method.comment())
};

stream.extend(quote! {
const #upper_name: &'static str = #method_name;
});
let self_param = if use_arc_self {
quote!(self: std::sync::Arc<Self>)
} else {
Expand Down Expand Up @@ -344,16 +355,34 @@ fn generate_trait_methods<T: Service>(
stream
}

fn generate_named(
fn generate_named<T: Service>(
service: &T,
server_service: &syn::Ident,
server_trait: &syn::Ident,
service_name: &str,
) -> TokenStream {
let mut stream = TokenStream::new();
for method in service.methods() {
let method_name = method.identifier();
let path = format!("/{}/{}", service_name, method_name);
let method_path = Lit::Str(LitStr::new(&path, Span::call_site()));
let method = quote! {
#method_path => {
Some(GrpcMethod::new(#service_name, #method_name))
}
};
stream.extend(method);
}
let service_name = syn::LitStr::new(service_name, proc_macro2::Span::call_site());

quote! {
impl<T: #server_trait> tonic::server::NamedService for #server_service<T> {
const NAME: &'static str = #service_name;
fn grpc_method(path: &str) -> Option<GrpcMethod> {
match path {
#stream
_ => None
}
}
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions tonic-health/src/generated/grpc.health.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,11 @@ pub mod health_client {
pub mod health_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::*;
pub const HEALTH_SERVICE_NAME: &str = "grpc.health.v1.Health";
/// Generated trait containing gRPC methods that should be implemented for use with HealthServer.
#[async_trait]
pub trait Health: Send + Sync + 'static {
const CHECK: &'static str = "Check";
/// If the requested service is unknown, the call will fail with status
/// NOT_FOUND.
async fn check(
Expand All @@ -215,6 +217,7 @@ pub mod health_server {
tonic::Response<super::HealthCheckResponse>,
tonic::Status,
>;
const WATCH: &'static str = "Watch";
/// Server streaming response type for the Watch method.
type WatchStream: futures_core::Stream<
Item = std::result::Result<super::HealthCheckResponse, tonic::Status>,
Expand Down Expand Up @@ -452,5 +455,16 @@ pub mod health_server {
}
impl<T: Health> tonic::server::NamedService for HealthServer<T> {
const NAME: &'static str = "grpc.health.v1.Health";
fn grpc_method(path: &str) -> Option<GrpcMethod> {
match path {
"/grpc.health.v1.Health/Check" => {
Some(GrpcMethod::new("grpc.health.v1.Health", "Check"))
}
"/grpc.health.v1.Health/Watch" => {
Some(GrpcMethod::new("grpc.health.v1.Health", "Watch"))
}
_ => None,
}
}
}
}
15 changes: 15 additions & 0 deletions tonic-reflection/src/generated/grpc.reflection.v1alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,11 @@ pub mod server_reflection_client {
pub mod server_reflection_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::*;
pub const SERVER_REFLECTION_SERVICE_NAME: &str = "grpc.reflection.v1alpha.ServerReflection";
/// Generated trait containing gRPC methods that should be implemented for use with ServerReflectionServer.
#[async_trait]
pub trait ServerReflection: Send + Sync + 'static {
const SERVER_REFLECTION_INFO: &'static str = "ServerReflectionInfo";
/// Server streaming response type for the ServerReflectionInfo method.
type ServerReflectionInfoStream: futures_core::Stream<
Item = std::result::Result<
Expand Down Expand Up @@ -460,5 +462,18 @@ pub mod server_reflection_server {
}
impl<T: ServerReflection> tonic::server::NamedService for ServerReflectionServer<T> {
const NAME: &'static str = "grpc.reflection.v1alpha.ServerReflection";
fn grpc_method(path: &str) -> Option<GrpcMethod> {
match path {
"/grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo" => {
Some(
GrpcMethod::new(
"grpc.reflection.v1alpha.ServerReflection",
"ServerReflectionInfo",
),
)
}
_ => None,
}
}
}
}
3 changes: 3 additions & 0 deletions tonic-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ where
S: NamedService,
{
const NAME: &'static str = S::NAME;
fn grpc_method(path: &str) -> Option<tonic::GrpcMethod> {
S::grpc_method(path)
}
}

pub(crate) mod util {
Expand Down
7 changes: 7 additions & 0 deletions tonic-web/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ where

impl<S: NamedService> NamedService for GrpcWebService<S> {
const NAME: &'static str = S::NAME;

fn grpc_method(path: &str) -> Option<tonic::GrpcMethod> {
S::grpc_method(path)
}
}

impl<'a> RequestKind<'a> {
Expand Down Expand Up @@ -263,6 +267,9 @@ mod tests {

impl NamedService for Svc {
const NAME: &'static str = "test";
fn grpc_method(_: &str) -> Option<tonic::GrpcMethod> {
None
}
}

mod grpc_web {
Expand Down
4 changes: 4 additions & 0 deletions tonic/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub use self::grpc::Grpc;
pub use self::service::{
ClientStreamingService, ServerStreamingService, StreamingService, UnaryService,
};
pub use crate::extensions::GrpcMethod;

/// A trait to provide a static reference to the service's
/// name. This is used for routing service's within the router.
Expand All @@ -23,4 +24,7 @@ pub trait NamedService {
///
/// [here]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
const NAME: &'static str;

/// Use to get gRPC method name from the path of uri
fn grpc_method(path: &str) -> Option<GrpcMethod>;
}
3 changes: 3 additions & 0 deletions tonic/src/service/interceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ where
S: crate::server::NamedService,
{
const NAME: &'static str = S::NAME;
fn grpc_method(path: &str) -> Option<crate::GrpcMethod> {
S::grpc_method(path)
}
}

/// Response future for [`InterceptedService`].
Expand Down
4 changes: 4 additions & 0 deletions tonic/src/transport/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ pub struct Router<L = Identity> {

impl<S: NamedService, T> NamedService for Either<S, T> {
const NAME: &'static str = S::NAME;

fn grpc_method(path: &str) -> Option<crate::GrpcMethod> {
S::grpc_method(path)
}
}

impl Server {
Expand Down
5 changes: 5 additions & 0 deletions tonic/src/transport/service/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ impl Routes {
S::Future: Send + 'static,
S::Error: Into<crate::Error> + Send,
{
// inject the GrpcMethod extension value if it is gRPC request.
let svc = svc.map_request(|mut req: Request<_>| {
S::grpc_method(req.uri().path()).and_then(|val| req.extensions_mut().insert(val));
req
});
let svc = svc.map_response(|res| res.map(axum::body::boxed));
self.router = self
.router
Expand Down