Browse Source

Merge pull request #82 from jcgruenhage/async-http

Make http requests async, switch from attohttpc to reqwest
pull/89/head
Rodolphe Bréard 2 years ago
committed by GitHub
parent
commit
6b51a7f952
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 354
      Cargo.lock
  2. 2
      acme_common/Cargo.toml
  3. 4
      acme_common/src/error.rs
  4. 6
      acmed/Cargo.toml
  5. 2
      acmed/build.rs
  6. 13
      acmed/src/acme_proto.rs
  7. 9
      acmed/src/acme_proto/account.rs
  8. 35
      acmed/src/acme_proto/http.rs
  9. 10
      acmed/src/endpoint.rs
  10. 88
      acmed/src/http.rs

354
Cargo.lock

@ -6,7 +6,6 @@ version = 3
name = "acme_common"
version = "0.21.0"
dependencies = [
"attohttpc",
"base64",
"daemonize",
"env_logger",
@ -17,6 +16,7 @@ dependencies = [
"openssl",
"openssl-sys",
"punycode",
"reqwest",
"serde_json",
"syslog",
"tinytemplate",
@ -30,7 +30,6 @@ dependencies = [
"acme_common",
"async-lock",
"async-process",
"attohttpc",
"bincode",
"clap",
"futures",
@ -39,6 +38,7 @@ dependencies = [
"nix",
"nom",
"rand",
"reqwest",
"serde",
"serde_json",
"tinytemplate",
@ -165,22 +165,6 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
[[package]]
name = "attohttpc"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2"
dependencies = [
"encoding_rs",
"encoding_rs_io",
"http",
"log",
"native-tls",
"serde",
"serde_json",
"url",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -223,6 +207,12 @@ dependencies = [
"log",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "bytes"
version = "1.4.0"
@ -336,15 +326,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "encoding_rs_io"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83"
dependencies = [
"encoding_rs",
]
[[package]]
name = "env_logger"
version = "0.10.0"
@ -504,7 +485,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.13",
]
[[package]]
@ -554,6 +535,25 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "h2"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -597,12 +597,72 @@ dependencies = [
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes",
"http",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
[[package]]
name = "httpdate"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]]
name = "idna"
version = "0.3.0"
@ -643,6 +703,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "ipnet"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
[[package]]
name = "is-terminal"
version = "0.4.7"
@ -661,6 +727,15 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -719,6 +794,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -827,7 +908,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.13",
]
[[package]]
@ -1022,6 +1103,43 @@ version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "reqwest"
version = "0.11.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]]
name = "rustix"
version = "0.37.11"
@ -1097,7 +1215,7 @@ checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.13",
]
[[package]]
@ -1120,6 +1238,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "signal-hook"
version = "0.3.15"
@ -1176,6 +1306,17 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.13"
@ -1313,7 +1454,31 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.13",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
@ -1350,6 +1515,38 @@ dependencies = [
"winnow",
]
[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [
"once_cell",
]
[[package]]
name = "try-lock"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
@ -1406,12 +1603,98 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
"log",
"try-lock",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 1.0.109",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "web-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -1598,3 +1881,12 @@ checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]

2
acme_common/Cargo.toml

@ -19,7 +19,6 @@ openssl_dyn = ["crypto_openssl", "openssl", "openssl-sys"]
openssl_vendored = ["crypto_openssl", "openssl/vendored", "openssl-sys/vendored"]
[dependencies]
attohttpc = { version = "0.24", default-features = false }
base64 = "0.21"
daemonize = "0.5"
env_logger = "0.10"
@ -29,6 +28,7 @@ native-tls = "0.2"
openssl = { version = "0.10", optional = true }
openssl-sys = { version = "0.9", optional = true }
punycode = "0.4"
reqwest = { version = "0.11.16", default-features = false }
serde_json = "1.0"
syslog = "6.0"
tinytemplate = "1.2"

4
acme_common/src/error.rs

@ -93,8 +93,8 @@ impl From<serde_json::error::Error> for Error {
}
}
impl From<attohttpc::Error> for Error {
fn from(error: attohttpc::Error) -> Self {
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
format!("HTTP error: {error}").into()
}
}

6
acmed/Cargo.toml

@ -16,14 +16,13 @@ publish = false
[features]
default = ["openssl_dyn"]
crypto_openssl = []
openssl_dyn = ["crypto_openssl", "acme_common/openssl_dyn", "attohttpc/tls"]
openssl_vendored = ["crypto_openssl", "acme_common/openssl_vendored", "attohttpc/tls", "attohttpc/tls-vendored"]
openssl_dyn = ["crypto_openssl", "acme_common/openssl_dyn"]
openssl_vendored = ["crypto_openssl", "acme_common/openssl_vendored"]
[dependencies]
acme_common = { path = "../acme_common" }
async-lock = "2.6"
async-process = "1.6"
attohttpc = { version = "0.24", default-features = false, features = ["charsets", "json"] }
bincode = "1.3"
clap = { version = "4.0", features = ["string"] }
futures = "0.3"
@ -36,6 +35,7 @@ tinytemplate = "1.2"
toml = "0.7"
tokio = { version = "1", features = ["full"] }
rand = "0.8.5"
reqwest = "0.11.16"
[target.'cfg(unix)'.dependencies]
nix = "0.26"

2
acmed/build.rs

@ -96,7 +96,7 @@ fn set_lock() {
}
};
for p in lock.package.iter() {
if p.name == "attohttpc" {
if p.name == "reqwest" {
let agent = format!("{}/{}", p.name, p.version);
set_rustc_env_var!("ACMED_HTTP_LIB_AGENT", agent);
set_rustc_env_var!("ACMED_HTTP_LIB_NAME", p.name);

13
acmed/src/acme_proto.rs

@ -94,7 +94,9 @@ pub async fn request_certificate(
let endpoint_name = endpoint_s.read().await.name.clone();
// Refresh the directory
http::refresh_directory(&mut *(endpoint_s.write().await)).map_err(HttpError::in_err)?;
http::refresh_directory(&mut *(endpoint_s.write().await))
.await
.map_err(HttpError::in_err)?;
// Synchronize the account
account_s
@ -109,7 +111,7 @@ pub async fn request_certificate(
let new_order = NewOrder::new(&cert.identifiers);
let new_order = serde_json::to_string(&new_order)?;
let data_builder = set_data_builder!(account_s, endpoint_name, new_order.as_bytes()).await;
match http::new_order(&mut *(endpoint_s.write().await), &data_builder) {
match http::new_order(&mut *(endpoint_s.write().await), &data_builder).await {
Ok((order, order_url)) => {
if let Some(e) = order.get_error() {
cert.warn(&e.prefix("Error").message);
@ -138,6 +140,7 @@ pub async fn request_certificate(
let data_builder = set_data_builder!(account_s, endpoint_name, b"").await;
let auth =
http::get_authorization(&mut *(endpoint_s.write().await), &data_builder, auth_url)
.await
.map_err(HttpError::in_err)?;
drop(data_builder);
if let Some(e) = auth.get_error() {
@ -178,6 +181,7 @@ pub async fn request_certificate(
&data_builder,
&chall_url,
)
.await
.map_err(HttpError::in_err)?;
drop(data_builder);
}
@ -192,6 +196,7 @@ pub async fn request_certificate(
&break_fn,
auth_url,
)
.await
.map_err(HttpError::in_err)?;
drop(data_builder);
for (data, hook_type) in hook_datas.iter() {
@ -211,6 +216,7 @@ pub async fn request_certificate(
&break_fn,
&order_url,
)
.await
.map_err(HttpError::in_err)?;
drop(data_builder);
@ -246,6 +252,7 @@ pub async fn request_certificate(
&data_builder,
&order.finalize,
)
.await
.map_err(HttpError::in_err)?;
drop(data_builder);
if let Some(e) = order.get_error() {
@ -261,6 +268,7 @@ pub async fn request_certificate(
&break_fn,
&order_url,
)
.await
.map_err(HttpError::in_err)?;
drop(data_builder);
@ -270,6 +278,7 @@ pub async fn request_certificate(
.ok_or_else(|| Error::from("no certificate available for download"))?;
let data_builder = set_data_builder!(account_s, endpoint_name, b"").await;
let crt = http::get_certificate(&mut *(endpoint_s.write().await), &data_builder, &crt_url)
.await
.map_err(HttpError::in_err)?;
drop(data_builder);
storage::write_certificate(&cert.file_manager, crt.as_bytes()).await?;

9
acmed/src/acme_proto/account.rs

@ -52,8 +52,9 @@ pub async fn register_account(
Some(n.to_string()),
)
};
let (acc_rep, account_url) =
http::new_account(endpoint, &data_builder).map_err(HttpError::in_err)?;
let (acc_rep, account_url) = http::new_account(endpoint, &data_builder)
.await
.map_err(HttpError::in_err)?;
account.set_account_url(&endpoint.name, &account_url)?;
let orders_url = match acc_rep.orders {
Some(url) => url,
@ -95,7 +96,7 @@ pub async fn update_account_contacts(
set_data_builder_sync!(account_owned, endpoint_name, acc_up_struct.as_bytes());
let url = account.get_endpoint(&endpoint_name)?.account_url.clone();
create_account_if_does_not_exist!(
http::post_jose_no_response(endpoint, &data_builder, &url),
http::post_jose_no_response(endpoint, &data_builder, &url).await,
endpoint,
account
)?;
@ -140,7 +141,7 @@ pub async fn update_account_key(
)
};
create_account_if_does_not_exist!(
http::post_jose_no_response(endpoint, &data_builder, &url),
http::post_jose_no_response(endpoint, &data_builder, &url).await,
endpoint,
account
)?;

35
acmed/src/acme_proto/http.rs

@ -8,7 +8,7 @@ macro_rules! pool_object {
($obj_type: ty, $obj_name: expr, $endpoint: expr, $url: expr, $data_builder: expr, $break: expr) => {{
for _ in 0..crate::DEFAULT_POOL_NB_TRIES {
thread::sleep(time::Duration::from_secs(crate::DEFAULT_POOL_WAIT_SEC));
let response = http::post_jose($endpoint, $url, $data_builder)?;
let response = http::post_jose($endpoint, $url, $data_builder).await?;
let obj = response.json::<$obj_type>()?;
if $break(&obj) {
return Ok(obj);
@ -19,14 +19,14 @@ macro_rules! pool_object {
}};
}
pub fn refresh_directory(endpoint: &mut Endpoint) -> Result<(), http::HttpError> {
pub async fn refresh_directory(endpoint: &mut Endpoint) -> Result<(), http::HttpError> {
let url = endpoint.url.clone();
let response = http::get(endpoint, &url)?;
let response = http::get(endpoint, &url).await?;
endpoint.dir = response.json::<Directory>()?;
Ok(())
}
pub fn post_jose_no_response<F>(
pub async fn post_jose_no_response<F>(
endpoint: &mut Endpoint,
data_builder: &F,
url: &str,
@ -34,11 +34,11 @@ pub fn post_jose_no_response<F>(
where
F: Fn(&str, &str) -> Result<String, Error>,
{
let _ = http::post_jose(endpoint, url, data_builder)?;
let _ = http::post_jose(endpoint, url, data_builder).await?;
Ok(())
}
pub fn new_account<F>(
pub async fn new_account<F>(
endpoint: &mut Endpoint,
data_builder: &F,
) -> Result<(AccountResponse, String), http::HttpError>
@ -46,7 +46,7 @@ where
F: Fn(&str, &str) -> Result<String, Error>,
{
let url = endpoint.dir.new_account.clone();
let response = http::post_jose(endpoint, &url, data_builder)?;
let response = http::post_jose(endpoint, &url, data_builder).await?;
let acc_uri = response
.get_header(http::HEADER_LOCATION)
.ok_or_else(|| Error::from("no account location found"))?;
@ -54,7 +54,7 @@ where
Ok((acc_resp, acc_uri))
}
pub fn new_order<F>(
pub async fn new_order<F>(
endpoint: &mut Endpoint,
data_builder: &F,
) -> Result<(Order, String), http::HttpError>
@ -62,7 +62,7 @@ where
F: Fn(&str, &str) -> Result<String, Error>,
{
let url = endpoint.dir.new_order.clone();
let response = http::post_jose(endpoint, &url, data_builder)?;
let response = http::post_jose(endpoint, &url, data_builder).await?;
let order_uri = response
.get_header(http::HEADER_LOCATION)
.ok_or_else(|| Error::from("no account location found"))?;
@ -70,7 +70,7 @@ where
Ok((order_resp, order_uri))
}
pub fn get_authorization<F>(
pub async fn get_authorization<F>(
endpoint: &mut Endpoint,
data_builder: &F,
url: &str,
@ -78,12 +78,12 @@ pub fn get_authorization<F>(
where
F: Fn(&str, &str) -> Result<String, Error>,
{
let response = http::post_jose(endpoint, url, data_builder)?;
let response = http::post_jose(endpoint, url, data_builder).await?;
let auth = response.json::<Authorization>()?;
Ok(auth)
}
pub fn pool_authorization<F, S>(
pub async fn pool_authorization<F, S>(
endpoint: &mut Endpoint,
data_builder: &F,
break_fn: &S,
@ -103,7 +103,7 @@ where
)
}
pub fn pool_order<F, S>(
pub async fn pool_order<F, S>(
endpoint: &mut Endpoint,
data_builder: &F,
break_fn: &S,
@ -116,7 +116,7 @@ where
pool_object!(Order, "order", endpoint, url, data_builder, break_fn)
}
pub fn finalize_order<F>(
pub async fn finalize_order<F>(
endpoint: &mut Endpoint,
data_builder: &F,
url: &str,
@ -124,12 +124,12 @@ pub fn finalize_order<F>(
where
F: Fn(&str, &str) -> Result<String, Error>,
{
let response = http::post_jose(endpoint, url, data_builder)?;
let response = http::post_jose(endpoint, url, data_builder).await?;
let order = response.json::<Order>()?;
Ok(order)
}
pub fn get_certificate<F>(
pub async fn get_certificate<F>(
endpoint: &mut Endpoint,
data_builder: &F,
url: &str,
@ -143,6 +143,7 @@ where
data_builder,
http::CONTENT_TYPE_JOSE,
http::CONTENT_TYPE_PEM,
)?;
)
.await?;
Ok(response.body)
}

10
acmed/src/endpoint.rs

@ -2,8 +2,8 @@ use crate::acme_proto::structs::Directory;
use crate::duration::parse_duration;
use acme_common::error::Error;
use std::cmp;
use std::thread;
use std::time::{Duration, Instant};
use tokio::time::sleep;
#[derive(Clone, Debug)]
pub struct Endpoint {
@ -65,19 +65,19 @@ impl RateLimit {
})
}
pub fn block_until_allowed(&mut self) {
pub async fn block_until_allowed(&mut self) {
if self.limits.is_empty() {
return;
}
let sleep_duration = self.get_sleep_duration();
let mut sleep_duration = self.get_sleep_duration();
loop {
sleep(sleep_duration).await;
self.prune_log();
if self.request_allowed() {
self.query_log.push(Instant::now());
return;
}
// TODO: find a better sleep duration
thread::sleep(sleep_duration);
sleep_duration = self.get_sleep_duration();
}
}

88
acmed/src/http.rs

@ -1,9 +1,9 @@
use crate::acme_proto::structs::{AcmeError, HttpApiError};
use crate::endpoint::Endpoint;
#[cfg(feature = "crypto_openssl")]
use acme_common::crypto::X509Certificate;
use acme_common::error::Error;
use attohttpc::{charsets, header, Response, Session};
use reqwest::header::{HeaderMap, HeaderValue};
use reqwest::{header, Client, ClientBuilder, Response};
use std::fs::File;
#[cfg(feature = "crypto_openssl")]
use std::io::prelude::*;
@ -16,7 +16,7 @@ pub const HEADER_NONCE: &str = "Replay-Nonce";
pub const HEADER_LOCATION: &str = "Location";
pub struct ValidHttpResponse {
headers: attohttpc::header::HeaderMap,
headers: HeaderMap,
pub body: String,
}
@ -38,9 +38,9 @@ impl ValidHttpResponse {
serde_json::from_str(&self.body).map_err(Error::from)
}
fn from_response(response: Response) -> Result<Self, Error> {
let (_status, headers, body) = response.split();
let body = body.text()?;
async fn from_response(response: Response) -> Result<Self, Error> {
let headers = response.headers().clone();
let body = response.text().await?;
log::trace!("HTTP response headers: {headers:?}");
log::trace!("HTTP response body: {body}");
Ok(ValidHttpResponse { headers, body })
@ -93,8 +93,8 @@ impl From<String> for HttpError {
}
}
impl From<attohttpc::Error> for HttpError {
fn from(error: attohttpc::Error) -> Self {
impl From<reqwest::Error> for HttpError {
fn from(error: reqwest::Error) -> Self {
HttpError::GenericError(error.into())
}
}
@ -106,10 +106,10 @@ fn is_nonce(data: &str) -> bool {
.all(|c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
}
fn new_nonce(endpoint: &mut Endpoint) -> Result<(), HttpError> {
rate_limit(endpoint);
async fn new_nonce(endpoint: &mut Endpoint) -> Result<(), HttpError> {
rate_limit(endpoint).await;
let url = endpoint.dir.new_nonce.clone();
let _ = get(endpoint, &url)?;
let _ = get(endpoint, &url).await?;
Ok(())
}
@ -126,7 +126,7 @@ fn update_nonce(endpoint: &mut Endpoint, response: &Response) -> Result<(), Erro
}
fn check_status(response: &Response) -> Result<(), Error> {
if !response.is_success() {
if !response.status().is_success() {
let status = response.status();
let msg = format!("HTTP error: {}: {}", status.as_u16(), status.as_str());
return Err(msg.into());
@ -134,18 +134,18 @@ fn check_status(response: &Response) -> Result<(), Error> {
Ok(())
}
fn rate_limit(endpoint: &mut Endpoint) {
endpoint.rl.block_until_allowed();
async fn rate_limit(endpoint: &mut Endpoint) {
endpoint.rl.block_until_allowed().await;
}
fn header_to_string(header_value: &header::HeaderValue) -> Result<String, Error> {
fn header_to_string(header_value: &HeaderValue) -> Result<String, Error> {
let s = header_value
.to_str()
.map_err(|_| Error::from("invalid header format"))?;
Ok(s.to_string())
}
fn get_session(root_certs: &[String]) -> Result<Session, Error> {
fn get_client(root_certs: &[String]) -> Result<Client, Error> {
let useragent = format!(
"{}/{} ({}) {}",
crate::APP_NAME,
@ -154,10 +154,11 @@ fn get_session(root_certs: &[String]) -> Result<Session, Error> {
env!("ACMED_HTTP_LIB_AGENT")
);
// TODO: allow to change the language
let mut session = Session::new();
session.default_charset(Some(charsets::UTF_8));
session.try_header(header::ACCEPT_LANGUAGE, "en-US,en;q=0.5")?;
session.try_header(header::USER_AGENT, &useragent)?;
let mut client_builder = ClientBuilder::new();
let mut default_headers = HeaderMap::new();
default_headers.append(header::ACCEPT_LANGUAGE, "en-US,en;q=0.5".parse().unwrap());
default_headers.append(header::USER_AGENT, useragent.parse().unwrap());
client_builder = client_builder.default_headers(default_headers);
for crt_file in root_certs.iter() {
#[cfg(feature = "crypto_openssl")]
{
@ -165,24 +166,29 @@ fn get_session(root_certs: &[String]) -> Result<Session, Error> {
File::open(crt_file)
.map_err(|e| Error::from(e).prefix(crt_file))?
.read_to_end(&mut buff)?;
let crt = X509Certificate::from_pem_native(&buff)?;
session.add_root_certificate(crt);
let crt = reqwest::Certificate::from_pem(&buff)?;
client_builder = client_builder.add_root_certificate(crt);
}
}
Ok(session)
Ok(client_builder.build()?)
}
pub fn get(endpoint: &mut Endpoint, url: &str) -> Result<ValidHttpResponse, HttpError> {
let mut session = get_session(&endpoint.root_certificates)?;
session.try_header(header::ACCEPT, CONTENT_TYPE_JSON)?;
rate_limit(endpoint);
let response = session.get(url).send()?;
pub async fn get(endpoint: &mut Endpoint, url: &str) -> Result<ValidHttpResponse, HttpError> {
let client = get_client(&endpoint.root_certificates)?;
rate_limit(endpoint).await;
let response = client
.get(url)
.header(header::ACCEPT, CONTENT_TYPE_JSON)
.send()
.await?;
update_nonce(endpoint, &response)?;
check_status(&response)?;
ValidHttpResponse::from_response(response).map_err(HttpError::from)
ValidHttpResponse::from_response(response)
.await
.map_err(HttpError::from)
}
pub fn post<F>(
pub async fn post<F>(
endpoint: &mut Endpoint,
url: &str,
data_builder: &F,
@ -192,25 +198,28 @@ pub fn post<F>(
where
F: Fn(&str, &str) -> Result<String, Error>,
{
let mut session = get_session(&endpoint.root_certificates)?;
session.try_header(header::ACCEPT, accept)?;
session.try_header(header::CONTENT_TYPE, content_type)?;
let client = get_client(&endpoint.root_certificates)?;
if endpoint.nonce.is_none() {
let _ = new_nonce(endpoint);
let _ = new_nonce(endpoint).await;
}
for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY {
let mut request = client.post(url);
request = request.header(header::ACCEPT, accept);
request = request.header(header::CONTENT_TYPE, content_type);
let nonce = &endpoint.nonce.clone().unwrap_or_default();
let body = data_builder(nonce, url)?;
rate_limit(endpoint);
rate_limit(endpoint).await;
log::trace!("POST request body: {body}");
let response = session.post(url).text(&body).send()?;
let response = request.body(body).send().await?;
update_nonce(endpoint, &response)?;
match check_status(&response) {
Ok(_) => {
return ValidHttpResponse::from_response(response).map_err(HttpError::from);
return ValidHttpResponse::from_response(response)
.await
.map_err(HttpError::from);
}
Err(_) => {
let resp = ValidHttpResponse::from_response(response)?;
let resp = ValidHttpResponse::from_response(response).await?;
let api_err = resp.json::<HttpApiError>()?;
let acme_err = api_err.get_acme_type();
if !acme_err.is_recoverable() {
@ -223,7 +232,7 @@ where
Err("too much errors, will not retry".into())
}
pub fn post_jose<F>(
pub async fn post_jose<F>(
endpoint: &mut Endpoint,
url: &str,
data_builder: &F,
@ -238,6 +247,7 @@ where
CONTENT_TYPE_JOSE,
CONTENT_TYPE_JSON,
)
.await
}
#[cfg(test)]

Loading…
Cancel
Save