Impossible to use `tonic-web` (0.12.3) while multiplexing with `axum` (0.7.7) without using the deprecated `Router::into_router()`?
- Dominant language
- Rust
- Stars
- 12.5k
- Forks
- 1.3k
- Avg merge
- 4d 7h
- Merged PRs (30d)
- 24
Description
## Bug Report
### Version
```toml
axum = { version = "0.7.7", features = ["macros"] }
hyper = { version = "1.4.1", features = ["http1", "http2", "server"] }
tonic = "0.12.3"
tonic-web = "0.12.3"
tower = { version = "0.5.1", features = ["steer"] }
tower-http = { version = "0.6.1", features = ["cors", "trace"] }
```
### Platform
N/A
### Crates
```toml
tonic = "0.12.3"
tonic-web = "0.12.3"
```
### Description
I'm working on upgrading some of my web services which use both gRPC (via `tonic`) and regular REST endpoints (via `axum`) and cannot find a way to implement the `GrpcWebLayer` without using the deprecated `tonic::transport::server::Router::into_router()` method because of mismatched body types.
Compiles:
```rust
let grpc = tonic::transport::Server::builder()
.accept_http1(true)
.layer(GrpcWebLayer::new())
.add_service(NodeMapServer::new(NodeMapServerImpl::new(databases)))
.into_router();
let service =
tower::steer::Steer::new([rest, grpc], |req: &hyper::Request<_>, _services: &[_]| {
if req
.headers()
.get(hyper::header::CONTENT_TYPE)
.map(|content_type| content_type.as_bytes())
.filter(|content_type| content_type.starts_with(b"application/grpc"))
.is_some()
{
// route to the gRPC service (second service element) when the
// header is set
1
} else {
// otherwise route to the REST service
0
}
});
let service = tower::ServiceBuilder::new()
.layer(
tower_http::cors::CorsLayer::new()
.allow_origin(tower_http::cors::Any)
.allow_headers(tower_http::cors::Any)
.expose_headers(tower_http::cors::Any),
)
.service(service);
axum::serve(listener, tower::make::Shared::new(service))
.with_graceful_shutdown(async move {
let _ = shutdown_rx.changed().await;
})
.await?;
```
Does not compile:
```rust
let grpc = tonic::service::Routes::new(NodeMapServer::new(NodeMapServerImpl::new(databases)))
.prepare()
.into_axum_router();
let service =
tower::steer::Steer::new([rest, grpc], |req: &hyper::Request<_>, _services: &[_]| {
if req
.headers()
.get(hyper::header::CONTENT_TYPE)
.map(|content_type| content_type.as_bytes())
.filter(|content_type| content_type.starts_with(b"application/grpc"))
.is_some()
{
// route to the gRPC service (second service element) when the
// header is set
1
} else {
// otherwise route to the REST service
0
}
});
let service = tower::ServiceBuilder::new()
.layer(
tower_http::cors::CorsLayer::new()
.allow_origin(tower_http::cors::Any)
.allow_headers(tower_http::cors::Any)
.expose_headers(tower_http::cors::Any),
)
// is this even the right place to do this?
.layer(GrpcWebLayer::new())
.service(service);
axum::serve(listener, tower::make::Shared::new(service))
.with_graceful_shutdown(async move {
let _ = shutdown_rx.changed().await;
})
.await?;
```
```rust
error[E0271]: type mismatch resolving `>> as Service>>>::Response == Response>`
--> src/main.rs:222:10
|
222 | .service(service);
| ^^^^^^^ expected `Response`, found `Response>`
|
= note: expected struct `hyper::Response`
found struct `hyper::Response>`
= note: required for `GrpcWebLayer` to implement `tower::Layer>>>`
= note: 1 redundant requirement hidden
= note: required for `Stack>` to implement `tower::Layer>>>`
error[E0271]: type mismatch resolving `>> as Service>>>::Response == Response>`
--> src/main.rs:222:18
|
222 | .service(service);
| ------- ^^^^^^^ expected `Response>`, found `Response`
| |
| required by a bound introduced by this call
|
= note: expected struct `hyper::Response>`
found struct `hyper::Response`
= note: required for `GrpcWebLayer` to implement `tower::Layer>>>`
= note: 1 redundant requirement hidden
= note: required for `Stack>` to implement `tower::Layer>>>`
error[E0277]: the trait bound `GrpcWebService>>>: tower::Service>` is not satisfied
--> src/main.rs:241:5
|
241 | axum::serve(listener, tower::make::Shared::new(service))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `tower::Service>` is not implemented for `GrpcWebService>>>`, which is required by `Cors>>>>: tower::Service>`
|
= help: the trait `tower::Service>>` is implemented for `GrpcWebService`
= note: required for `Cors>>>>` to implement `tower::Service>`
```
am I missing something obvious? I'd like to not use the deprecated method if possible, but it doesn't seem possible to do so otherwise. is there a workaround available at this point or do I need for `tonic-web` to be changed to be generic over the body type? (https://github.com/hyperium/tonic/issues/1361)
Contributor guide
Assessment
This issue has not been assessed yet.