From 87facfe44a14a61e94ee50c0a5f64724b065bdd8 Mon Sep 17 00:00:00 2001 From: cin Date: Tue, 1 Aug 2023 00:40:20 +0800 Subject: [PATCH] feat(proxi): support proxy configuration (#283) Fixes: https://github.com/orogene/orogene/issues/223 --- crates/nassun/src/client.rs | 20 +++++++- crates/oro-client/src/client.rs | 80 ++++++++++++++++++++++++++++-- src/apply_args.rs | 9 ++++ src/lib.rs | 32 ++++++++++++ tests/snapshots/help__add.snap | 17 +++++++ tests/snapshots/help__apply.snap | 17 +++++++ tests/snapshots/help__ping.snap | 17 +++++++ tests/snapshots/help__reapply.snap | 17 +++++++ tests/snapshots/help__remove.snap | 17 +++++++ tests/snapshots/help__view.snap | 17 +++++++ 10 files changed, 237 insertions(+), 6 deletions(-) diff --git a/crates/nassun/src/client.rs b/crates/nassun/src/client.rs index 19e082b9..714b23dc 100644 --- a/crates/nassun/src/client.rs +++ b/crates/nassun/src/client.rs @@ -28,6 +28,12 @@ pub struct NassunOpts { default_tag: Option, registries: HashMap, Url>, memoize_metadata: bool, + #[cfg(not(target_arch = "wasm32"))] + proxy: bool, + #[cfg(not(target_arch = "wasm32"))] + proxy_url: Option, + #[cfg(not(target_arch = "wasm32"))] + no_proxy_domain: Option, } impl NassunOpts { @@ -88,7 +94,9 @@ impl NassunOpts { #[cfg(target_arch = "wasm32")] let client_builder = OroClient::builder().registry(registry); #[cfg(not(target_arch = "wasm32"))] - let mut client_builder = OroClient::builder().registry(registry); + let mut client_builder = OroClient::builder() + .registry(registry) + .set_proxy(self.proxy); #[cfg(not(target_arch = "wasm32"))] let cache = if let Some(cache) = self.cache { client_builder = client_builder.cache(cache.clone()); @@ -96,7 +104,15 @@ impl NassunOpts { } else { Arc::new(None) }; - let client = client_builder.build(); + #[cfg(not(target_arch = "wasm32"))] + if let Some(url) = self.proxy_url { + client_builder = client_builder.set_proxy_url(url).unwrap(); + } + #[cfg(not(target_arch = "wasm32"))] + if let Some(url) = self.no_proxy_domain { + client_builder = client_builder.set_no_proxy(url); + } + let client: OroClient = client_builder.build(); Nassun { #[cfg(not(target_arch = "wasm32"))] cache, diff --git a/crates/oro-client/src/client.rs b/crates/oro-client/src/client.rs index 584b7a1c..a919455b 100644 --- a/crates/oro-client/src/client.rs +++ b/crates/oro-client/src/client.rs @@ -8,14 +8,25 @@ use reqwest::Client; #[cfg(not(target_arch = "wasm32"))] use reqwest::ClientBuilder; #[cfg(not(target_arch = "wasm32"))] +use reqwest::{NoProxy, Proxy}; +#[cfg(not(target_arch = "wasm32"))] use reqwest_middleware::ClientWithMiddleware; use url::Url; +#[cfg(not(target_arch = "wasm32"))] +use crate::OroClientError; + #[derive(Clone, Debug)] pub struct OroClientBuilder { registry: Url, #[cfg(not(target_arch = "wasm32"))] cache: Option, + #[cfg(not(target_arch = "wasm32"))] + proxy: bool, + #[cfg(not(target_arch = "wasm32"))] + proxy_url: Option, + #[cfg(not(target_arch = "wasm32"))] + no_proxy_domain: Option, } impl Default for OroClientBuilder { @@ -24,6 +35,12 @@ impl Default for OroClientBuilder { registry: Url::parse("https://registry.npmjs.org").unwrap(), #[cfg(not(target_arch = "wasm32"))] cache: None, + #[cfg(not(target_arch = "wasm32"))] + proxy: false, + #[cfg(not(target_arch = "wasm32"))] + proxy_url: None, + #[cfg(not(target_arch = "wasm32"))] + no_proxy_domain: None, } } } @@ -44,17 +61,61 @@ impl OroClientBuilder { self } + #[cfg(not(target_arch = "wasm32"))] + pub fn set_proxy(mut self, proxy: bool) -> Self { + self.proxy = proxy; + self + } + + #[cfg(not(target_arch = "wasm32"))] + pub fn set_proxy_url(mut self, proxy_url: impl AsRef) -> Result { + match Url::parse(proxy_url.as_ref()) { + Ok(url_info) => { + let username = url_info.username(); + let password = url_info.password(); + let mut proxy = Proxy::all(url_info.as_ref())?; + + if let Some(password_str) = password { + proxy = proxy.basic_auth(username, password_str); + } + + proxy = proxy.no_proxy(self.get_no_proxy()); + self.proxy_url = Some(proxy); + self.proxy = true; + Ok(self) + } + Err(e) => Err(OroClientError::UrlParseError(e)), + } + } + + #[cfg(not(target_arch = "wasm32"))] + pub fn set_no_proxy(mut self, no_proxy_domain: impl AsRef) -> Self { + self.no_proxy_domain = Some(no_proxy_domain.as_ref().into()); + self + } + pub fn build(self) -> OroClient { #[cfg(target_arch = "wasm32")] let client_uncached = Client::new(); #[cfg(not(target_arch = "wasm32"))] - let client_uncached = ClientBuilder::new() + let mut client_core = ClientBuilder::new() .user_agent("orogene") .pool_max_idle_per_host(20) - .timeout(std::time::Duration::from_secs(60 * 5)) - .build() - .expect("Failed to build HTTP client."); + .timeout(std::time::Duration::from_secs(60 * 5)); + + #[cfg(not(target_arch = "wasm32"))] + if let Some(url) = self.proxy_url { + client_core = client_core.proxy(url); + } + + #[cfg(not(target_arch = "wasm32"))] + if !self.proxy { + client_core = client_core.no_proxy(); + } + + #[cfg(not(target_arch = "wasm32"))] + let client_uncached = client_core.build().expect("Fail to build HTTP client."); #[cfg(not(target_arch = "wasm32"))] let mut client_builder = reqwest_middleware::ClientBuilder::new(client_uncached.clone()); @@ -80,6 +141,17 @@ impl OroClientBuilder { client_uncached, } } + + #[cfg(not(target_arch = "wasm32"))] + fn get_no_proxy(&self) -> Option { + if let Some(ref no_proxy_conf) = self.no_proxy_domain { + if !no_proxy_conf.is_empty() { + return NoProxy::from_string(no_proxy_conf); + } + } + + NoProxy::from_env().or(None) + } } #[derive(Clone, Debug)] diff --git a/src/apply_args.rs b/src/apply_args.rs index 9b8a4bac..dce9bb1e 100644 --- a/src/apply_args.rs +++ b/src/apply_args.rs @@ -104,6 +104,15 @@ pub struct ApplyArgs { #[arg(from_global)] pub emoji: bool, + + #[arg(from_global)] + pub proxy: bool, + + #[arg(from_global)] + pub proxy_url: Option, + + #[arg(from_global)] + pub no_proxy: Option, } impl ApplyArgs { diff --git a/src/lib.rs b/src/lib.rs index 6b498b2e..f58a9325 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -256,6 +256,38 @@ pub struct Orogene { #[command(subcommand)] subcommand: OroCmd, + + /// Use proxy to delegate the network. + /// + /// Proxy is opt-in, it uses for outgoing http/https request. + /// If enabled, should set proxy-url too. + #[arg( + help_heading = "Global Options", + global = true, + long, + default_value_t = false + )] + proxy: bool, + + /// A proxy to use for outgoing http requests. + #[arg( + help_heading = "Global Options", + global = true, + long = "proxy-url", + default_value = None + )] + proxy_url: Option, + + /// Use commas to separate multiple entries, e.g. `.host1.com,.host2.com`. + /// + /// Can also be configured through the `NO_PROXY` environment variable, like `NO_PROXY=.host1.com`. + #[arg( + help_heading = "Global Options", + global = true, + long = "no-proxy-domain", + default_value = None + )] + no_proxy_domain: Option, } impl Orogene { diff --git a/tests/snapshots/help__add.snap b/tests/snapshots/help__add.snap index 8faeb39d..0200fd8c 100644 --- a/tests/snapshots/help__add.snap +++ b/tests/snapshots/help__add.snap @@ -1,5 +1,6 @@ --- source: tests/help.rs +assertion_line: 7 expression: "sub_md(\"add\")" --- stderr: @@ -191,4 +192,20 @@ Telemetry for Orogene is opt-in, anonymous, and is used to help the team improve Sentry DSN (access token) where telemetry will be sent (if enabled) +#### `--proxy` + +Use proxy to delegate the network. + +Proxy is opt-in, it uses for outgoing http/https request. If enabled, should set proxy-url too. + +#### `--proxy-url ` + +A proxy to use for outgoing http requests + +#### `--no-proxy-domain ` + +Use commas to separate multiple entries, e.g. `.host1.com,.host2.com`. + +Can also be configured through the `NO_PROXY` environment variable, like `NO_PROXY=.host1.com`. + diff --git a/tests/snapshots/help__apply.snap b/tests/snapshots/help__apply.snap index 0d4ef125..7743b7aa 100644 --- a/tests/snapshots/help__apply.snap +++ b/tests/snapshots/help__apply.snap @@ -1,5 +1,6 @@ --- source: tests/help.rs +assertion_line: 12 expression: "sub_md(\"apply\")" --- stderr: @@ -171,4 +172,20 @@ Telemetry for Orogene is opt-in, anonymous, and is used to help the team improve Sentry DSN (access token) where telemetry will be sent (if enabled) +#### `--proxy` + +Use proxy to delegate the network. + +Proxy is opt-in, it uses for outgoing http/https request. If enabled, should set proxy-url too. + +#### `--proxy-url ` + +A proxy to use for outgoing http requests + +#### `--no-proxy-domain ` + +Use commas to separate multiple entries, e.g. `.host1.com,.host2.com`. + +Can also be configured through the `NO_PROXY` environment variable, like `NO_PROXY=.host1.com`. + diff --git a/tests/snapshots/help__ping.snap b/tests/snapshots/help__ping.snap index 641d3046..30827ef0 100644 --- a/tests/snapshots/help__ping.snap +++ b/tests/snapshots/help__ping.snap @@ -1,5 +1,6 @@ --- source: tests/help.rs +assertion_line: 17 expression: "sub_md(\"ping\")" --- stderr: @@ -105,4 +106,20 @@ Telemetry for Orogene is opt-in, anonymous, and is used to help the team improve Sentry DSN (access token) where telemetry will be sent (if enabled) +#### `--proxy` + +Use proxy to delegate the network. + +Proxy is opt-in, it uses for outgoing http/https request. If enabled, should set proxy-url too. + +#### `--proxy-url ` + +A proxy to use for outgoing http requests + +#### `--no-proxy-domain ` + +Use commas to separate multiple entries, e.g. `.host1.com,.host2.com`. + +Can also be configured through the `NO_PROXY` environment variable, like `NO_PROXY=.host1.com`. + diff --git a/tests/snapshots/help__reapply.snap b/tests/snapshots/help__reapply.snap index 5b880c93..7ecdab1f 100644 --- a/tests/snapshots/help__reapply.snap +++ b/tests/snapshots/help__reapply.snap @@ -1,5 +1,6 @@ --- source: tests/help.rs +assertion_line: 22 expression: "sub_md(\"reapply\")" --- stderr: @@ -167,4 +168,20 @@ Telemetry for Orogene is opt-in, anonymous, and is used to help the team improve Sentry DSN (access token) where telemetry will be sent (if enabled) +#### `--proxy` + +Use proxy to delegate the network. + +Proxy is opt-in, it uses for outgoing http/https request. If enabled, should set proxy-url too. + +#### `--proxy-url ` + +A proxy to use for outgoing http requests + +#### `--no-proxy-domain ` + +Use commas to separate multiple entries, e.g. `.host1.com,.host2.com`. + +Can also be configured through the `NO_PROXY` environment variable, like `NO_PROXY=.host1.com`. + diff --git a/tests/snapshots/help__remove.snap b/tests/snapshots/help__remove.snap index 320d4a42..412034a5 100644 --- a/tests/snapshots/help__remove.snap +++ b/tests/snapshots/help__remove.snap @@ -1,5 +1,6 @@ --- source: tests/help.rs +assertion_line: 27 expression: "sub_md(\"remove\")" --- stderr: @@ -175,4 +176,20 @@ Telemetry for Orogene is opt-in, anonymous, and is used to help the team improve Sentry DSN (access token) where telemetry will be sent (if enabled) +#### `--proxy` + +Use proxy to delegate the network. + +Proxy is opt-in, it uses for outgoing http/https request. If enabled, should set proxy-url too. + +#### `--proxy-url ` + +A proxy to use for outgoing http requests + +#### `--no-proxy-domain ` + +Use commas to separate multiple entries, e.g. `.host1.com,.host2.com`. + +Can also be configured through the `NO_PROXY` environment variable, like `NO_PROXY=.host1.com`. + diff --git a/tests/snapshots/help__view.snap b/tests/snapshots/help__view.snap index 319aa50d..475a3b0d 100644 --- a/tests/snapshots/help__view.snap +++ b/tests/snapshots/help__view.snap @@ -1,5 +1,6 @@ --- source: tests/help.rs +assertion_line: 32 expression: "sub_md(\"view\")" --- stderr: @@ -119,4 +120,20 @@ Telemetry for Orogene is opt-in, anonymous, and is used to help the team improve Sentry DSN (access token) where telemetry will be sent (if enabled) +#### `--proxy` + +Use proxy to delegate the network. + +Proxy is opt-in, it uses for outgoing http/https request. If enabled, should set proxy-url too. + +#### `--proxy-url ` + +A proxy to use for outgoing http requests + +#### `--no-proxy-domain ` + +Use commas to separate multiple entries, e.g. `.host1.com,.host2.com`. + +Can also be configured through the `NO_PROXY` environment variable, like `NO_PROXY=.host1.com`. +