From e4b97d6b4ff50fd7bfc580286e2b42a45e676c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Sat, 8 Apr 2023 23:28:55 +0200 Subject: [PATCH 1/2] Make http requests async, switch from attohttpc to reqwest --- Cargo.lock | 354 +++++++++++++++++++++++++++++--- acme_common/Cargo.toml | 2 +- acme_common/src/error.rs | 4 +- acmed/Cargo.toml | 6 +- acmed/build.rs | 2 +- acmed/src/acme_proto.rs | 13 +- acmed/src/acme_proto/account.rs | 9 +- acmed/src/acme_proto/http.rs | 35 ++-- acmed/src/http.rs | 78 ++++--- 9 files changed, 408 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c7e16e..3ba0685 100644 --- a/Cargo.lock +++ b/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", +] diff --git a/acme_common/Cargo.toml b/acme_common/Cargo.toml index 325a57b..2ad86c3 100644 --- a/acme_common/Cargo.toml +++ b/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" diff --git a/acme_common/src/error.rs b/acme_common/src/error.rs index 44cf1c0..18ca131 100644 --- a/acme_common/src/error.rs +++ b/acme_common/src/error.rs @@ -93,8 +93,8 @@ impl From for Error { } } -impl From for Error { - fn from(error: attohttpc::Error) -> Self { +impl From for Error { + fn from(error: reqwest::Error) -> Self { format!("HTTP error: {error}").into() } } diff --git a/acmed/Cargo.toml b/acmed/Cargo.toml index a6d097f..22de4af 100644 --- a/acmed/Cargo.toml +++ b/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" diff --git a/acmed/build.rs b/acmed/build.rs index 2e0fed1..631c03d 100644 --- a/acmed/build.rs +++ b/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); diff --git a/acmed/src/acme_proto.rs b/acmed/src/acme_proto.rs index 1ddffd7..e16b28b 100644 --- a/acmed/src/acme_proto.rs +++ b/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?; diff --git a/acmed/src/acme_proto/account.rs b/acmed/src/acme_proto/account.rs index aace673..8b8fec0 100644 --- a/acmed/src/acme_proto/account.rs +++ b/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 )?; diff --git a/acmed/src/acme_proto/http.rs b/acmed/src/acme_proto/http.rs index 022ec42..212ef1c 100644 --- a/acmed/src/acme_proto/http.rs +++ b/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::()?; Ok(()) } -pub fn post_jose_no_response( +pub async fn post_jose_no_response( endpoint: &mut Endpoint, data_builder: &F, url: &str, @@ -34,11 +34,11 @@ pub fn post_jose_no_response( where F: Fn(&str, &str) -> Result, { - let _ = http::post_jose(endpoint, url, data_builder)?; + let _ = http::post_jose(endpoint, url, data_builder).await?; Ok(()) } -pub fn new_account( +pub async fn new_account( endpoint: &mut Endpoint, data_builder: &F, ) -> Result<(AccountResponse, String), http::HttpError> @@ -46,7 +46,7 @@ where F: Fn(&str, &str) -> Result, { 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( +pub async fn new_order( endpoint: &mut Endpoint, data_builder: &F, ) -> Result<(Order, String), http::HttpError> @@ -62,7 +62,7 @@ where F: Fn(&str, &str) -> Result, { 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( +pub async fn get_authorization( endpoint: &mut Endpoint, data_builder: &F, url: &str, @@ -78,12 +78,12 @@ pub fn get_authorization( where F: Fn(&str, &str) -> Result, { - let response = http::post_jose(endpoint, url, data_builder)?; + let response = http::post_jose(endpoint, url, data_builder).await?; let auth = response.json::()?; Ok(auth) } -pub fn pool_authorization( +pub async fn pool_authorization( endpoint: &mut Endpoint, data_builder: &F, break_fn: &S, @@ -103,7 +103,7 @@ where ) } -pub fn pool_order( +pub async fn pool_order( 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( +pub async fn finalize_order( endpoint: &mut Endpoint, data_builder: &F, url: &str, @@ -124,12 +124,12 @@ pub fn finalize_order( where F: Fn(&str, &str) -> Result, { - let response = http::post_jose(endpoint, url, data_builder)?; + let response = http::post_jose(endpoint, url, data_builder).await?; let order = response.json::()?; Ok(order) } -pub fn get_certificate( +pub async fn get_certificate( 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) } diff --git a/acmed/src/http.rs b/acmed/src/http.rs index 7b0c048..39fc81d 100644 --- a/acmed/src/http.rs +++ b/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 { - let (_status, headers, body) = response.split(); - let body = body.text()?; + async fn from_response(response: Response) -> Result { + 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 for HttpError { } } -impl From for HttpError { - fn from(error: attohttpc::Error) -> Self { +impl From 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> { +async fn new_nonce(endpoint: &mut Endpoint) -> Result<(), HttpError> { rate_limit(endpoint); 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()); @@ -138,14 +138,14 @@ fn rate_limit(endpoint: &mut Endpoint) { endpoint.rl.block_until_allowed(); } -fn header_to_string(header_value: &header::HeaderValue) -> Result { +fn header_to_string(header_value: &HeaderValue) -> Result { let s = header_value .to_str() .map_err(|_| Error::from("invalid header format"))?; Ok(s.to_string()) } -fn get_session(root_certs: &[String]) -> Result { +fn get_client(root_certs: &[String]) -> Result { let useragent = format!( "{}/{} ({}) {}", crate::APP_NAME, @@ -154,10 +154,11 @@ fn get_session(root_certs: &[String]) -> Result { 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 { 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 { - let mut session = get_session(&endpoint.root_certificates)?; - session.try_header(header::ACCEPT, CONTENT_TYPE_JSON)?; +pub async fn get(endpoint: &mut Endpoint, url: &str) -> Result { + let client = get_client(&endpoint.root_certificates)?; rate_limit(endpoint); - let response = session.get(url).send()?; + 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( +pub async fn post( endpoint: &mut Endpoint, url: &str, data_builder: &F, @@ -192,25 +198,28 @@ pub fn post( where F: Fn(&str, &str) -> Result, { - 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); 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::()?; 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( +pub async fn post_jose( endpoint: &mut Endpoint, url: &str, data_builder: &F, @@ -238,6 +247,7 @@ where CONTENT_TYPE_JOSE, CONTENT_TYPE_JSON, ) + .await } #[cfg(test)] From eeca233442d3c81a06eeeb5e80babf2fb9b452e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Sat, 8 Apr 2023 23:56:31 +0200 Subject: [PATCH 2/2] Make rate-limits async as well --- acmed/src/endpoint.rs | 10 +++++----- acmed/src/http.rs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/acmed/src/endpoint.rs b/acmed/src/endpoint.rs index a4016c1..5279c7b 100644 --- a/acmed/src/endpoint.rs +++ b/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(); } } diff --git a/acmed/src/http.rs b/acmed/src/http.rs index 39fc81d..8ac6d8a 100644 --- a/acmed/src/http.rs +++ b/acmed/src/http.rs @@ -107,7 +107,7 @@ fn is_nonce(data: &str) -> bool { } async fn new_nonce(endpoint: &mut Endpoint) -> Result<(), HttpError> { - rate_limit(endpoint); + rate_limit(endpoint).await; let url = endpoint.dir.new_nonce.clone(); let _ = get(endpoint, &url).await?; Ok(()) @@ -134,8 +134,8 @@ 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: &HeaderValue) -> Result { @@ -175,7 +175,7 @@ fn get_client(root_certs: &[String]) -> Result { pub async fn get(endpoint: &mut Endpoint, url: &str) -> Result { let client = get_client(&endpoint.root_certificates)?; - rate_limit(endpoint); + rate_limit(endpoint).await; let response = client .get(url) .header(header::ACCEPT, CONTENT_TYPE_JSON) @@ -208,7 +208,7 @@ where 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 = request.body(body).send().await?; update_nonce(endpoint, &response)?;