use bytes::Bytes; use http::header::CONTENT_TYPE; use http::{Request, Response, Version}; use http_body::Body; use pin_project::pin_project; use std::error::Error; use std::future::Future; use std::pin::Pin; use std::task::{ready, Context, Poll}; use tower_layer::Layer; use tower_service::Service; use tracing::debug; use crate::call::content_types::GRPC_WEB; use crate::call::GrpcWebCall; /// Layer implementing the grpc-web protocol for clients. #[derive(Debug, Clone)] pub struct GrpcWebClientLayer { _priv: (), } impl GrpcWebClientLayer { /// Create a new grpc-web for clients layer. pub fn new() -> GrpcWebClientLayer { Self { _priv: () } } } impl Default for GrpcWebClientLayer { fn default() -> Self { Self::new() } } impl Layer for GrpcWebClientLayer { type Service = GrpcWebClientService; fn layer(&self, inner: S) -> Self::Service { GrpcWebClientService::new(inner) } } /// A [`Service`] that wraps some inner http service that will /// coerce requests coming from [`tonic::client::Grpc`] into proper /// `grpc-web` requests. #[derive(Debug, Clone)] pub struct GrpcWebClientService { inner: S, } impl GrpcWebClientService { /// Create a new grpc-web for clients service. pub fn new(inner: S) -> Self { Self { inner } } } impl Service> for GrpcWebClientService where S: Service>, Response = Response>, B1: Body, B2: Body, B2::Error: Error, { type Response = Response>; type Error = S::Error; type Future = ResponseFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, mut req: Request) -> Self::Future { if req.version() == Version::HTTP_2 { debug!("coercing HTTP2 request to HTTP1.1"); *req.version_mut() = Version::HTTP_11; } req.headers_mut() .insert(CONTENT_TYPE, GRPC_WEB.try_into().unwrap()); let req = req.map(GrpcWebCall::client_request); let fut = self.inner.call(req); ResponseFuture { inner: fut } } } /// Response future for the [`GrpcWebService`]. #[allow(missing_debug_implementations)] #[pin_project] #[must_use = "futures do nothing unless polled"] pub struct ResponseFuture { #[pin] inner: F, } impl Future for ResponseFuture where B: Body, F: Future, E>>, { type Output = Result>, E>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let res = ready!(self.project().inner.poll(cx)); Poll::Ready(res.map(|r| r.map(GrpcWebCall::client_response))) } }