Skip to main content

tonic_web/
client.rs

1/*
2 *
3 * Copyright 2025 gRPC authors.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24
25use http::header::CONTENT_TYPE;
26use http::{Request, Response, Version};
27use pin_project::pin_project;
28use std::fmt;
29use std::future::Future;
30use std::pin::Pin;
31use std::task::{Context, Poll, ready};
32use tower_layer::Layer;
33use tower_service::Service;
34use tracing::debug;
35
36use crate::call::GrpcWebCall;
37use crate::call::content_types::GRPC_WEB;
38
39/// Layer implementing the grpc-web protocol for clients.
40#[derive(Debug, Default, Clone)]
41pub struct GrpcWebClientLayer {
42    _priv: (),
43}
44
45impl GrpcWebClientLayer {
46    /// Create a new grpc-web for clients layer.
47    pub fn new() -> GrpcWebClientLayer {
48        Self::default()
49    }
50}
51
52impl<S> Layer<S> for GrpcWebClientLayer {
53    type Service = GrpcWebClientService<S>;
54
55    fn layer(&self, inner: S) -> Self::Service {
56        GrpcWebClientService::new(inner)
57    }
58}
59
60/// A [`Service`] that wraps some inner http service that will
61/// coerce requests coming from [`tonic::client::Grpc`] into proper
62/// `grpc-web` requests.
63#[derive(Debug, Clone)]
64pub struct GrpcWebClientService<S> {
65    inner: S,
66}
67
68impl<S> GrpcWebClientService<S> {
69    /// Create a new grpc-web for clients service.
70    pub fn new(inner: S) -> Self {
71        Self { inner }
72    }
73}
74
75impl<S, B1, B2> Service<Request<B1>> for GrpcWebClientService<S>
76where
77    S: Service<Request<GrpcWebCall<B1>>, Response = Response<B2>>,
78{
79    type Response = Response<GrpcWebCall<B2>>;
80    type Error = S::Error;
81    type Future = ResponseFuture<S::Future>;
82
83    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
84        self.inner.poll_ready(cx)
85    }
86
87    fn call(&mut self, mut req: Request<B1>) -> Self::Future {
88        if req.version() == Version::HTTP_2 {
89            debug!("coercing HTTP2 request to HTTP1.1");
90
91            *req.version_mut() = Version::HTTP_11;
92        }
93
94        req.headers_mut()
95            .insert(CONTENT_TYPE, GRPC_WEB.try_into().unwrap());
96
97        let req = req.map(GrpcWebCall::client_request);
98
99        let fut = self.inner.call(req);
100
101        ResponseFuture { inner: fut }
102    }
103}
104
105/// Response future for the [`GrpcWebService`](crate::GrpcWebService).
106#[pin_project]
107#[must_use = "futures do nothing unless polled"]
108pub struct ResponseFuture<F> {
109    #[pin]
110    inner: F,
111}
112
113impl<F, B, E> Future for ResponseFuture<F>
114where
115    F: Future<Output = Result<Response<B>, E>>,
116{
117    type Output = Result<Response<GrpcWebCall<B>>, E>;
118
119    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
120        let res = ready!(self.project().inner.poll(cx));
121
122        Poll::Ready(res.map(|r| r.map(GrpcWebCall::client_response)))
123    }
124}
125
126impl<F> fmt::Debug for ResponseFuture<F> {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        f.debug_struct("ResponseFuture").finish()
129    }
130}