hyperium / tonic

A native gRPC client & server implementation with async/await support.
https://docs.rs/tonic
MIT License
9.32k stars 954 forks source link

gRPC-web CORS issue #1678

Closed Emulator000 closed 1 day ago

Emulator000 commented 2 months ago

Bug Report

Version

❯ cargo tree | grep tonic
│   │   │   │   └── tonic v0.11.0
│   │   │   └── tonic v0.11.0 (*)
│   │   │   ├── tonic v0.11.0 (*)
│   │   │   └── tonic-reflection v0.11.0
│   │   │       └── tonic v0.11.0 (*)
│   │   │   └── tonic-build v0.11.0
│   ├── tonic v0.11.0 (*)
├── tonic v0.11.0 (*)
├── tonic-web v0.11.0
│   ├── tonic v0.11.0 (*)

Platform

Linux

Crates

tonic-web

Description

I'm trying to use gRPC-web example provided here but I'm getting a CORS error into the browser during the API call: image

I resolved in the past this problem with this modified example configuration:

use tonic::{transport::Server, Request, Response, Status};

use hello_world::greeter_server::{Greeter, GreeterServer};
use hello_world::{HelloReply, HelloRequest};
use once_cell::sync::Lazy;

const DEFAULT_MAX_AGE: Duration = Duration::from_secs(24 * 60 * 60);
const DEFAULT_EXPOSED_HEADERS: [HeaderName; 3] = [
    HeaderName::from_static("grpc-status"),
    HeaderName::from_static("grpc-message"),
    HeaderName::from_static("grpc-status-details-bin"),
];

static GRPC_WEB_CORS: Lazy<CorsLayer> = Lazy::new(|| {
    CorsLayer::new()
        .allow_origin(AllowOrigin::mirror_request())
        .allow_headers(AllowHeaders::mirror_request())
        .allow_methods(AllowMethods::mirror_request())
        .allow_credentials(true)
        .max_age(DEFAULT_MAX_AGE)
        .expose_headers(ExposeHeaders::from(DEFAULT_EXPOSED_HEADERS))
});

pub mod hello_world {
    tonic::include_proto!("helloworld");
}

#[derive(Default)]
pub struct MyGreeter {}

#[tonic::async_trait]
impl Greeter for MyGreeter {
    async fn say_hello(
        &self,
        request: Request<HelloRequest>,
    ) -> Result<Response<HelloReply>, Status> {
        println!("Got a request from {:?}", request.remote_addr());

        let reply = hello_world::HelloReply {
            message: format!("Hello {}!", request.into_inner().name),
        };
        Ok(Response::new(reply))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt::init();

    let addr = "127.0.0.1:3000".parse().unwrap();

    let greeter = MyGreeter::default();
    let greeter = GreeterServer::new(greeter);

    println!("GreeterServer listening on {}", addr);

    Server::builder()
        // GrpcWeb is over http1 so we must enable it.
        .accept_http1(true)
        .layer(GrpcWebLayer::new())
        .layer(GRPC_WEB_CORS)
        .add_service(tonic_web::enable(greeter))
        .serve(addr)
        .await?;

    Ok(())
}

but within the new version I'm getting this compiler error:

    |
104 |         .serve(
    |          ^^^^^ the trait `Service<http::request::Request<tonic::transport::Body>>` is not implemented for `Cors<Routes>`, which is required by `Stack<GrpcWebLayer, tower::layer::util::Identity>: Layer<Cors<Routes>>`
    |
    = help: the trait `Service<http::Request<ReqBody>>` is implemented for `Cors<S>`
    = note: required for `GrpcWebLayer` to implement `Layer<Cors<Routes>>`
    = note: 1 redundant requirement hidden
    = note: required for `Stack<GrpcWebLayer, tower::layer::util::Identity>` to implement `Layer<Cors<Routes>>`

so I can't use it anymore and I've to relay entirely into the tonic_web::enable feature that is designed for the gRPC-web CORS problem.

repnop commented 1 month ago

just ran into this myself, the problem is (noted by another user in #1636) that the version of tower-http that tonic-web is using is 0.4.4 as of the latest published release (0.11.0), so you need to manually downgrade the tower-http version in your crate to match that 0.4.4 version, which will then compile. it fails because of the major version mismatch which means there's two Service traits (one from the tower-http you have as a dependency, and one from the version of tower-http that tonic-web has as a dependency) which means its not implemented for the one you need.