tonic_web/lib.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
25//! grpc-web protocol translation for [`tonic`] services.
26//!
27//! [`tonic_web`] enables tonic servers to handle requests from [grpc-web] clients directly,
28//! without the need of an external proxy. It achieves this by wrapping individual tonic services
29//! with a [tower] service that performs the translation between protocols and handles `cors`
30//! requests.
31//!
32//! ## Enabling tonic services
33//!
34//! You can customize the CORS configuration composing the [`GrpcWebLayer`] with the cors layer of your choice.
35//!
36//! ```ignore
37//! #[tokio::main]
38//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
39//! let addr = "[::1]:50051".parse().unwrap();
40//! let greeter = GreeterServer::new(MyGreeter::default());
41//!
42//! Server::builder()
43//! .accept_http1(true)
44//! // This will apply the gRPC-Web translation layer
45//! .layer(GrpcWebLayer::new())
46//! .add_service(greeter)
47//! .serve(addr)
48//! .await?;
49//!
50//! Ok(())
51//! }
52//! ```
53//!
54//! Alternatively, if you have a tls enabled server, you could skip setting `accept_http1` to `true`.
55//! This works because the browser will handle `ALPN`.
56//!
57//! ```ignore
58//! #[tokio::main]
59//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
60//! let cert = tokio::fs::read("server.pem").await?;
61//! let key = tokio::fs::read("server.key").await?;
62//! let identity = Identity::from_pem(cert, key);
63//!
64//! let addr = "[::1]:50051".parse().unwrap();
65//! let greeter = GreeterServer::new(MyGreeter::default());
66//!
67//! // No need to enable HTTP/1
68//! Server::builder()
69//! .tls_config(ServerTlsConfig::new().identity(identity))?
70//! .layer(GrpcWebLayer::new())
71//! .add_service(greeter)
72//! .serve(addr)
73//! .await?;
74//!
75//! Ok(())
76//! }
77//! ```
78//!
79//! ## Limitations
80//!
81//! * `tonic_web` is designed to work with grpc-web-compliant clients only. It is not expected to
82//! handle arbitrary HTTP/x.x requests or bespoke protocols.
83//! * Similarly, the cors support implemented by this crate will *only* handle grpc-web and
84//! grpc-web preflight requests.
85//! * Currently, grpc-web clients can only perform `unary` and `server-streaming` calls. These
86//! are the only requests this crate is designed to handle. Support for client and bi-directional
87//! streaming will be officially supported when clients do.
88//! * There is no support for web socket transports.
89//!
90//!
91//! [`tonic`]: https://github.com/grpc/grpc-rust
92//! [`tonic_web`]: https://github.com/grpc/grpc-rust
93//! [grpc-web]: https://github.com/grpc/grpc-web
94//! [tower]: https://github.com/tower-rs/tower
95#![doc(issue_tracker_base_url = "https://github.com/grpc/grpc-rust/issues/")]
96
97pub use call::GrpcWebCall;
98pub use client::{GrpcWebClientLayer, GrpcWebClientService};
99pub use layer::GrpcWebLayer;
100pub use service::{GrpcWebService, ResponseFuture};
101
102mod call;
103mod client;
104mod layer;
105mod service;
106
107type BoxError = Box<dyn std::error::Error + Send + Sync>;
108
109pub(crate) mod util {
110 pub(crate) mod base64 {
111 use base64::{
112 alphabet,
113 engine::{
114 DecodePaddingMode,
115 general_purpose::{GeneralPurpose, GeneralPurposeConfig},
116 },
117 };
118
119 pub(crate) const STANDARD: GeneralPurpose = GeneralPurpose::new(
120 &alphabet::STANDARD,
121 GeneralPurposeConfig::new()
122 .with_encode_padding(true)
123 .with_decode_padding_mode(DecodePaddingMode::Indifferent),
124 );
125 }
126}