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