Skip to content

Commit

Permalink
Update hyper to 1.x and integrate with hyper-util
Browse files Browse the repository at this point in the history
This updates the entire dependency stack to hyper 1.x and http 1.x.

The tokio-runtime feature was removed since it no longer serves a
purpose and tokio is a hard dependency.

Since hyper 1.x removed the high-level server API that supported
customizing the accept process of the server via an Accept trait, the
acceptor in hyper-rustls is removed. The server example however was
updated to showcase how rustls can be integrated with the low-level
hyper 1.x API.

Signed-off-by: Jens Reidel <[email protected]>
  • Loading branch information
Gelbpunkt authored and cpu committed Jan 11, 2024
1 parent def00ca commit e19a299
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 406 deletions.
23 changes: 12 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 11,33 @@ repository = "https://github.com/rustls/hyper-rustls"
documentation = "https://docs.rs/hyper-rustls/"

[dependencies]
http = "0.2"
hyper = { version = "0.14", default-features = false, features = ["client"] }
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" }
rustls-native-certs = { version = "0.7", optional = true }
rustls = { version = "0.22", default-features = false }
tokio = "1.0"
tokio-rustls = { version = "0.25", default-features = false }
tower-service = "0.3"
webpki-roots = { version = "0.26", optional = true }
futures-util = { version = "0.3", default-features = false }

[dev-dependencies]
hyper = { version = "0.14", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", default-features = false, features = ["server-auto"] }
rustls = { version = "0.22", default-features = false, features = ["tls12"] }
rustls-pemfile = "2"
tokio = { version = "1.0", features = ["io-std", "macros", "net", "rt-multi-thread"] }

[features]
default = ["native-tokio", "http1", "tls12", "logging", "acceptor", "ring"]
acceptor = ["hyper/server", "ring", "tokio-runtime"]
http1 = ["hyper/http1"]
http2 = ["hyper/http2"]
webpki-tokio = ["tokio-runtime", "webpki-roots"]
native-tokio = ["tokio-runtime", "rustls-native-certs"]
default = ["native-tokio", "http1", "tls12", "logging", "ring"]
http1 = ["hyper-util/http1"]
http2 = ["hyper-util/http2"]
webpki-tokio = ["webpki-roots"]
native-tokio = ["rustls-native-certs"]
ring = ["rustls/ring"]
tokio-runtime = ["hyper/runtime"]
tls12 = ["tokio-rustls/tls12", "rustls/tls12"]
logging = ["log", "tokio-rustls/logging", "rustls/logging"]

Expand All @@ -48,7 49,7 @@ required-features = ["native-tokio", "http1"]
[[example]]
name = "server"
path = "examples/server.rs"
required-features = ["tokio-runtime", "acceptor"]
required-features = ["ring"]

[package.metadata.docs.rs]
all-features = true
Expand Down
15 changes: 10 additions & 5 deletions examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 2,11 @@
//!
//! First parameter is the mandatory URL to GET.
//! Second parameter is an optional path to CA store.
use hyper::{body::to_bytes, client, Body, Uri};
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;
Expand Down Expand Up @@ -68,7 71,7 @@ async fn run_client() -> io::Result<()> {
.build();

// Build the hyper client from the HTTPS connector.
let client: client::Client<_, hyper::Body> = client::Client::builder().build(https);
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
Expand All @@ -81,10 84,12 @@ async fn run_client() -> io::Result<()> {
println!("Status:\n{}", res.status());
println!("Headers:\n{:#?}", res.headers());

let body: Body = res.into_body();
let body = to_bytes(body)
let body = res
.into_body()
.collect()
.await
.map_err(|e| error(format!("Could not get body: {:?}", e)))?;
.map_err(|e| error(format!("Could not get body: {:?}", e)))?
.to_bytes();
println!("Body:\n{}", String::from_utf8_lossy(&body));

Ok(())
Expand Down
84 changes: 57 additions & 27 deletions examples/server.rs
Original file line number Diff line number Diff line change
@@ -1,20 1,25 @@
//! Simple HTTPS echo service based on hyper-rustls
//! Simple HTTPS echo service based on hyper_util and rustls
//!
//! First parameter is the mandatory port to use.
//! Certificate and private key are hardcoded to sample files.
//! hyper will automatically use HTTP/2 if a client starts talking HTTP/2,
//! otherwise HTTP/1.1 will be used.

#![cfg(feature = "acceptor")]

use std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::vec::Vec;
use std::{env, fs, io};

use hyper::server::conn::AddrIncoming;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use hyper_rustls::TlsAcceptor;
use http::{Method, Request, Response, StatusCode};
use http_body_util::{BodyExt, Full};
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 rustls::ServerConfig;
use tokio::net::TcpListener;
use tokio_rustls::TlsAcceptor;

fn main() {
// Serve an echo service over HTTPS, with proper error handling.
Expand All @@ -32,45 37,70 @@ fn error(err: String) -> io::Error {
async fn run_server() -> Result<(), Box<dyn std::error::Error Send Sync>> {
// First parameter is port number (optional, defaults to 1337)
let port = match env::args().nth(1) {
Some(ref p) => p.to_owned(),
None => "1337".to_owned(),
Some(ref p) => p.parse()?,
None => 1337,
};
let addr = format!("127.0.0.1:{}", port).parse()?;
let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), port);

// Load public certificate.
let certs = load_certs("examples/sample.pem")?;
// Load private key.
let key = load_private_key("examples/sample.rsa")?;
// Build TLS configuration.

println!("Starting to serve on https://{}", addr);

// Create a TCP listener via tokio.
let incoming = AddrIncoming::bind(&addr)?;
let acceptor = TlsAcceptor::builder()
let incoming = TcpListener::bind(&addr).await?;

// Build TLS configuration.
let mut server_config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|e| error(format!("{}", e)))?
.with_all_versions_alpn()
.with_incoming(incoming);
let service = make_service_fn(|_| async { Ok::<_, io::Error>(service_fn(echo)) });
let server = Server::builder(acceptor).serve(service);

// Run the future, keep going until an error occurs.
println!("Starting to serve on https://{}.", addr);
server.await?;
Ok(())
.map_err(|e| error(e.to_string()))?;
server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()];
let tls_acceptor = TlsAcceptor::from(Arc::new(server_config));

let service = service_fn(echo);

loop {
let (tcp_stream, _remote_addr) = incoming.accept().await?;

let tls_acceptor = tls_acceptor.clone();
tokio::spawn(async move {
let tls_stream = match tls_acceptor.accept(tcp_stream).await {
Ok(tls_stream) => tls_stream,
Err(err) => {
eprintln!("failed to perform tls handshake: {err:#}");
return;
}
};
if let Err(err) = Builder::new(TokioExecutor::new())
.serve_connection(TokioIo::new(tls_stream), service)
.await
{
eprintln!("failed to serve connection: {err:#}");
}
});
}
}

// Custom echo service, handling two different routes and a
// catch-all 404 responder.
async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let mut response = Response::new(Body::empty());
async fn echo(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Error> {
let mut response = Response::new(Full::default());
match (req.method(), req.uri().path()) {
// Help route.
(&Method::GET, "/") => {
*response.body_mut() = Body::from("Try POST /echo\n");
*response.body_mut() = Full::from("Try POST /echo\n");
}
// Echo service route.
(&Method::POST, "/echo") => {
*response.body_mut() = req.into_body();
*response.body_mut() = Full::from(
req.into_body()
.collect()
.await?
.to_bytes(),
);
}
// Catch-all 404.
_ => {
Expand Down
169 changes: 0 additions & 169 deletions src/acceptor.rs

This file was deleted.

Loading

0 comments on commit e19a299

Please sign in to comment.