xref: /tonic/examples/src/tls_rustls/client.rs (revision c8754f3a)
1 //! This examples shows how you can combine `hyper-rustls` and `tonic` to
2 //! provide a custom `ClientConfig` for the tls configuration.
3 
4 pub mod pb {
5     tonic::include_proto!("/grpc.examples.unaryecho");
6 }
7 
8 use hyper::Uri;
9 use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
10 use pb::{echo_client::EchoClient, EchoRequest};
11 use tokio_rustls::rustls::{ClientConfig, RootCertStore};
12 
13 #[tokio::main]
14 async fn main() -> Result<(), Box<dyn std::error::Error>> {
15     let data_dir = std::path::PathBuf::from_iter([std::env!("CARGO_MANIFEST_DIR"), "data"]);
16     let fd = std::fs::File::open(data_dir.join("tls/ca.pem"))?;
17 
18     let mut roots = RootCertStore::empty();
19 
20     let mut buf = std::io::BufReader::new(&fd);
21     let certs = rustls_pemfile::certs(&mut buf).collect::<Result<Vec<_>, _>>()?;
22     roots.add_parsable_certificates(certs.into_iter());
23 
24     let tls = ClientConfig::builder()
25         .with_root_certificates(roots)
26         .with_no_client_auth();
27 
28     let mut http = HttpConnector::new();
29     http.enforce_http(false);
30 
31     // We have to do some wrapping here to map the request type from
32     // `https://example.com` -> `https://[::1]:50051` because `rustls`
33     // doesn't accept ip's as `ServerName`.
34     let connector = tower::ServiceBuilder::new()
35         .layer_fn(move |s| {
36             let tls = tls.clone();
37 
38             hyper_rustls::HttpsConnectorBuilder::new()
39                 .with_tls_config(tls)
40                 .https_or_http()
41                 .enable_http2()
42                 .wrap_connector(s)
43         })
44         // Since our cert is signed with `example.com` but we actually want to connect
45         // to a local server we will override the Uri passed from the `HttpsConnector`
46         // and map it to the correct `Uri` that will connect us directly to the local server.
47         .map_request(|_| Uri::from_static("https://[::1]:50051"))
48         .service(http);
49 
50     let client = hyper_util::client::legacy::Client::builder(TokioExecutor::new()).build(connector);
51 
52     // Using `with_origin` will let the codegenerated client set the `scheme` and
53     // `authority` from the provided `Uri`.
54     let uri = Uri::from_static("https://example.com");
55     let mut client = EchoClient::with_origin(client, uri);
56 
57     let request = tonic::Request::new(EchoRequest {
58         message: "hello".into(),
59     });
60 
61     let response = client.unary_echo(request).await?;
62 
63     println!("RESPONSE={:?}", response);
64 
65     Ok(())
66 }
67