Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ http = "1"
hyper = { version = "1", default-features = false }
hyper-util = { version = "0.1", default-features = false, features = ["client-legacy", "tokio"] }
log = { version = "0.4.4", optional = true }
pki-types = { package = "rustls-pki-types", version = "1" }
pki-types = { package = "rustls-pki-types", version = "1", features = ["std"] }
rustls-native-certs = { version = "0.8", optional = true }
rustls-platform-verifier = { version = "0.6", optional = true }
rustls = { version = "0.23", default-features = false }
Expand All @@ -41,7 +41,6 @@ cfg-if = "1"
http-body-util = "0.1"
hyper-util = { version = "0.1", default-features = false, features = ["server-auto"] }
rustls = { version = "0.23", default-features = false, features = ["tls12"] }
rustls-pemfile = "2"
tokio = { version = "1.0", features = ["io-std", "macros", "net", "rt-multi-thread"] }

[[example]]
Expand Down
92 changes: 37 additions & 55 deletions examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,24 @@
//!
//! 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 pki_types::{pem::PemObject, CertificateDer};
use rustls::{ClientConfig, 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<()> {
async fn main() -> io::Result<()> {
// Set a process wide default crypto provider.
#[cfg(feature = "ring")]
let _ = rustls::crypto::ring::default_provider().install_default();
Expand All @@ -34,39 +28,29 @@ async fn run_client() -> io::Result<()> {

// 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 <url> <ca_store>");
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,
Some(u) => Uri::from_str(&u).map_err(|e| error(e.to_string()))?,
None => return Ok(()),
};

// Prepare the TLS client config
let tls = match ca {
Some(ref mut rd) => {
// Read trust roots
let certs = rustls_pemfile::certs(rd).collect::<Result<Vec<_>, _>>()?;
let tls = match env::args().nth(2) {
Some(path) => {
let data = fs::read(&path).map_err(|e| error(format!("failed to open {path}: {e}")))?;

let mut roots = RootCertStore::empty();
roots.add_parsable_certificates(certs);
// TLS client config using the custom CA store for lookups
rustls::ClientConfig::builder()
for cert in CertificateDer::pem_slice_iter(&data) {
let cert = cert.map_err(|e| error(format!("invalid PEM: {e}")))?;
roots.add_parsable_certificates([cert]);
}

ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth()
}
// Default TLS client config with native roots
None => rustls::ClientConfig::builder()
.with_native_roots()?
None => ClientConfig::builder()
.with_native_roots()
.map_err(|e| error(e.to_string()))?
.with_no_client_auth(),
};
// Prepare the HTTPS connector
Expand All @@ -79,27 +63,25 @@ async fn run_client() -> io::Result<()> {
// Build the hyper client from the HTTPS connector.
let client: Client<_, Empty<Bytes>> = 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());
// Send the request and print the response.
let res = client
.get(url)
.await
.map_err(|e| error(format!("request failed: {e:?}")))?;

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));
let status = res.status();
let headers = res.headers().clone();

Ok(())
};
let body = res
.into_body()
.collect()
.await
.map_err(|e| error(format!("body error: {e:?}")))?
.to_bytes();

println!("Status:\n{status}");
println!("Headers:\n{headers:#?}");
println!("Body:\n{}", String::from_utf8_lossy(&body));

fut.await
Ok(())
}
18 changes: 8 additions & 10 deletions examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use hyper::body::{Bytes, Incoming};
use hyper::service::service_fn;
use hyper_util::rt::{TokioExecutor, TokioIo};
use hyper_util::server::conn::auto::Builder;
use pki_types::{CertificateDer, PrivateKeyDer};
use pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer};
use rustls::ServerConfig;
use tokio::net::TcpListener;
use tokio_rustls::TlsAcceptor;
Expand Down Expand Up @@ -114,25 +114,23 @@ async fn echo(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Er
};
Ok(response)
}

// Load public certificate from file.
fn load_certs(filename: &str) -> io::Result<Vec<CertificateDer<'static>>> {
// Open certificate file.
let certfile =
fs::File::open(filename).map_err(|e| error(format!("failed to open {filename}: {e}")))?;
let mut reader = io::BufReader::new(certfile);
let data = fs::read(filename).map_err(|e| error(format!("failed to open {filename}: {e}")))?;

// Load and return certificate.
rustls_pemfile::certs(&mut reader).collect()
CertificateDer::pem_slice_iter(&data)
.map(|cert| cert.map_err(|e| error(format!("invalid PEM in {filename}: {e}"))))
.collect()
}

// Load private key from file.
fn load_private_key(filename: &str) -> io::Result<PrivateKeyDer<'static>> {
// Open keyfile.
let keyfile =
fs::File::open(filename).map_err(|e| error(format!("failed to open {filename}: {e}")))?;
let mut reader = io::BufReader::new(keyfile);
let data = fs::read(filename).map_err(|e| error(format!("failed to open {filename}: {e}")))?;

// Load and return a single private key.
rustls_pemfile::private_key(&mut reader).map(|key| key.unwrap())
PrivateKeyDer::from_pem_slice(&data)
.map_err(|e| error(format!("invalid private key in {filename}: {e}")))
}