Update to hyper dependency to version 1 (#50)

This commit is contained in:
Luke Frisken 2024-06-27 21:04:02 +10:00 committed by GitHub
parent 547a495092
commit efb83b464a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 78 additions and 53 deletions

View File

@ -34,7 +34,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.63.0 toolchain: 1.64.0
- run: cargo check --lib --all-features - run: cargo check --lib --all-features
lint: lint:

View File

@ -1,8 +1,8 @@
[package] [package]
name = "instant-acme" name = "instant-acme"
version = "0.5.0" version = "0.6.0"
edition = "2021" edition = "2021"
rust-version = "1.63" rust-version = "1.64"
license = "Apache-2.0" license = "Apache-2.0"
description = "Async pure-Rust ACME client" description = "Async pure-Rust ACME client"
homepage = "https://github.com/instant-labs/instant-acme" homepage = "https://github.com/instant-labs/instant-acme"
@ -16,8 +16,10 @@ default = ["hyper-rustls"]
[dependencies] [dependencies]
base64 = "0.21.0" base64 = "0.21.0"
hyper = { version = "0.14.18", features = ["client", "http1", "http2"] } hyper = { version = "1.3.1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.24", default-features = false, features = ["http1", "http2", "native-tokio", "tls12"], 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"] } ring = { version = "0.17", features = ["std"] }
rustls-pki-types = "1.1.0" rustls-pki-types = "1.1.0"
serde = { version = "1.0.104", features = ["derive"] } serde = { version = "1.0.104", features = ["derive"] }

View File

@ -1,6 +1,6 @@
[licenses] [licenses]
version = 2 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]] [[licenses.clarify]]
name = "ring" name = "ring"

View File

@ -9,11 +9,14 @@ use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
use hyper::client::connect::Connect; use http_body_util::{BodyExt, Full};
#[cfg(feature = "hyper-rustls")] use hyper::body::{Bytes, Incoming};
use hyper::client::HttpConnector;
use hyper::header::{CONTENT_TYPE, LOCATION}; use hyper::header::{CONTENT_TYPE, LOCATION};
use hyper::{Body, Method, Request, Response, StatusCode}; use hyper::{Method, Request, Response, StatusCode};
#[cfg(feature = "hyper-rustls")]
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::digest::{digest, SHA256};
use ring::rand::SystemRandom; use ring::rand::SystemRandom;
use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING};
@ -134,7 +137,11 @@ impl Order {
.await?; .await?;
self.nonce = nonce_from_response(&rsp); self.nonce = nonce_from_response(&rsp);
let body = hyper::body::to_bytes(Problem::from_response(rsp).await?).await?; let body = Problem::from_response(rsp)
.await?
.collect()
.await?
.to_bytes();
Ok(Some( Ok(Some(
String::from_utf8(body.to_vec()) String::from_utf8(body.to_vec())
.map_err(|_| "unable to decode certificate as UTF-8")?, .map_err(|_| "unable to decode certificate as UTF-8")?,
@ -206,7 +213,7 @@ impl Account {
pub async fn from_credentials(credentials: AccountCredentials) -> Result<Self, Error> { pub async fn from_credentials(credentials: AccountCredentials) -> Result<Self, Error> {
Ok(Self { Ok(Self {
inner: Arc::new( inner: Arc::new(
AccountInner::from_credentials(credentials, Box::<DefaultClient>::default()) AccountInner::from_credentials(credentials, Box::new(DefaultClient::try_new()?))
.await?, .await?,
), ),
}) })
@ -256,7 +263,7 @@ impl Account {
Self::create_inner( Self::create_inner(
account, account,
external_account, external_account,
Client::new(server_url, Box::<DefaultClient>::default()).await?, Client::new(server_url, Box::new(DefaultClient::try_new()?)).await?,
server_url, server_url,
) )
.await .await
@ -419,7 +426,7 @@ impl AccountInner {
payload: Option<&impl Serialize>, payload: Option<&impl Serialize>,
nonce: Option<String>, nonce: Option<String>,
url: &str, url: &str,
) -> Result<Response<Body>, Error> { ) -> Result<Response<Incoming>, Error> {
self.client.post(payload, nonce, self, url).await self.client.post(payload, nonce, self, url).await
} }
} }
@ -451,10 +458,10 @@ impl Client {
async fn new(server_url: &str, http: Box<dyn HttpClient>) -> Result<Self, Error> { async fn new(server_url: &str, http: Box<dyn HttpClient>) -> Result<Self, Error> {
let req = Request::builder() let req = Request::builder()
.uri(server_url) .uri(server_url)
.body(Body::empty()) .body(Full::default())
.unwrap(); .expect("infallible error should not occur");
let rsp = http.request(req).await?; let rsp = http.request(req).await?;
let body = hyper::body::to_bytes(rsp.into_body()).await?; let body = rsp.into_body().collect().await?.to_bytes();
Ok(Client { Ok(Client {
http, http,
urls: serde_json::from_slice(&body)?, urls: serde_json::from_slice(&body)?,
@ -467,17 +474,16 @@ impl Client {
nonce: Option<String>, nonce: Option<String>,
signer: &impl Signer, signer: &impl Signer,
url: &str, url: &str,
) -> Result<Response<Body>, Error> { ) -> Result<Response<Incoming>, Error> {
let nonce = self.nonce(nonce).await?; let nonce = self.nonce(nonce).await?;
let body = JoseJson::new(payload, signer.header(Some(&nonce), url), signer)?; let body = JoseJson::new(payload, signer.header(Some(&nonce), url), signer)?;
let request = Request::builder() let request = Request::builder()
.method(Method::POST) .method(Method::POST)
.uri(url) .uri(url)
.header(CONTENT_TYPE, JOSE_JSON) .header(CONTENT_TYPE, JOSE_JSON)
.body(Body::from(serde_json::to_vec(&body)?)) .body(Full::from(serde_json::to_vec(&body)?))?;
.unwrap();
Ok(self.http.request(request).await?) self.http.request(request).await
} }
async fn nonce(&self, nonce: Option<String>) -> Result<String, Error> { async fn nonce(&self, nonce: Option<String>) -> Result<String, Error> {
@ -488,8 +494,8 @@ impl Client {
let request = Request::builder() let request = Request::builder()
.method(Method::HEAD) .method(Method::HEAD)
.uri(&self.urls.new_nonce) .uri(&self.urls.new_nonce)
.body(Body::empty()) .body(Full::default())
.unwrap(); .expect("infallible error should not occur");
let rsp = self.http.request(request).await?; let rsp = self.http.request(request).await?;
// https://datatracker.ietf.org/doc/html/rfc8555#section-7.2 // https://datatracker.ietf.org/doc/html/rfc8555#section-7.2
@ -650,38 +656,40 @@ impl Signer for ExternalAccountKey {
} }
} }
fn nonce_from_response(rsp: &Response<Body>) -> Option<String> { fn nonce_from_response(rsp: &Response<Incoming>) -> Option<String> {
rsp.headers() rsp.headers()
.get(REPLAY_NONCE) .get(REPLAY_NONCE)
.and_then(|hv| String::from_utf8(hv.as_ref().to_vec()).ok()) .and_then(|hv| String::from_utf8(hv.as_ref().to_vec()).ok())
} }
#[cfg(feature = "hyper-rustls")] #[cfg(feature = "hyper-rustls")]
struct DefaultClient(hyper::Client<hyper_rustls::HttpsConnector<HttpConnector>>); struct DefaultClient(HyperClient<hyper_rustls::HttpsConnector<HttpConnector>, Full<Bytes>>);
#[cfg(feature = "hyper-rustls")] #[cfg(feature = "hyper-rustls")]
impl HttpClient for DefaultClient { impl DefaultClient {
fn request( fn try_new() -> Result<Self, Error> {
&self, Ok(Self(
req: Request<Body>, HyperClient::builder(TokioExecutor::new()).build(
) -> Pin<Box<dyn Future<Output = hyper::Result<Response<Body>>> + Send>> {
Box::pin(self.0.request(req))
}
}
#[cfg(feature = "hyper-rustls")]
impl Default for DefaultClient {
fn default() -> Self {
Self(
hyper::Client::builder().build(
hyper_rustls::HttpsConnectorBuilder::new() hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots() .with_native_roots()
.map_err(|e| Error::Other(Box::new(e)))?
.https_only() .https_only()
.enable_http1() .enable_http1()
.enable_http2() .enable_http2()
.build(), .build(),
), ),
) ))
}
}
#[cfg(feature = "hyper-rustls")]
impl HttpClient for DefaultClient {
fn request(
&self,
req: Request<Full<Bytes>>,
) -> Pin<Box<dyn Future<Output = Result<Response<Incoming>, Error>> + Send>> {
let fut = self.0.request(req);
Box::pin(async move { fut.await.map_err(Error::from) })
} }
} }
@ -690,19 +698,20 @@ pub trait HttpClient: Send + Sync + 'static {
/// Send the given request and return the response /// Send the given request and return the response
fn request( fn request(
&self, &self,
req: Request<Body>, req: Request<Full<Bytes>>,
) -> Pin<Box<dyn Future<Output = hyper::Result<Response<Body>>> + Send>>; ) -> Pin<Box<dyn Future<Output = Result<Response<Incoming>, Error>> + Send>>;
} }
impl<C> HttpClient for hyper::Client<C> impl<C> HttpClient for HyperClient<C, Full<Bytes>>
where where
C: Connect + Clone + Send + Sync + 'static, C: Connect + Clone + Send + Sync + 'static,
{ {
fn request( fn request(
&self, &self,
req: Request<Body>, req: Request<Full<Bytes>>,
) -> Pin<Box<dyn Future<Output = hyper::Result<Response<Body>>> + Send>> { ) -> Pin<Box<dyn Future<Output = Result<Response<hyper::body::Incoming>, Error>> + Send>> {
Box::pin(<hyper::Client<C>>::request(self, req)) let fut = <HyperClient<C, Full<Bytes>>>::request(self, req);
Box::pin(async move { fut.await.map_err(Error::from) })
} }
} }

View File

@ -1,7 +1,9 @@
use std::fmt; use std::fmt;
use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
use hyper::{Body, Response}; use http_body_util::BodyExt;
use hyper::body::Incoming;
use hyper::Response;
use ring::digest::{digest, Digest, SHA256}; use ring::digest::{digest, Digest, SHA256};
use ring::signature::{EcdsaKeyPair, KeyPair}; use ring::signature::{EcdsaKeyPair, KeyPair};
use rustls_pki_types::CertificateDer; use rustls_pki_types::CertificateDer;
@ -27,15 +29,21 @@ pub enum Error {
/// Failed to instantiate a private key /// Failed to instantiate a private key
#[error("invalid key bytes: {0}")] #[error("invalid key bytes: {0}")]
CryptoKey(#[from] ring::error::KeyRejected), CryptoKey(#[from] ring::error::KeyRejected),
/// HTTP request failure /// HTTP failure
#[error("HTTP request failure: {0}")] #[error("HTTP request failure: {0}")]
Http(#[from] hyper::Error), Http(#[from] hyper::http::Error),
/// Hyper request failure
#[error("HTTP request failure: {0}")]
Hyper(#[from] hyper::Error),
/// Invalid ACME server URL /// Invalid ACME server URL
#[error("invalid URI: {0}")] #[error("invalid URI: {0}")]
InvalidUri(#[from] hyper::http::uri::InvalidUri), InvalidUri(#[from] hyper::http::uri::InvalidUri),
/// Failed to (de)serialize a JSON object /// Failed to (de)serialize a JSON object
#[error("failed to (de)serialize JSON: {0}")] #[error("failed to (de)serialize JSON: {0}")]
Json(#[from] serde_json::Error), Json(#[from] serde_json::Error),
/// Other kind of error
#[error(transparent)]
Other(Box<dyn std::error::Error + Send + Sync + 'static>),
/// Miscellaneous errors /// Miscellaneous errors
#[error("missing data: {0}")] #[error("missing data: {0}")]
Str(&'static str), Str(&'static str),
@ -47,6 +55,12 @@ impl From<&'static str> for Error {
} }
} }
impl From<hyper_util::client::legacy::Error> for Error {
fn from(value: hyper_util::client::legacy::Error) -> Self {
Self::Other(Box::new(value))
}
}
/// ACME account credentials /// ACME account credentials
/// ///
/// This opaque type contains the account ID, the private key data and the /// This opaque type contains the account ID, the private key data and the
@ -119,20 +133,20 @@ pub struct Problem {
} }
impl Problem { impl Problem {
pub(crate) async fn check<T: DeserializeOwned>(rsp: Response<Body>) -> Result<T, Error> { pub(crate) async fn check<T: DeserializeOwned>(rsp: Response<Incoming>) -> Result<T, Error> {
Ok(serde_json::from_slice( 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<Body>) -> Result<Body, Error> { pub(crate) async fn from_response(rsp: Response<Incoming>) -> Result<Incoming, Error> {
let status = rsp.status(); let status = rsp.status();
let body = rsp.into_body(); let body = rsp.into_body();
if status.is_informational() || status.is_success() || status.is_redirection() { if status.is_informational() || status.is_success() || status.is_redirection() {
return Ok(body); return Ok(body);
} }
let body = hyper::body::to_bytes(body).await?; let body = body.collect().await?.to_bytes();
Err(serde_json::from_slice::<Problem>(&body)?.into()) Err(serde_json::from_slice::<Problem>(&body)?.into())
} }
} }