diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dacfa2..e47104f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,22 @@ # Changelog +## 3.0.0 + +* Update `hyper` dependency to `1.0`. This is considered a breaking change because we expose the ability to + construct your own `AsyncRobot` using a custom `hyper::Client`, which was [removed in hyper v1.0](https://hyper.rs/guides/1/upgrading/). + The *Client* functionality is still available in the spin-off crate [hyper-util](https://github.com/hyperium/hyper-util), + which is also what `hrobot-rs` uses now. +* Switch to using the rustls built-in webpki roots by default, instead of native roots. This is potentially a breaking change, + but in all likelihood, this won't impact you unless you're behind an intercepting firewall man-in-the-middling your traffic. + If you need to override this behaviour, see [AsyncRobot::new](https://docs.rs/hrobot/3.0.0/hrobot/struct.AsyncRobot.html#method.new) + for information about providing your own customized hyper client. +* Update `serial_test` dependency to v3.0.0 + ## 2.0.0 * Replace Decimal export with rust_decimal re-export. * Replace ByteSize export with bytesize re-export. -* Fix doc and tes references to the above exports. +* Fix doc and test references to the above exports. * Remove explicit crate links where unnecessary. ## 1.1.0 diff --git a/Cargo.toml b/Cargo.toml index bce2224..899eac0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,10 @@ serde_html_form = "0.2.2" base64 = "0.21.2" urlencoding = "2" -hyper = { version = "0.14.27", features = ["http1", "client"] } -hyper-rustls = { version = "0.24.1" } +hyper = { version = "1.2.0", features = ["http1", "client"] } +hyper-rustls = { version = "0.26.0", default-features = false, features = ["http1", "webpki-tokio", "ring" ] } +hyper-util = { version = "0.1.3", features = ["client", "client-legacy", "http1" ] } +http-body-util = "0.1.0" [dev-dependencies] tracing-subscriber = "0.3.17" @@ -35,11 +37,11 @@ tracing-test = { version = "0.2.4" } tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] } dotenvy = "0.15" -serial_test = "2" +serial_test = "3.0.0" [features] -# Enabled non-disruptive tests and mus be provided as a selected feature, -# if you want to test endpoints lik get/list, which do not have side-effects. +# Enables non-disruptive tests and must be provided as a selected feature, +# if you want to test endpoints like get/list, which do not have side-effects. non-disruptive-tests = [] # Enables disruptive tests. These are tests which modify the current state # of RUNNING servers storageboxes, firewalls, etc., and MAY leave them in an diff --git a/src/api/boot/cpanel.rs b/src/api/boot/cpanel.rs index d5b4fe7..faef94d 100644 --- a/src/api/boot/cpanel.rs +++ b/src/api/boot/cpanel.rs @@ -239,7 +239,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_get_cpanel_configuration() { let _ = dotenvy::dotenv().ok(); @@ -265,7 +265,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_last_cpanel_config() { let _ = dotenvy::dotenv().ok(); @@ -301,7 +301,7 @@ mod tests { #[tokio::test] #[ignore = "enabling the Cpanel installation system is expensive, even if the system is never activated."] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_enable_disable_cpanel() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/boot/linux.rs b/src/api/boot/linux.rs index 18bd74e..6ab6b4f 100644 --- a/src/api/boot/linux.rs +++ b/src/api/boot/linux.rs @@ -255,7 +255,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_get_linux_configuration() { let _ = dotenvy::dotenv().ok(); @@ -282,7 +282,7 @@ mod tests { #[tokio::test] #[traced_test] #[ignore = "unexpected failure might leave the linux installation system enabled."] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_enable_disable_linux() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/boot/mod.rs b/src/api/boot/mod.rs index df4bb96..cc34fb9 100644 --- a/src/api/boot/mod.rs +++ b/src/api/boot/mod.rs @@ -129,7 +129,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_get_boot_configuration() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/boot/plesk.rs b/src/api/boot/plesk.rs index 601b633..2e3719b 100644 --- a/src/api/boot/plesk.rs +++ b/src/api/boot/plesk.rs @@ -237,7 +237,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_get_plesk_configuration() { let _ = dotenvy::dotenv().ok(); @@ -263,7 +263,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_last_plesk_config() { let _ = dotenvy::dotenv().ok(); @@ -296,7 +296,7 @@ mod tests { #[tokio::test] #[traced_test] #[ignore = "enabling the Plesk installation system is expensive, even if the system is never activated."] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_enable_disable_plesk() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/boot/rescue.rs b/src/api/boot/rescue.rs index abc34af..3c58c76 100644 --- a/src/api/boot/rescue.rs +++ b/src/api/boot/rescue.rs @@ -279,7 +279,7 @@ mod isolated_tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_get_rescue_configuration() { let _ = dotenvy::dotenv().ok(); @@ -306,7 +306,7 @@ mod isolated_tests { #[tokio::test] #[ignore = "unexpected failure might leave the rescue system enabled."] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_enable_disable_vkvm() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/boot/vnc.rs b/src/api/boot/vnc.rs index 549a040..8808fc0 100644 --- a/src/api/boot/vnc.rs +++ b/src/api/boot/vnc.rs @@ -225,7 +225,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_get_vnc_configuration() { let _ = dotenvy::dotenv().ok(); @@ -242,7 +242,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_last_vnc_config() { let _ = dotenvy::dotenv().ok(); @@ -270,7 +270,7 @@ mod tests { #[tokio::test] #[ignore = "unexpected failure might leave the vnc installation system enabled."] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_enable_disable_vnc() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/boot/windows.rs b/src/api/boot/windows.rs index 88665a7..e992794 100644 --- a/src/api/boot/windows.rs +++ b/src/api/boot/windows.rs @@ -225,7 +225,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_get_windows_configuration() { let _ = dotenvy::dotenv().ok(); @@ -251,7 +251,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_last_windows_config() { let _ = dotenvy::dotenv().ok(); @@ -285,7 +285,7 @@ mod tests { #[tokio::test] #[ignore = "enabling the Windows installation system is expensive, even if the system is never activated."] #[traced_test] - #[serial("boot-configuration")] + #[serial(boot_configuration)] async fn test_enable_disable_windows() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/firewall/mod.rs b/src/api/firewall/mod.rs index 2f42a65..c4e08d6 100644 --- a/src/api/firewall/mod.rs +++ b/src/api/firewall/mod.rs @@ -361,7 +361,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("firewall")] + #[serial(firewall)] async fn test_get_firewall() { let _ = dotenvy::dotenv().ok(); @@ -379,7 +379,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("firewall-templates")] + #[serial(firewall_templates)] async fn test_list_firewall_templates() { let _ = dotenvy::dotenv().ok(); @@ -391,7 +391,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("firewall-templates")] + #[serial(firewall_templates)] async fn test_get_firewall_template() { let _ = dotenvy::dotenv().ok(); @@ -417,7 +417,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("firewall")] + #[serial(firewall)] #[ignore = "unexpected failure might leave firewall in modified state."] async fn test_set_firewall_configuration() { let _ = dotenvy::dotenv().ok(); @@ -476,7 +476,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("firewall")] + #[serial(firewall)] #[ignore = "unexpected failure might leave firewall in modified state."] async fn test_apply_firewall_template() { let _ = dotenvy::dotenv().ok(); @@ -545,7 +545,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("firewall")] + #[serial(firewall)] #[ignore = "removing a production server's firewall, even temporarily, is obviously always *very* dangerous."] async fn test_delete_firewall() { let _ = dotenvy::dotenv().ok(); @@ -597,7 +597,7 @@ mod tests { #[tokio::test] #[ignore = "unexpected failure could leave template behind."] #[traced_test] - #[serial("firewall-templates")] + #[serial(firewall_templates)] async fn test_create_update_delete_firewall_template() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/ip.rs b/src/api/ip.rs index 8f04e5c..9d256e2 100644 --- a/src/api/ip.rs +++ b/src/api/ip.rs @@ -493,7 +493,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("ip")] + #[serial(ip)] #[ignore = "unexpected failure can leave the traffic warning in undesired configuration"] async fn test_enable_and_disable_traffic_warnings() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/keys.rs b/src/api/keys.rs index 1e49c9d..1dbdede 100644 --- a/src/api/keys.rs +++ b/src/api/keys.rs @@ -239,7 +239,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("ssh-keys")] + #[serial(ssh_keys)] #[ignore = "unexpected failure might leave test key behind."] async fn test_create_delete_key() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/storagebox/mod.rs b/src/api/storagebox/mod.rs index c17f6f4..2eaaf2f 100644 --- a/src/api/storagebox/mod.rs +++ b/src/api/storagebox/mod.rs @@ -883,7 +883,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("storagebox")] + #[serial(storagebox)] async fn test_get_storagebox() { let _ = dotenvy::dotenv().ok(); @@ -900,7 +900,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("storagebox")] + #[serial(storagebox)] async fn test_list_snapshots() { let _ = dotenvy::dotenv().ok(); @@ -962,7 +962,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("storagebox")] + #[serial(storagebox)] #[ignore = "resets password, potentially breaking existing pasword-based clients"] async fn test_reset_password() { let _ = dotenvy::dotenv().ok(); @@ -983,7 +983,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("storagebox")] + #[serial(storagebox)] #[ignore = "messes up enabled/disabled services for the storagebox, potentially leaving it in an unsafe state"] async fn test_toggle_all_settings() { let _ = dotenvy::dotenv().ok(); @@ -1072,7 +1072,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("storagebox")] + #[serial(storagebox)] #[ignore = "creating, reverting and deleting snapshots could lead to data loss"] async fn test_create_revert_delete_snapshot() { let _ = dotenvy::dotenv().ok(); @@ -1105,7 +1105,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("storagebox")] + #[serial(storagebox)] #[ignore = "creating and deleting snapshots could lead to data loss"] async fn test_create_comment_delete_snapshot() { let _ = dotenvy::dotenv().ok(); @@ -1142,7 +1142,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("storagebox")] + #[serial(storagebox)] #[ignore = "replaces the snapshot plan of the storage box."] async fn test_update_snapshot_plans() { let _ = dotenvy::dotenv().ok(); @@ -1195,7 +1195,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("storagebox")] + #[serial(storagebox)] #[ignore = "creates new subaccounts with read/write permissions"] async fn test_create_update_delete_subaccount() { let _ = dotenvy::dotenv().ok(); diff --git a/src/api/vswitch.rs b/src/api/vswitch.rs index 054559e..09b1e32 100644 --- a/src/api/vswitch.rs +++ b/src/api/vswitch.rs @@ -500,7 +500,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("vswitch")] + #[serial(vswitch)] async fn test_list_vswitches() { let _ = dotenvy::dotenv().ok(); @@ -512,7 +512,7 @@ mod tests { #[tokio::test] #[traced_test] - #[serial("vswitch")] + #[serial(vswitch)] async fn test_get_vswitch() { let _ = dotenvy::dotenv().ok(); @@ -539,7 +539,7 @@ mod tests { #[tokio::test] #[traced_test] #[ignore = "modifies vswitch connectivity of servers"] - #[serial("vswitch")] + #[serial(vswitch)] async fn test_vswitch_end_to_end() { let _ = dotenvy::dotenv().ok(); @@ -603,7 +603,7 @@ mod tests { #[tokio::test] #[traced_test] #[ignore = "modifies vswitch connectivity of servers"] - #[serial("vswitch")] + #[serial(vswitch)] async fn test_connect_disconnect_multiple() { let _ = dotenvy::dotenv().ok(); diff --git a/src/client.rs b/src/client.rs index c586acb..07d8f0f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,6 +1,11 @@ mod r#async { - use hyper::{client::HttpConnector, Body}; + use http_body_util::{BodyExt, Full}; + use hyper::body::Bytes; use hyper_rustls::HttpsConnector; + use hyper_util::{ + client::legacy::{connect::HttpConnector, Client}, + rt::TokioExecutor, + }; use serde::de::DeserializeOwned; use tracing::trace; @@ -31,17 +36,17 @@ mod r#async { #[derive(Debug, Clone)] pub struct AsyncRobot { credentials: Credentials, - client: hyper::Client, Body>, + client: Client, Full>, } impl Default for AsyncRobot { fn default() -> Self { - let https = hyper_rustls::HttpsConnectorBuilder::new() - .with_native_roots() + let https: HttpsConnector = hyper_rustls::HttpsConnectorBuilder::new() + .with_webpki_roots() .https_only() .enable_http1() .build(); - let client = hyper::Client::builder().build(https); + let client = Client::builder(TokioExecutor::new()).build(https); Self::from_env(client).unwrap() } @@ -53,23 +58,25 @@ mod r#async { /// and the given client. /// /// # Example - /// Construct an [`AsyncRobot`] using a [`hyper::Client`] and [`hyper_rustls`]. + /// Construct an [`AsyncRobot`] using a [`hyper_util::client::legacy::Client`] and [`hyper_rustls`]. /// ```rust /// # #[tokio::main] /// # async fn main() { /// let https = hyper_rustls::HttpsConnectorBuilder::new() - /// .with_native_roots() + /// .with_webpki_roots() /// .https_only() /// .enable_http1() /// .build(); /// - /// let client = hyper::Client::builder().build(https); + /// let client = hyper_util::client::legacy::Client::builder( + /// hyper_util::rt::TokioExecutor::new() + /// ).build(https); /// /// let robot = hrobot::AsyncRobot::from_env(client); /// # } /// ``` pub fn from_env( - client: hyper::Client, Body>, + client: Client, Full>, ) -> Result { Ok(Self::new( client, @@ -81,23 +88,25 @@ mod r#async { /// Construct a new [`AsyncRobot`], using the given client, username and password. /// /// # Example - /// Construct an [`AsyncRobot`] using a custom [`hyper::Client`]. + /// Construct an [`AsyncRobot`] using a custom [`hyper_util::client::legacy::Client`]. /// ```rust /// # #[tokio::main] /// # async fn main() { /// let https = hyper_rustls::HttpsConnectorBuilder::new() - /// .with_native_roots() + /// .with_webpki_roots() /// .https_only() /// .enable_http1() /// .build(); /// - /// let client = hyper::Client::builder().build(https); + /// let client = hyper_util::client::legacy::Client::builder( + /// hyper_util::rt::TokioExecutor::new() + /// ).build(https); /// /// let robot = hrobot::AsyncRobot::new(client, "#ws+username", "p@ssw0rd"); /// # } /// ``` pub fn new( - client: hyper::Client, Body>, + client: Client, Full>, username: &str, password: &str, ) -> Self { @@ -118,8 +127,8 @@ mod r#async { let authenticated_request = request.authenticate(&self.credentials); let body = match authenticated_request.body() { - None => Body::empty(), - Some(value) => Body::from(value.to_owned()), + None => Full::default(), + Some(value) => Full::from(value.to_owned()), }; let request = hyper::Request::builder() @@ -140,9 +149,12 @@ mod r#async { .await .map_err(Error::transport)?; - let body = hyper::body::to_bytes(response.into_body()) + let body = response + .into_body() + .collect() .await - .map_err(Error::transport)?; + .map_err(Error::transport)? + .to_bytes(); let stringified = String::from_utf8_lossy(&body); trace!("response body: {stringified}");