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