Here's a working example that might help someone in the future.
Updated to use hyper_util::rt::tokio::TokioExecutor
use core::panic;
use http_body_util::{combinators::BoxBody, BodyExt, Empty};
use hyper::body::Bytes;
use hyper::Request;
use hyper_util::rt::TokioIo;
use log::debug;
use std::{net::ToSocketAddrs, sync::Arc};
use tokio::net::TcpStream;
use tokio_rustls::{rustls, TlsConnector};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set log level to TRACE to see detailed information
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.init();
let domain = "www.google.com";
let uri = format!("https://{}/", domain);
let target_host = match format!("{}:443", domain).to_socket_addrs() {
Ok(socket_ip) => socket_ip.into_iter().next().unwrap(),
Err(e) => {
panic!("DNS resolution error: {}", e);
}
};
let mut root_cert_store = rustls::RootCertStore::empty();
root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let mut config = rustls::ClientConfig::builder()
.with_root_certificates(root_cert_store)
.with_no_client_auth();
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()];
let connector = TlsConnector::from(Arc::new(config));
let tcp_stream = TcpStream::connect(target_host).await?;
let tls_domain = rustls_pki_types::ServerName::try_from(domain)
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid dnsname"))?
.to_owned();
let stream = connector.connect(tls_domain, tcp_stream).await?;
let io = TokioIo::new(stream);
let executor = hyper_util::rt::tokio::TokioExecutor::new();
let (mut sender, conn) = hyper::client::conn::http2::handshake(executor, io).await?;
tokio::task::spawn(async move {
if let Err(e) = conn.await {
println!("Error: {:?}", e);
}
});
let upstream_request = Request::builder()
.uri(uri)
.header("user-agent", "hyper-client-http2")
.version(hyper::Version::HTTP_2)
.body(Empty::<Bytes>::new())?;
debug!("Request: {:#?}", upstream_request);
let res = sender.send_request(upstream_request).await?;
debug!("Response: {:#?}", res);
let body = res.collect().await?.to_bytes();
println!("{}", String::from_utf8_lossy(&body));
Ok(())
}