From 73296bf654c63f62d49976d66d662f5ac45831f8 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Fri, 21 Jun 2024 14:19:56 +1000 Subject: [PATCH 01/14] WIP updating hyper to hyper 1.0 --- Cargo.toml | 10 ++++++---- src/lib.rs | 35 ++++++++++++++++++++--------------- src/types.rs | 2 +- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bebbf6e..2e406d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,11 @@ categories = ["web-programming", "api-bindings"] default = ["hyper-rustls"] [dependencies] -base64 = "0.21.0" -hyper = { version = "0.14.18", features = ["client", "http1", "http2"] } -hyper-rustls = { version = "0.24", default-features = false, features = ["http1", "http2", "native-tokio", "tls12"], optional = true } +base64 = "0.22.1" +hyper = { version = "1.3.1", features = ["client", "http1", "http2"] } +hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "tls12", "rustls-native-certs", "webpki-roots", "ring"], optional = true } +hyper-util = { version = "0.1.5", features = ["client", "client-legacy", "http1", "http2", "tokio"]} +http-body-util = "0.1.2" ring = { version = "0.17", features = ["std"] } rustls-pki-types = "1.1.0" serde = { version = "1.0.104", features = ["derive"] } @@ -27,7 +29,7 @@ thiserror = "1.0.30" [dev-dependencies] anyhow = "1.0.66" clap = { version = "4.0.29", features = ["derive"] } -rcgen = "0.12" +rcgen = "0.13.1" tokio = { version = "1.22.0", features = ["macros", "rt", "rt-multi-thread", "time"] } tracing = "0.1.37" tracing-subscriber = "0.3.16" diff --git a/src/lib.rs b/src/lib.rs index 29b308f..b2d5849 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,11 +9,14 @@ use std::pin::Pin; use std::sync::Arc; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; -use hyper::client::connect::Connect; +use http_body_util::combinators::BoxBody; +use http_body_util::Full; +use hyper::body::Bytes; +use hyper_util::client::legacy::connect::Connect; #[cfg(feature = "hyper-rustls")] -use hyper::client::HttpConnector; +use hyper_util::client::legacy::connect::HttpConnector; use hyper::header::{CONTENT_TYPE, LOCATION}; -use hyper::{Body, Method, Request, Response, StatusCode}; +use hyper::{body::Body, Method, Request, Response, StatusCode}; use ring::digest::{digest, SHA256}; use ring::rand::SystemRandom; use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; @@ -650,21 +653,21 @@ impl Signer for ExternalAccountKey { } } -fn nonce_from_response(rsp: &Response) -> Option { +fn nonce_from_response(rsp: &Response>) -> Option { rsp.headers() .get(REPLAY_NONCE) .and_then(|hv| String::from_utf8(hv.as_ref().to_vec()).ok()) } #[cfg(feature = "hyper-rustls")] -struct DefaultClient(hyper::Client>); +struct DefaultClient(hyper_util::client::legacy::Client, BoxBody>); #[cfg(feature = "hyper-rustls")] impl HttpClient for DefaultClient { fn request( &self, - req: Request, - ) -> Pin>> + Send>> { + req: Request>, + ) -> Pin, hyper_util::client::legacy::Error>> + Send>> { Box::pin(self.0.request(req)) } } @@ -673,9 +676,11 @@ impl HttpClient for DefaultClient { impl Default for DefaultClient { fn default() -> Self { Self( - hyper::Client::builder().build( + hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build( hyper_rustls::HttpsConnectorBuilder::new() - .with_native_roots() + .with_native_roots().unwrap_or_else(|_| { + hyper_rustls::HttpsConnectorBuilder::new().with_webpki_roots() + }) .https_only() .enable_http1() .enable_http2() @@ -690,19 +695,19 @@ pub trait HttpClient: Send + Sync + 'static { /// Send the given request and return the response fn request( &self, - req: Request, - ) -> Pin>> + Send>>; + req: Request>, + ) -> Pin, hyper_util::client::legacy::Error>> + Send>>; } -impl HttpClient for hyper::Client +impl HttpClient for hyper_util::client::legacy::Client> where C: Connect + Clone + Send + Sync + 'static, { fn request( &self, - req: Request, - ) -> Pin>> + Send>> { - Box::pin(>::request(self, req)) + req: Request>, + ) -> Pin, hyper_util::client::legacy::Error>> + Send>> { + Box::pin(>>::request(self, req)) } } diff --git a/src/types.rs b/src/types.rs index dfd73dd..751f242 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ use std::fmt; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; -use hyper::{Body, Response}; +use hyper::Response; use ring::digest::{digest, Digest, SHA256}; use ring::signature::{EcdsaKeyPair, KeyPair}; use rustls_pki_types::CertificateDer; From 1b821daaefcc1a63572bdd5b6e13e71d120088d9 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Sun, 23 Jun 2024 14:38:39 +1000 Subject: [PATCH 02/14] Fix remaining compilation errors, revert rcgen back to previous version to fix example --- Cargo.toml | 2 +- src/lib.rs | 125 +++++++++++++++++++++++++++++++++++---------------- src/types.rs | 23 +++++++--- 3 files changed, 105 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2e406d6..babc5b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ thiserror = "1.0.30" [dev-dependencies] anyhow = "1.0.66" clap = { version = "4.0.29", features = ["derive"] } -rcgen = "0.13.1" +rcgen = "0.12.1" tokio = { version = "1.22.0", features = ["macros", "rt", "rt-multi-thread", "time"] } tracing = "0.1.37" tracing-subscriber = "0.3.16" diff --git a/src/lib.rs b/src/lib.rs index b2d5849..c39e049 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,13 @@ use std::sync::Arc; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; use http_body_util::combinators::BoxBody; -use http_body_util::Full; -use hyper::body::Bytes; +use http_body_util::BodyExt; +use hyper::body::{Bytes, Incoming}; +use hyper::header::{CONTENT_TYPE, LOCATION}; +use hyper::{Method, Request, Response, StatusCode}; use hyper_util::client::legacy::connect::Connect; #[cfg(feature = "hyper-rustls")] use hyper_util::client::legacy::connect::HttpConnector; -use hyper::header::{CONTENT_TYPE, LOCATION}; -use hyper::{body::Body, Method, Request, Response, StatusCode}; use ring::digest::{digest, SHA256}; use ring::rand::SystemRandom; use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; @@ -137,9 +137,13 @@ impl Order { .await?; self.nonce = nonce_from_response(&rsp); - let body = hyper::body::to_bytes(Problem::from_response(rsp).await?).await?; + let body_bytes = Problem::from_response(rsp) + .await? + .collect() + .await? + .to_bytes(); Ok(Some( - String::from_utf8(body.to_vec()) + String::from_utf8(body_bytes.to_vec()) .map_err(|_| "unable to decode certificate as UTF-8")?, )) } @@ -422,7 +426,7 @@ impl AccountInner { payload: Option<&impl Serialize>, nonce: Option, url: &str, - ) -> Result, Error> { + ) -> Result, Error> { self.client.post(payload, nonce, self, url).await } } @@ -451,16 +455,20 @@ struct Client { } impl Client { - async fn new(server_url: &str, http: Box) -> Result { + async fn new(directory_url: &str, http: Box) -> Result { let req = Request::builder() - .uri(server_url) - .body(Body::empty()) - .unwrap(); + .uri(directory_url) + .body( + http_body_util::Empty::new() + .map_err::<_, Error>(|_| unreachable!("Should be Infallible")) + .boxed(), + ) + .expect("Infallible error should not occur"); let rsp = http.request(req).await?; - let body = hyper::body::to_bytes(rsp.into_body()).await?; + let body_bytes = rsp.into_body().collect().await?.to_bytes(); Ok(Client { http, - urls: serde_json::from_slice(&body)?, + urls: serde_json::from_slice(&body_bytes)?, }) } @@ -470,15 +478,18 @@ impl Client { nonce: Option, signer: &impl Signer, url: &str, - ) -> Result, Error> { + ) -> Result, Error> { let nonce = self.nonce(nonce).await?; let body = JoseJson::new(payload, signer.header(Some(&nonce), url), signer)?; let request = Request::builder() .method(Method::POST) .uri(url) .header(CONTENT_TYPE, JOSE_JSON) - .body(Body::from(serde_json::to_vec(&body)?)) - .unwrap(); + .body( + http_body_util::Full::from(serde_json::to_vec(&body)?) + .map_err::<_, Error>(|_| unreachable!("Should be Infallible")) + .boxed(), + )?; Ok(self.http.request(request).await?) } @@ -491,8 +502,12 @@ impl Client { let request = Request::builder() .method(Method::HEAD) .uri(&self.urls.new_nonce) - .body(Body::empty()) - .unwrap(); + .body( + http_body_util::Empty::new() + .map_err::<_, Error>(|_| unreachable!("Should be Infallible")) + .boxed(), + ) + .expect("Should be Infallible"); let rsp = self.http.request(request).await?; // https://datatracker.ietf.org/doc/html/rfc8555#section-7.2 @@ -653,21 +668,35 @@ impl Signer for ExternalAccountKey { } } -fn nonce_from_response(rsp: &Response>) -> Option { +fn nonce_from_response(rsp: &Response) -> Option { rsp.headers() .get(REPLAY_NONCE) .and_then(|hv| String::from_utf8(hv.as_ref().to_vec()).ok()) } #[cfg(feature = "hyper-rustls")] -struct DefaultClient(hyper_util::client::legacy::Client, BoxBody>); +struct DefaultClient( + hyper_util::client::legacy::Client< + hyper_rustls::HttpsConnector, + BoxBody, + >, +); #[cfg(feature = "hyper-rustls")] impl HttpClient for DefaultClient { fn request( &self, - req: Request>, - ) -> Pin, hyper_util::client::legacy::Error>> + Send>> { + req: Request>, + ) -> Pin< + Box< + dyn Future< + Output = Result< + Response, + hyper_util::client::legacy::Error, + >, + > + Send, + >, + > { Box::pin(self.0.request(req)) } } @@ -676,16 +705,18 @@ impl HttpClient for DefaultClient { impl Default for DefaultClient { fn default() -> Self { Self( - hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build( - hyper_rustls::HttpsConnectorBuilder::new() - .with_native_roots().unwrap_or_else(|_| { - hyper_rustls::HttpsConnectorBuilder::new().with_webpki_roots() - }) - .https_only() - .enable_http1() - .enable_http2() - .build(), - ), + hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build( + hyper_rustls::HttpsConnectorBuilder::new() + .with_native_roots() + .unwrap_or_else(|_| { + hyper_rustls::HttpsConnectorBuilder::new().with_webpki_roots() + }) + .https_only() + .enable_http1() + .enable_http2() + .build(), + ), ) } } @@ -695,19 +726,37 @@ pub trait HttpClient: Send + Sync + 'static { /// Send the given request and return the response fn request( &self, - req: Request>, - ) -> Pin, hyper_util::client::legacy::Error>> + Send>>; + req: Request>, + ) -> Pin< + Box< + dyn Future< + Output = Result< + Response, + hyper_util::client::legacy::Error, + >, + > + Send, + >, + >; } -impl HttpClient for hyper_util::client::legacy::Client> +impl HttpClient for hyper_util::client::legacy::Client> where C: Connect + Clone + Send + Sync + 'static, { fn request( &self, - req: Request>, - ) -> Pin, hyper_util::client::legacy::Error>> + Send>> { - Box::pin(>>::request(self, req)) + req: Request>, + ) -> Pin< + Box< + dyn Future< + Output = Result< + Response, + hyper_util::client::legacy::Error, + >, + > + Send, + >, + > { + Box::pin(>>::request(self, req)) } } diff --git a/src/types.rs b/src/types.rs index 751f242..f39dead 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,9 @@ use std::fmt; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; +use http_body_util::combinators::BoxBody; +use http_body_util::BodyExt; +use hyper::body::{Bytes, Incoming}; use hyper::Response; use ring::digest::{digest, Digest, SHA256}; use ring::signature::{EcdsaKeyPair, KeyPair}; @@ -30,6 +33,12 @@ pub enum Error { /// HTTP request failure #[error("HTTP request failure: {0}")] Http(#[from] hyper::Error), + /// HTTP request failure + #[error("HTTP request failure: {0}")] + HttpHttp(#[from] hyper::http::Error), + /// HTTP client request failure + #[error("HTTP request failure: {0}")] + HttpClient(#[from] hyper_util::client::legacy::Error), /// Invalid ACME server URL #[error("invalid URI: {0}")] InvalidUri(#[from] hyper::http::uri::InvalidUri), @@ -119,21 +128,23 @@ pub struct Problem { } impl Problem { - pub(crate) async fn check(rsp: Response) -> Result { + pub(crate) async fn check(rsp: Response) -> Result { Ok(serde_json::from_slice( - &hyper::body::to_bytes(Self::from_response(rsp).await?).await?, + &Self::from_response(rsp).await?.collect().await?.to_bytes(), )?) } - pub(crate) async fn from_response(rsp: Response) -> Result { + pub(crate) async fn from_response( + rsp: Response, + ) -> Result, Error> { let status = rsp.status(); let body = rsp.into_body(); if status.is_informational() || status.is_success() || status.is_redirection() { - return Ok(body); + return Ok(body.map_err::<_, Error>(|_| unreachable!()).boxed()); } - let body = hyper::body::to_bytes(body).await?; - Err(serde_json::from_slice::(&body)?.into()) + let body_bytes = body.collect().await?.to_bytes(); + Err(serde_json::from_slice::(&body_bytes)?.into()) } } From 1c06b72318a278355dc0eac9af818352a5fb5fbe Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Mon, 24 Jun 2024 19:26:03 +1000 Subject: [PATCH 03/14] Address merge request comments --- Cargo.toml | 4 ++-- src/lib.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index babc5b0..7ea5054 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ categories = ["web-programming", "api-bindings"] default = ["hyper-rustls"] [dependencies] -base64 = "0.22.1" +base64 = "0.21.0" hyper = { version = "1.3.1", features = ["client", "http1", "http2"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "tls12", "rustls-native-certs", "webpki-roots", "ring"], optional = true } hyper-util = { version = "0.1.5", features = ["client", "client-legacy", "http1", "http2", "tokio"]} @@ -29,7 +29,7 @@ thiserror = "1.0.30" [dev-dependencies] anyhow = "1.0.66" clap = { version = "4.0.29", features = ["derive"] } -rcgen = "0.12.1" +rcgen = "0.12" tokio = { version = "1.22.0", features = ["macros", "rt", "rt-multi-thread", "time"] } tracing = "0.1.37" tracing-subscriber = "0.3.16" diff --git a/src/lib.rs b/src/lib.rs index c39e049..ec56393 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,13 +137,13 @@ impl Order { .await?; self.nonce = nonce_from_response(&rsp); - let body_bytes = Problem::from_response(rsp) + let body = Problem::from_response(rsp) .await? .collect() .await? .to_bytes(); Ok(Some( - String::from_utf8(body_bytes.to_vec()) + String::from_utf8(body.to_vec()) .map_err(|_| "unable to decode certificate as UTF-8")?, )) } @@ -455,9 +455,9 @@ struct Client { } impl Client { - async fn new(directory_url: &str, http: Box) -> Result { + async fn new(server_url: &str, http: Box) -> Result { let req = Request::builder() - .uri(directory_url) + .uri(server_url) .body( http_body_util::Empty::new() .map_err::<_, Error>(|_| unreachable!("Should be Infallible")) @@ -465,10 +465,10 @@ impl Client { ) .expect("Infallible error should not occur"); let rsp = http.request(req).await?; - let body_bytes = rsp.into_body().collect().await?.to_bytes(); + let body = rsp.into_body().collect().await?.to_bytes(); Ok(Client { http, - urls: serde_json::from_slice(&body_bytes)?, + urls: serde_json::from_slice(&body)?, }) } @@ -478,7 +478,7 @@ impl Client { nonce: Option, signer: &impl Signer, url: &str, - ) -> Result, Error> { + ) -> Result, Error> { let nonce = self.nonce(nonce).await?; let body = JoseJson::new(payload, signer.header(Some(&nonce), url), signer)?; let request = Request::builder() From 609362f380bb13ff355fc575ebee8a8f118188e3 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Tue, 25 Jun 2024 13:06:53 +1000 Subject: [PATCH 04/14] Address MR comments for Incoming --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ec56393..d818602 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -691,7 +691,7 @@ impl HttpClient for DefaultClient { Box< dyn Future< Output = Result< - Response, + Response, hyper_util::client::legacy::Error, >, > + Send, @@ -731,7 +731,7 @@ pub trait HttpClient: Send + Sync + 'static { Box< dyn Future< Output = Result< - Response, + Response, hyper_util::client::legacy::Error, >, > + Send, From daaed4e01241a84491ace20a4fce158e7b6665f1 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Tue, 25 Jun 2024 14:38:38 +1000 Subject: [PATCH 05/14] Swap to using http_body_util::Full instead of BoxBody + Full + Empty --- src/lib.rs | 52 ++++++++++++++-------------------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d818602..189461e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,7 @@ use std::pin::Pin; use std::sync::Arc; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; -use http_body_util::combinators::BoxBody; -use http_body_util::BodyExt; +use http_body_util::{BodyExt, Full}; use hyper::body::{Bytes, Incoming}; use hyper::header::{CONTENT_TYPE, LOCATION}; use hyper::{Method, Request, Response, StatusCode}; @@ -458,11 +457,7 @@ impl Client { async fn new(server_url: &str, http: Box) -> Result { let req = Request::builder() .uri(server_url) - .body( - http_body_util::Empty::new() - .map_err::<_, Error>(|_| unreachable!("Should be Infallible")) - .boxed(), - ) + .body(http_body_util::Full::default()) .expect("Infallible error should not occur"); let rsp = http.request(req).await?; let body = rsp.into_body().collect().await?.to_bytes(); @@ -485,11 +480,7 @@ impl Client { .method(Method::POST) .uri(url) .header(CONTENT_TYPE, JOSE_JSON) - .body( - http_body_util::Full::from(serde_json::to_vec(&body)?) - .map_err::<_, Error>(|_| unreachable!("Should be Infallible")) - .boxed(), - )?; + .body(http_body_util::Full::from(serde_json::to_vec(&body)?))?; Ok(self.http.request(request).await?) } @@ -502,11 +493,7 @@ impl Client { let request = Request::builder() .method(Method::HEAD) .uri(&self.urls.new_nonce) - .body( - http_body_util::Empty::new() - .map_err::<_, Error>(|_| unreachable!("Should be Infallible")) - .boxed(), - ) + .body(http_body_util::Full::default()) .expect("Should be Infallible"); let rsp = self.http.request(request).await?; @@ -676,25 +663,18 @@ fn nonce_from_response(rsp: &Response) -> Option { #[cfg(feature = "hyper-rustls")] struct DefaultClient( - hyper_util::client::legacy::Client< - hyper_rustls::HttpsConnector, - BoxBody, - >, + hyper_util::client::legacy::Client, Full>, ); #[cfg(feature = "hyper-rustls")] impl HttpClient for DefaultClient { fn request( &self, - req: Request>, + req: Request>, ) -> Pin< Box< - dyn Future< - Output = Result< - Response, - hyper_util::client::legacy::Error, - >, - > + Send, + dyn Future, hyper_util::client::legacy::Error>> + + Send, >, > { Box::pin(self.0.request(req)) @@ -726,26 +706,22 @@ pub trait HttpClient: Send + Sync + 'static { /// Send the given request and return the response fn request( &self, - req: Request>, + req: Request>, ) -> Pin< Box< - dyn Future< - Output = Result< - Response, - hyper_util::client::legacy::Error, - >, - > + Send, + dyn Future, hyper_util::client::legacy::Error>> + + Send, >, >; } -impl HttpClient for hyper_util::client::legacy::Client> +impl HttpClient for hyper_util::client::legacy::Client> where C: Connect + Clone + Send + Sync + 'static, { fn request( &self, - req: Request>, + req: Request>, ) -> Pin< Box< dyn Future< @@ -756,7 +732,7 @@ where > + Send, >, > { - Box::pin(>>::request(self, req)) + Box::pin(>>::request(self, req)) } } From 3641afc1f6229add13e40b87ca164944dab23d6c Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Tue, 25 Jun 2024 17:33:54 +1000 Subject: [PATCH 06/14] Bump MSRV to 1.64 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7ea5054..f9681e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "instant-acme" version = "0.5.0" edition = "2021" -rust-version = "1.63" +rust-version = "1.64" license = "Apache-2.0" description = "Async pure-Rust ACME client" homepage = "https://github.com/instant-labs/instant-acme" From 7161b94b5f79d19fe57fdc84b0d5e3f6f47c908b Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Wed, 26 Jun 2024 13:48:11 +1000 Subject: [PATCH 07/14] Address MR comments --- src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 189461e..6d94600 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use http_body_util::{BodyExt, Full}; use hyper::body::{Bytes, Incoming}; use hyper::header::{CONTENT_TYPE, LOCATION}; use hyper::{Method, Request, Response, StatusCode}; -use hyper_util::client::legacy::connect::Connect; +use hyper_util::client::legacy::{Client as HyperClient, connect::Connect}; #[cfg(feature = "hyper-rustls")] use hyper_util::client::legacy::connect::HttpConnector; use ring::digest::{digest, SHA256}; @@ -457,7 +457,7 @@ impl Client { async fn new(server_url: &str, http: Box) -> Result { let req = Request::builder() .uri(server_url) - .body(http_body_util::Full::default()) + .body(Full::default()) .expect("Infallible error should not occur"); let rsp = http.request(req).await?; let body = rsp.into_body().collect().await?.to_bytes(); @@ -480,7 +480,7 @@ impl Client { .method(Method::POST) .uri(url) .header(CONTENT_TYPE, JOSE_JSON) - .body(http_body_util::Full::from(serde_json::to_vec(&body)?))?; + .body(Full::from(serde_json::to_vec(&body)?))?; Ok(self.http.request(request).await?) } @@ -493,7 +493,7 @@ impl Client { let request = Request::builder() .method(Method::HEAD) .uri(&self.urls.new_nonce) - .body(http_body_util::Full::default()) + .body(Full::default()) .expect("Should be Infallible"); let rsp = self.http.request(request).await?; @@ -663,7 +663,7 @@ fn nonce_from_response(rsp: &Response) -> Option { #[cfg(feature = "hyper-rustls")] struct DefaultClient( - hyper_util::client::legacy::Client, Full>, + HyperClient, Full>, ); #[cfg(feature = "hyper-rustls")] @@ -685,7 +685,7 @@ impl HttpClient for DefaultClient { impl Default for DefaultClient { fn default() -> Self { Self( - hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + HyperClient::builder(hyper_util::rt::TokioExecutor::new()) .build( hyper_rustls::HttpsConnectorBuilder::new() .with_native_roots() @@ -715,7 +715,7 @@ pub trait HttpClient: Send + Sync + 'static { >; } -impl HttpClient for hyper_util::client::legacy::Client> +impl HttpClient for HyperClient> where C: Connect + Clone + Send + Sync + 'static, { @@ -732,7 +732,7 @@ where > + Send, >, > { - Box::pin(>>::request(self, req)) + Box::pin(>>::request(self, req)) } } From e1c3f2062de60e8209e6a954b067795a4f0353b9 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Wed, 26 Jun 2024 13:54:54 +1000 Subject: [PATCH 08/14] Address more MR comments --- src/lib.rs | 29 +++++++++++++---------------- src/types.rs | 13 +++++-------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6d94600..baebbde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,9 +13,9 @@ use http_body_util::{BodyExt, Full}; use hyper::body::{Bytes, Incoming}; use hyper::header::{CONTENT_TYPE, LOCATION}; use hyper::{Method, Request, Response, StatusCode}; -use hyper_util::client::legacy::{Client as HyperClient, connect::Connect}; #[cfg(feature = "hyper-rustls")] use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::{connect::Connect, Client as HyperClient}; use ring::digest::{digest, SHA256}; use ring::rand::SystemRandom; use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; @@ -662,9 +662,7 @@ fn nonce_from_response(rsp: &Response) -> Option { } #[cfg(feature = "hyper-rustls")] -struct DefaultClient( - HyperClient, Full>, -); +struct DefaultClient(HyperClient, Full>); #[cfg(feature = "hyper-rustls")] impl HttpClient for DefaultClient { @@ -685,18 +683,17 @@ impl HttpClient for DefaultClient { impl Default for DefaultClient { fn default() -> Self { Self( - HyperClient::builder(hyper_util::rt::TokioExecutor::new()) - .build( - hyper_rustls::HttpsConnectorBuilder::new() - .with_native_roots() - .unwrap_or_else(|_| { - hyper_rustls::HttpsConnectorBuilder::new().with_webpki_roots() - }) - .https_only() - .enable_http1() - .enable_http2() - .build(), - ), + HyperClient::builder(hyper_util::rt::TokioExecutor::new()).build( + hyper_rustls::HttpsConnectorBuilder::new() + .with_native_roots() + .unwrap_or_else(|_| { + hyper_rustls::HttpsConnectorBuilder::new().with_webpki_roots() + }) + .https_only() + .enable_http1() + .enable_http2() + .build(), + ), ) } } diff --git a/src/types.rs b/src/types.rs index f39dead..c9cb7a6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,8 @@ use std::fmt; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; -use http_body_util::combinators::BoxBody; use http_body_util::BodyExt; -use hyper::body::{Bytes, Incoming}; +use hyper::body::Incoming; use hyper::Response; use ring::digest::{digest, Digest, SHA256}; use ring::signature::{EcdsaKeyPair, KeyPair}; @@ -134,17 +133,15 @@ impl Problem { )?) } - pub(crate) async fn from_response( - rsp: Response, - ) -> Result, Error> { + pub(crate) async fn from_response(rsp: Response) -> Result { let status = rsp.status(); let body = rsp.into_body(); if status.is_informational() || status.is_success() || status.is_redirection() { - return Ok(body.map_err::<_, Error>(|_| unreachable!()).boxed()); + return Ok(body); } - let body_bytes = body.collect().await?.to_bytes(); - Err(serde_json::from_slice::(&body_bytes)?.into()) + let body = body.collect().await?.to_bytes(); + Err(serde_json::from_slice::(&body)?.into()) } } From ad340e10b1ad3de5e37d347c5c52551265d0422e Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Wed, 26 Jun 2024 14:32:16 +1000 Subject: [PATCH 09/14] Address MR comments, change signature of HttpClient --- src/lib.rs | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index baebbde..c982dda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -669,13 +669,9 @@ impl HttpClient for DefaultClient { fn request( &self, req: Request>, - ) -> Pin< - Box< - dyn Future, hyper_util::client::legacy::Error>> - + Send, - >, - > { - Box::pin(self.0.request(req)) + ) -> Pin, Error>> + Send>> { + let fut = self.0.request(req); + Box::pin(async move { fut.await.map_err(Error::from) }) } } @@ -704,12 +700,7 @@ pub trait HttpClient: Send + Sync + 'static { fn request( &self, req: Request>, - ) -> Pin< - Box< - dyn Future, hyper_util::client::legacy::Error>> - + Send, - >, - >; + ) -> Pin, Error>> + Send>>; } impl HttpClient for HyperClient> @@ -719,17 +710,9 @@ where fn request( &self, req: Request>, - ) -> Pin< - Box< - dyn Future< - Output = Result< - Response, - hyper_util::client::legacy::Error, - >, - > + Send, - >, - > { - Box::pin(>>::request(self, req)) + ) -> Pin, Error>> + Send>> { + let fut = >>::request(self, req); + Box::pin(async move { fut.await.map_err(Error::from) }) } } From 93b6618c6c90ded399a971dd837c9b856eba7567 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Wed, 26 Jun 2024 14:36:21 +1000 Subject: [PATCH 10/14] Re-organise Error enum to address MR comments --- src/types.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/types.rs b/src/types.rs index c9cb7a6..693744c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -29,15 +29,12 @@ pub enum Error { /// Failed to instantiate a private key #[error("invalid key bytes: {0}")] CryptoKey(#[from] ring::error::KeyRejected), - /// HTTP request failure + /// Hyper request failure #[error("HTTP request failure: {0}")] - Http(#[from] hyper::Error), - /// HTTP request failure + Hyper(#[from] hyper::Error), + /// HTTP failure #[error("HTTP request failure: {0}")] - HttpHttp(#[from] hyper::http::Error), - /// HTTP client request failure - #[error("HTTP request failure: {0}")] - HttpClient(#[from] hyper_util::client::legacy::Error), + Http(#[from] hyper::http::Error), /// Invalid ACME server URL #[error("invalid URI: {0}")] InvalidUri(#[from] hyper::http::uri::InvalidUri), @@ -47,6 +44,9 @@ pub enum Error { /// Miscellaneous errors #[error("missing data: {0}")] Str(&'static str), + /// Other kind of error + #[error(transparent)] + Other(Box), } impl From<&'static str> for Error { @@ -55,6 +55,12 @@ impl From<&'static str> for Error { } } +impl From for Error { + fn from(value: hyper_util::client::legacy::Error) -> Self { + Self::Other(Box::new(value)) + } +} + /// ACME account credentials /// /// This opaque type contains the account ID, the private key data and the From 8ce4fbd1523bf7cc5dc7bfedd70ea121a83c2545 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Wed, 26 Jun 2024 14:42:15 +1000 Subject: [PATCH 11/14] Remove dependency on wepki-roots crate by making DefaultClient constructor fallible --- Cargo.toml | 2 +- src/lib.rs | 38 ++++++++++++++++++-------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f9681e3..05a94af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ default = ["hyper-rustls"] [dependencies] base64 = "0.21.0" hyper = { version = "1.3.1", features = ["client", "http1", "http2"] } -hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "tls12", "rustls-native-certs", "webpki-roots", "ring"], optional = true } +hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "tls12", "rustls-native-certs", "ring"], optional = true } hyper-util = { version = "0.1.5", features = ["client", "client-legacy", "http1", "http2", "tokio"]} http-body-util = "0.1.2" ring = { version = "0.17", features = ["std"] } diff --git a/src/lib.rs b/src/lib.rs index c982dda..db1b467 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -212,7 +212,7 @@ impl Account { pub async fn from_credentials(credentials: AccountCredentials) -> Result { Ok(Self { inner: Arc::new( - AccountInner::from_credentials(credentials, Box::::default()) + AccountInner::from_credentials(credentials, Box::new(DefaultClient::try_new()?)) .await?, ), }) @@ -262,7 +262,7 @@ impl Account { Self::create_inner( account, external_account, - Client::new(server_url, Box::::default()).await?, + Client::new(server_url, Box::new(DefaultClient::try_new()?)).await?, server_url, ) .await @@ -665,32 +665,30 @@ fn nonce_from_response(rsp: &Response) -> Option { struct DefaultClient(HyperClient, Full>); #[cfg(feature = "hyper-rustls")] -impl HttpClient for DefaultClient { - fn request( - &self, - req: Request>, - ) -> Pin, Error>> + Send>> { - let fut = self.0.request(req); - Box::pin(async move { fut.await.map_err(Error::from) }) - } -} - -#[cfg(feature = "hyper-rustls")] -impl Default for DefaultClient { - fn default() -> Self { - Self( +impl DefaultClient { + fn try_new() -> Result { + Ok(Self( HyperClient::builder(hyper_util::rt::TokioExecutor::new()).build( hyper_rustls::HttpsConnectorBuilder::new() .with_native_roots() - .unwrap_or_else(|_| { - hyper_rustls::HttpsConnectorBuilder::new().with_webpki_roots() - }) + .map_err(|e| Error::Other(Box::new(e)))? .https_only() .enable_http1() .enable_http2() .build(), ), - ) + )) + } +} + +#[cfg(feature = "hyper-rustls")] +impl HttpClient for DefaultClient { + fn request( + &self, + req: Request>, + ) -> Pin, Error>> + Send>> { + let fut = self.0.request(req); + Box::pin(async move { fut.await.map_err(Error::from) }) } } From 808f39dc63b6a5f0a1b30ca36aad88096e16c8d9 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Thu, 27 Jun 2024 09:50:16 +1000 Subject: [PATCH 12/14] Resolve MR comments --- src/lib.rs | 11 ++++++----- src/types.rs | 12 ++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index db1b467..1cdb833 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,9 @@ use hyper::body::{Bytes, Incoming}; use hyper::header::{CONTENT_TYPE, LOCATION}; use hyper::{Method, Request, Response, StatusCode}; #[cfg(feature = "hyper-rustls")] -use hyper_util::client::legacy::connect::HttpConnector; -use hyper_util::client::legacy::{connect::Connect, Client as HyperClient}; +use hyper_util::client::legacy::connect::{Connect, HttpConnector}; +use hyper_util::client::legacy::Client as HyperClient; +use hyper_util::rt::TokioExecutor; use ring::digest::{digest, SHA256}; use ring::rand::SystemRandom; use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; @@ -458,7 +459,7 @@ impl Client { let req = Request::builder() .uri(server_url) .body(Full::default()) - .expect("Infallible error should not occur"); + .expect("infallible error should not occur"); let rsp = http.request(req).await?; let body = rsp.into_body().collect().await?.to_bytes(); Ok(Client { @@ -494,7 +495,7 @@ impl Client { .method(Method::HEAD) .uri(&self.urls.new_nonce) .body(Full::default()) - .expect("Should be Infallible"); + .expect("infallible error should not occur"); let rsp = self.http.request(request).await?; // https://datatracker.ietf.org/doc/html/rfc8555#section-7.2 @@ -668,7 +669,7 @@ struct DefaultClient(HyperClient, Fu impl DefaultClient { fn try_new() -> Result { Ok(Self( - HyperClient::builder(hyper_util::rt::TokioExecutor::new()).build( + HyperClient::builder(TokioExecutor::new()).build( hyper_rustls::HttpsConnectorBuilder::new() .with_native_roots() .map_err(|e| Error::Other(Box::new(e)))? diff --git a/src/types.rs b/src/types.rs index 693744c..41da9b0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -29,24 +29,24 @@ pub enum Error { /// Failed to instantiate a private key #[error("invalid key bytes: {0}")] CryptoKey(#[from] ring::error::KeyRejected), - /// Hyper request failure - #[error("HTTP request failure: {0}")] - Hyper(#[from] hyper::Error), /// HTTP failure #[error("HTTP request failure: {0}")] Http(#[from] hyper::http::Error), + /// Hyper request failure + #[error("HTTP request failure: {0}")] + Hyper(#[from] hyper::Error), /// Invalid ACME server URL #[error("invalid URI: {0}")] InvalidUri(#[from] hyper::http::uri::InvalidUri), /// Failed to (de)serialize a JSON object #[error("failed to (de)serialize JSON: {0}")] Json(#[from] serde_json::Error), - /// Miscellaneous errors - #[error("missing data: {0}")] - Str(&'static str), /// Other kind of error #[error(transparent)] Other(Box), + /// Miscellaneous errors + #[error("missing data: {0}")] + Str(&'static str), } impl From<&'static str> for Error { From 53bb223d5f6742f839a5693f2e023af953883469 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Thu, 27 Jun 2024 09:52:53 +1000 Subject: [PATCH 13/14] Fix CI failures, bump MSRV in CI --- .github/workflows/rust.yml | 2 +- deny.toml | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index de46941..7c02fa9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -34,7 +34,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.63.0 + toolchain: 1.64.0 - run: cargo check --lib --all-features lint: diff --git a/deny.toml b/deny.toml index 0a8819d..a33d5ac 100644 --- a/deny.toml +++ b/deny.toml @@ -1,6 +1,6 @@ [licenses] version = 2 -allow = ["Apache-2.0", "ISC", "MIT", "OpenSSL", "Unicode-DFS-2016"] +allow = ["Apache-2.0", "BSD-3-Clause", "ISC", "MIT", "OpenSSL", "Unicode-DFS-2016"] [[licenses.clarify]] name = "ring" diff --git a/src/lib.rs b/src/lib.rs index 1cdb833..2b7927b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -483,7 +483,7 @@ impl Client { .header(CONTENT_TYPE, JOSE_JSON) .body(Full::from(serde_json::to_vec(&body)?))?; - Ok(self.http.request(request).await?) + self.http.request(request).await } async fn nonce(&self, nonce: Option) -> Result { From 764145c7c790724d488c0c1dd87ae535edffeac2 Mon Sep 17 00:00:00 2001 From: Luke Frisken Date: Thu, 27 Jun 2024 09:53:39 +1000 Subject: [PATCH 14/14] Bump crate version to 0.6.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 05a94af..feb792e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "instant-acme" -version = "0.5.0" +version = "0.6.0" edition = "2021" rust-version = "1.64" license = "Apache-2.0"