Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to hyper dependency to version 1 #50

Merged
merged 15 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -16,8 +16,10 @@ 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 }
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", "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"] }
Expand Down
86 changes: 47 additions & 39 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ use std::pin::Pin;
use std::sync::Arc;

use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
use hyper::client::connect::Connect;
#[cfg(feature = "hyper-rustls")]
use hyper::client::HttpConnector;
use http_body_util::{BodyExt, Full};
use hyper::body::{Bytes, Incoming};
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::HttpConnector;
use hyper_util::client::legacy::{connect::Connect, Client as HyperClient};
kellpossible marked this conversation as resolved.
Show resolved Hide resolved
use ring::digest::{digest, SHA256};
use ring::rand::SystemRandom;
use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING};
Expand Down Expand Up @@ -134,7 +136,11 @@ impl Order {
.await?;

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(
String::from_utf8(body.to_vec())
.map_err(|_| "unable to decode certificate as UTF-8")?,
Expand Down Expand Up @@ -206,7 +212,7 @@ impl Account {
pub async fn from_credentials(credentials: AccountCredentials) -> Result<Self, Error> {
Ok(Self {
inner: Arc::new(
AccountInner::from_credentials(credentials, Box::<DefaultClient>::default())
AccountInner::from_credentials(credentials, Box::new(DefaultClient::try_new()?))
.await?,
),
})
Expand Down Expand Up @@ -256,7 +262,7 @@ impl Account {
Self::create_inner(
account,
external_account,
Client::new(server_url, Box::<DefaultClient>::default()).await?,
Client::new(server_url, Box::new(DefaultClient::try_new()?)).await?,
server_url,
)
.await
Expand Down Expand Up @@ -419,7 +425,7 @@ impl AccountInner {
payload: Option<&impl Serialize>,
nonce: Option<String>,
url: &str,
) -> Result<Response<Body>, Error> {
) -> Result<Response<Incoming>, Error> {
self.client.post(payload, nonce, self, url).await
}
}
Expand Down Expand Up @@ -451,10 +457,10 @@ impl Client {
async fn new(server_url: &str, http: Box<dyn HttpClient>) -> Result<Self, Error> {
let req = Request::builder()
.uri(server_url)
.body(Body::empty())
.unwrap();
.body(Full::default())
.expect("Infallible error should not occur");
kellpossible marked this conversation as resolved.
Show resolved Hide resolved
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 {
http,
urls: serde_json::from_slice(&body)?,
Expand All @@ -467,15 +473,14 @@ impl Client {
nonce: Option<String>,
signer: &impl Signer,
url: &str,
) -> Result<Response<Body>, Error> {
) -> Result<Response<Incoming>, 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(Full::from(serde_json::to_vec(&body)?))?;

Ok(self.http.request(request).await?)
}
Expand All @@ -488,8 +493,8 @@ impl Client {
let request = Request::builder()
.method(Method::HEAD)
.uri(&self.urls.new_nonce)
.body(Body::empty())
.unwrap();
.body(Full::default())
.expect("Should be Infallible");
kellpossible marked this conversation as resolved.
Show resolved Hide resolved

let rsp = self.http.request(request).await?;
// https://datatracker.ietf.org/doc/html/rfc8555#section-7.2
Expand Down Expand Up @@ -650,38 +655,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()
.get(REPLAY_NONCE)
.and_then(|hv| String::from_utf8(hv.as_ref().to_vec()).ok())
}

#[cfg(feature = "hyper-rustls")]
struct DefaultClient(hyper::Client<hyper_rustls::HttpsConnector<HttpConnector>>);

#[cfg(feature = "hyper-rustls")]
impl HttpClient for DefaultClient {
fn request(
&self,
req: Request<Body>,
) -> Pin<Box<dyn Future<Output = hyper::Result<Response<Body>>> + Send>> {
Box::pin(self.0.request(req))
}
}
struct DefaultClient(HyperClient<hyper_rustls::HttpsConnector<HttpConnector>, Full<Bytes>>);

#[cfg(feature = "hyper-rustls")]
impl Default for DefaultClient {
fn default() -> Self {
Self(
hyper::Client::builder().build(
impl DefaultClient {
fn try_new() -> Result<Self, Error> {
Ok(Self(
HyperClient::builder(hyper_util::rt::TokioExecutor::new()).build(
kellpossible marked this conversation as resolved.
Show resolved Hide resolved
hyper_rustls::HttpsConnectorBuilder::new()
.with_native_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<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) })
}
}

Expand All @@ -690,19 +697,20 @@ pub trait HttpClient: Send + Sync + 'static {
/// Send the given request and return the response
fn request(
&self,
req: Request<Body>,
) -> Pin<Box<dyn Future<Output = hyper::Result<Response<Body>>> + Send>>;
req: Request<Full<Bytes>>,
) -> 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
C: Connect + Clone + Send + Sync + 'static,
{
fn request(
&self,
req: Request<Body>,
) -> Pin<Box<dyn Future<Output = hyper::Result<Response<Body>>> + Send>> {
Box::pin(<hyper::Client<C>>::request(self, req))
req: Request<Full<Bytes>>,
) -> Pin<Box<dyn Future<Output = Result<Response<hyper::body::Incoming>, Error>> + Send>> {
let fut = <HyperClient<C, Full<Bytes>>>::request(self, req);
Box::pin(async move { fut.await.map_err(Error::from) })
}
}

Expand Down
28 changes: 21 additions & 7 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::fmt;

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::signature::{EcdsaKeyPair, KeyPair};
use rustls_pki_types::CertificateDer;
Expand All @@ -27,9 +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),
Hyper(#[from] hyper::Error),
/// HTTP failure
#[error("HTTP request failure: {0}")]
kellpossible marked this conversation as resolved.
Show resolved Hide resolved
Http(#[from] hyper::http::Error),
/// Invalid ACME server URL
#[error("invalid URI: {0}")]
InvalidUri(#[from] hyper::http::uri::InvalidUri),
Expand All @@ -39,6 +44,9 @@ pub enum Error {
/// Miscellaneous errors
#[error("missing data: {0}")]
Str(&'static str),
/// Other kind of error
#[error(transparent)]
Other(Box<dyn std::error::Error + Send + Sync + 'static>),
kellpossible marked this conversation as resolved.
Show resolved Hide resolved
}

impl From<&'static str> for Error {
Expand All @@ -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
///
/// This opaque type contains the account ID, the private key data and the
Expand Down Expand Up @@ -119,20 +133,20 @@ pub struct 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(
&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 body = rsp.into_body();
if status.is_informational() || status.is_success() || status.is_redirection() {
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())
}
}
Expand Down
Loading