xref: /tonic/tonic-web/src/lib.rs (revision ff6a690c)
1 //! grpc-web protocol translation for [`tonic`] services.
2 //!
3 //! [`tonic_web`] enables tonic servers to handle requests from [grpc-web] clients directly,
4 //! without the need of an external proxy. It achieves this by wrapping individual tonic services
5 //! with a [tower] service that performs the translation between protocols and handles `cors`
6 //! requests.
7 //!
8 //! ## Getting Started
9 //!
10 //! ```toml
11 //! [dependencies]
12 //! tonic_web = "0.1"
13 //! ```
14 //!
15 //! ## Enabling tonic services
16 //!
17 //! The easiest way to get started, is to call the [`enable`] function with your tonic service
18 //! and allow the tonic server to accept HTTP/1.1 requests:
19 //!
20 //! ```ignore
21 //! #[tokio::main]
22 //! async fn main() -> Result<(), Box<dyn std::error::Error>> {
23 //!     let addr = "[::1]:50051".parse().unwrap();
24 //!     let greeter = GreeterServer::new(MyGreeter::default());
25 //!
26 //!     Server::builder()
27 //!        .accept_http1(true)
28 //!        .add_service(tonic_web::enable(greeter))
29 //!        .serve(addr)
30 //!        .await?;
31 //!
32 //!    Ok(())
33 //! }
34 //!
35 //! ```
36 //! This will apply a default configuration that works well with grpc-web clients out of the box.
37 //! See the [`Config`] documentation for details.
38 //!
39 //! Alternatively, if you have a tls enabled server, you could skip setting `accept_http1` to `true`.
40 //! This works because the browser will handle `ALPN`.
41 //!
42 //! ```ignore
43 //! #[tokio::main]
44 //! async fn main() -> Result<(), Box<dyn std::error::Error>> {
45 //!     let cert = tokio::fs::read("server.pem").await?;
46 //!     let key = tokio::fs::read("server.key").await?;
47 //!     let identity = Identity::from_pem(cert, key);
48 //!
49 //!     let addr = "[::1]:50051".parse().unwrap();
50 //!     let greeter = GreeterServer::new(MyGreeter::default());
51 //!
52 //!     // No need to enable HTTP/1
53 //!     Server::builder()
54 //!        .tls_config(ServerTlsConfig::new().identity(identity))?
55 //!        .add_service(tonic_web::enable(greeter))
56 //!        .serve(addr)
57 //!        .await?;
58 //!
59 //!    Ok(())
60 //! }
61 //! ```
62 //!
63 //! ## Limitations
64 //!
65 //! * `tonic_web` is designed to work with grpc-web-compliant clients only. It is not expected to
66 //! handle arbitrary HTTP/x.x requests or bespoke protocols.
67 //! * Similarly, the cors support implemented  by this crate will *only* handle grpc-web and
68 //! grpc-web preflight requests.
69 //! * Currently, grpc-web clients can only perform `unary` and `server-streaming` calls. These
70 //! are the only requests this crate is designed to handle. Support for client and bi-directional
71 //! streaming will be officially supported when clients do.
72 //! * There is no support for web socket transports.
73 //!
74 //!
75 //! [`tonic`]: https://github.com/hyperium/tonic
76 //! [`tonic_web`]: https://github.com/hyperium/tonic
77 //! [grpc-web]: https://github.com/grpc/grpc-web
78 //! [tower]: https://github.com/tower-rs/tower
79 //! [`enable`]: crate::enable()
80 //! [`Config`]: crate::Config
81 #![warn(
82     missing_debug_implementations,
83     missing_docs,
84     rust_2018_idioms,
85     unreachable_pub
86 )]
87 
88 pub use config::Config;
89 
90 mod call;
91 mod config;
92 mod cors;
93 mod service;
94 
95 use crate::service::GrpcWeb;
96 use std::future::Future;
97 use std::pin::Pin;
98 use tonic::body::BoxBody;
99 use tonic::transport::NamedService;
100 use tower_service::Service;
101 
102 /// enable a tonic service to handle grpc-web requests with the default configuration.
103 ///
104 /// Shortcut for `tonic_web::config().enable(service)`
105 pub fn enable<S>(service: S) -> GrpcWeb<S>
106 where
107     S: Service<http::Request<hyper::Body>, Response = http::Response<BoxBody>>,
108     S: NamedService + Clone + Send + 'static,
109     S::Future: Send + 'static,
110     S::Error: Into<BoxError> + Send,
111 {
112     config().enable(service)
113 }
114 
115 /// returns a default [`Config`] instance for configuring services.
116 ///
117 /// ## Example
118 ///
119 /// ```
120 /// let config = tonic_web::config()
121 ///      .allow_origins(vec!["http://foo.com"])
122 ///      .allow_credentials(false)
123 ///      .expose_headers(vec!["x-request-id"]);
124 ///
125 /// // let greeter = config.enable(Greeter);
126 /// // let route_guide = config.enable(RouteGuide);
127 /// ```
128 pub fn config() -> Config {
129     Config::default()
130 }
131 
132 type BoxError = Box<dyn std::error::Error + Send + Sync>;
133 type BoxFuture<T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + Send>>;
134