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