//! Simple HTTPS GET client based on hyper-rustls //! //! First parameter is the mandatory URL to GET. //! Second parameter is an optional path to CA store. use http::Uri; use http_body_util::{BodyExt, Empty}; use hyper::body::Bytes; use hyper_rustls::ConfigBuilderExt; use hyper_util::{client::legacy::Client, rt::TokioExecutor}; use rustls::RootCertStore; use std::str::FromStr; use std::{env, fs, io}; fn main() { // Send GET request and inspect result, with proper error handling. if let Err(e) = run_client() { eprintln!("FAILED: {}", e); std::process::exit(1); } } fn error(err: String) -> io::Error { io::Error::new(io::ErrorKind::Other, err) } #[tokio::main] async fn run_client() -> io::Result<()> { // Set a process wide default crypto provider. #[cfg(feature = "ring")] let _ = rustls::crypto::ring::default_provider().install_default(); #[cfg(feature = "aws-lc-rs")] let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); // First parameter is target URL (mandatory). let url = match env::args().nth(1) { Some(ref url) => Uri::from_str(url).map_err(|e| error(format!("{}", e)))?, None => { println!("Usage: client "); return Ok(()); } }; // Second parameter is custom Root-CA store (optional, defaults to native cert store). let mut ca = match env::args().nth(2) { Some(ref path) => { let f = fs::File::open(path) .map_err(|e| error(format!("failed to open {}: {}", path, e)))?; let rd = io::BufReader::new(f); Some(rd) } None => None, }; // Prepare the TLS client config let tls = match ca { Some(ref mut rd) => { // Read trust roots let certs = rustls_pemfile::certs(rd).collect::, _>>()?; let mut roots = RootCertStore::empty(); roots.add_parsable_certificates(certs); // TLS client config using the custom CA store for lookups rustls::ClientConfig::builder() .with_root_certificates(roots) .with_no_client_auth() } // Default TLS client config with native roots None => rustls::ClientConfig::builder() .with_native_roots()? .with_no_client_auth(), }; // Prepare the HTTPS connector let https = hyper_rustls::HttpsConnectorBuilder::new() .with_tls_config(tls) .https_or_http() .enable_http1() .build(); // Build the hyper client from the HTTPS connector. let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(https); // Prepare a chain of futures which sends a GET request, inspects // the returned headers, collects the whole body and prints it to // stdout. let fut = async move { let res = client .get(url) .await .map_err(|e| error(format!("Could not get: {:?}", e)))?; println!("Status:\n{}", res.status()); println!("Headers:\n{:#?}", res.headers()); let body = res .into_body() .collect() .await .map_err(|e| error(format!("Could not get body: {:?}", e)))? .to_bytes(); println!("Body:\n{}", String::from_utf8_lossy(&body)); Ok(()) }; fut.await }