-
I'm experimenting with creating a web server that mixes So I'd love to have a single entry point (most probably axum), and when clients request on a However, adding an actual HTTP request seems inefficient since I have everything in the same process. I'd rather call the service more or less directly through tonic-web if it is possible, just skipping the additionnal TCP connection. How can I approach this problem? I see |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 11 replies
-
I'm pretty sure I found a satisfying solution. I'll describe it here as soon as I get it working as wanted. |
Beta Was this translation helpful? Give feedback.
-
After a bit a research involving some convoluted ways, it turns this is actually very easy. As let grpc_web_greeter = tonic_web::enable(GreeterServer::new(GreeterStruct));
let axum_addr = "[::]:50051".parse()?;
let web_app = axum::Router::new()
.route(
"/greeter.Greeter/*rpc",
axum::routing::any_service(grpc_web_greeter.clone())
)
.route("/", get(web_root)); Or, if you need a full example: given a syntax = "proto3";
package greeter;
service Greeter {
rpc Greet (GreetRequest) returns (GreetReply);
}
message GreetRequest {
string name = 1;
}
message GreetReply {
string answer = 1;
} You can have a combined grpc + grpc-web + "classic" web service, all on the same port, like so: use std::error::Error;
use axum::routing::get;
use greeter::{
greeter_server::{Greeter, GreeterServer},
GreetReply, GreetRequest,
};
mod greeter {
tonic::include_proto!("greeter");
}
struct GreeterStruct;
#[tonic::async_trait]
impl Greeter for GreeterStruct {
async fn greet(
&self,
req: tonic::Request<GreetRequest>,
) -> Result<tonic::Response<GreetReply>, tonic::Status> {
let name = req.into_inner().name;
println!("In greet(), name: {}", name);
let reply = GreetReply {
answer: format!("Hello {name}!").into(),
};
Ok(tonic::Response::new(reply))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let grpc_web_greeter = tonic_web::enable(GreeterServer::new(GreeterStruct));
let axum_addr = "[::]:50051".parse()?;
let web_app = axum::Router::new()
.route(
"/greeter.Greeter/*rpc",
axum::routing::any_service(grpc_web_greeter.clone())
)
.route("/", get(web_root));
axum::Server::bind(&axum_addr)
.serve(web_app.into_make_service())
.await?;
Ok(())
}
async fn web_root() -> &'static str {
"Hello World!"
} |
Beta Was this translation helpful? Give feedback.
After a bit a research involving some convoluted ways, it turns this is actually very easy. As
tonic
andtonic-web
-wrapped services implementService
fromtower-service
, you can simply create youraxum
app and wrap yourtonic-web
service inaxum::routing::any_service
. Works great, still answers direct non-web gRPC too (you probably need to have thehttp2
feature ofaxum
enabled). No need for weird url prefix, rewriting or anything: