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