Update to hyper dependency to version 1 (#50)
This commit is contained in:
parent
547a495092
commit
efb83b464a
|
@ -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:
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -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"] }
|
||||||
|
|
|
@ -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"
|
||||||
|
|
89
src/lib.rs
89
src/lib.rs
|
@ -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) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
28
src/types.rs
28
src/types.rs
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue