Skip to content

Commit 7efe89b

Browse files
committed
use reqwest::Proxy to pass proxy config
1 parent 952879d commit 7efe89b

File tree

30 files changed

+90
-104
lines changed

30 files changed

+90
-104
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pixi_build_frontend/src/tool/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ impl Tool {
9898
}
9999

100100
/// Construct a new command that enables invocation of the tool.
101+
/// TODO: whether to inject proxy config
101102
pub fn command(&self) -> std::process::Command {
102103
match self {
103104
Tool::Isolated(tool) => {

crates/pixi_config/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ rattler = { workspace = true }
2121
rattler_conda_types = { workspace = true }
2222
rattler_networking = { workspace = true, features = ["s3"] }
2323
rattler_repodata_gateway = { workspace = true, features = ["gateway"] }
24+
reqwest = { workspace = true }
2425
serde = { workspace = true }
2526
serde_ignored = { workspace = true }
2627
serde_json = { workspace = true }

crates/pixi_config/src/lib.rs

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{
33
path::{Path, PathBuf},
44
process::{Command, Stdio},
55
str::FromStr,
6+
sync::LazyLock,
67
};
78

89
use clap::{ArgAction, Parser};
@@ -15,6 +16,7 @@ use rattler_conda_types::{
1516
};
1617
use rattler_networking::s3_middleware;
1718
use rattler_repodata_gateway::{Gateway, GatewayBuilder, SourceConfig};
19+
use reqwest::{NoProxy, Proxy};
1820
use serde::{de::IntoDeserializer, Deserialize, Serialize};
1921
use url::Url;
2022

@@ -55,6 +57,25 @@ pub fn get_default_author() -> Option<(String, String)> {
5557
Some((name?, email.unwrap_or_else(|| "".into())))
5658
}
5759

60+
// detect proxy env vars like curl: https://curl.se/docs/manpage.html
61+
static ENV_HTTP_PROXY: LazyLock<Option<String>> = LazyLock::new(|| {
62+
["http_proxy", "all_proxy", "ALL_PROXY"]
63+
.iter()
64+
.find_map(|&k| std::env::var(k).ok().filter(|v| !v.is_empty()))
65+
});
66+
static ENV_HTTPS_PROXY: LazyLock<Option<String>> = LazyLock::new(|| {
67+
["https_proxy", "HTTPS_PROXY", "all_proxy", "ALL_PROXY"]
68+
.iter()
69+
.find_map(|&k| std::env::var(k).ok().filter(|v| !v.is_empty()))
70+
});
71+
static ENV_NO_PROXY: LazyLock<Option<String>> = LazyLock::new(|| {
72+
["no_proxy", "NO_PROXY"]
73+
.iter()
74+
.find_map(|&k| std::env::var(k).ok().filter(|v| !v.is_empty()))
75+
});
76+
static USE_PROXY_FROM_ENV: LazyLock<bool> =
77+
LazyLock::new(|| (*ENV_HTTPS_PROXY).is_some() || (*ENV_HTTP_PROXY).is_some());
78+
5879
/// Get pixi home directory, default to `$HOME/.pixi`
5980
///
6081
/// It may be overridden by the `PIXI_HOME` environment variable.
@@ -949,6 +970,23 @@ impl Config {
949970
.validate()
950971
.map_err(|e| ConfigError::ValidationError(e, path.to_path_buf()))?;
951972

973+
// check proxy config
974+
if config.proxy_config.https.is_none() && config.proxy_config.http.is_none() {
975+
if !config.proxy_config.non_proxy_hosts.is_empty() {
976+
tracing::warn!("proxy_config.non_proxy_hosts is not empty but will be ignored")
977+
}
978+
} else if *USE_PROXY_FROM_ENV {
979+
let config_no_proxy = Some(config.proxy_config.non_proxy_hosts.iter().join(","))
980+
.filter(|v| !v.is_empty());
981+
if (*ENV_HTTPS_PROXY).as_deref() != config.proxy_config.https.as_ref().map(Url::as_str)
982+
|| (*ENV_HTTP_PROXY).as_deref()
983+
!= config.proxy_config.http.as_ref().map(Url::as_str)
984+
|| *ENV_NO_PROXY != config_no_proxy
985+
{
986+
tracing::info!("proxy configs are overridden by proxy environment vars.")
987+
}
988+
}
989+
952990
Ok(config)
953991
}
954992

@@ -1208,55 +1246,41 @@ impl Config {
12081246
self.concurrency.downloads
12091247
}
12101248

1211-
pub fn activate_proxy_envs(&self) {
1212-
if self.proxy_config.https.is_none() && self.proxy_config.http.is_none() {
1213-
if !self.proxy_config.non_proxy_hosts.is_empty() {
1214-
tracing::info!("proxy_config.non_proxy_hosts is ignored")
1215-
}
1216-
return;
1249+
pub fn get_proxies(&self) -> reqwest::Result<Vec<Proxy>> {
1250+
if (self.proxy_config.https.is_none() && self.proxy_config.http.is_none())
1251+
|| *USE_PROXY_FROM_ENV
1252+
{
1253+
return Ok(vec![]);
12171254
}
12181255

1219-
// detect proxy env vars like curl: https://curl.se/docs/manpage.html
1220-
let env_https_proxy = ["https_proxy", "HTTPS_PROXY", "all_proxy", "ALL_PROXY"]
1221-
.iter()
1222-
.find_map(|&k| std::env::var(k).ok().filter(|v| !v.is_empty()));
1223-
let env_http_proxy = ["http_proxy", "all_proxy", "ALL_PROXY"]
1224-
.iter()
1225-
.find_map(|&k| std::env::var(k).ok().filter(|v| !v.is_empty()));
1226-
let env_no_proxy = ["no_proxy", "NO_PROXY"]
1227-
.iter()
1228-
.find_map(|&k| std::env::var(k).ok().filter(|v| !v.is_empty()));
1229-
12301256
let config_no_proxy =
12311257
Some(self.proxy_config.non_proxy_hosts.iter().join(",")).filter(|v| !v.is_empty());
12321258

1233-
if env_https_proxy.is_some() || env_http_proxy.is_some() {
1234-
if env_https_proxy.as_deref() != self.proxy_config.https.as_ref().map(Url::as_str)
1235-
|| env_http_proxy.as_deref() != self.proxy_config.http.as_ref().map(Url::as_str)
1236-
|| env_no_proxy != config_no_proxy
1237-
{
1238-
tracing::info!("proxy configs are overridden by proxy environment vars.")
1259+
let mut result: Vec<Proxy> = Vec::new();
1260+
let config_no_proxy: Option<NoProxy> =
1261+
config_no_proxy.as_deref().and_then(NoProxy::from_string);
1262+
1263+
if self.proxy_config.https == self.proxy_config.http {
1264+
result.push(
1265+
Proxy::all(
1266+
self.proxy_config
1267+
.https
1268+
.as_ref()
1269+
.expect("must be some")
1270+
.as_str(),
1271+
)?
1272+
.no_proxy(config_no_proxy),
1273+
);
1274+
} else {
1275+
if let Some(url) = &self.proxy_config.http {
1276+
result.push(Proxy::http(url.as_str())?.no_proxy(config_no_proxy.clone()));
12391277
}
1240-
return;
1241-
}
1242-
1243-
if let Some(url) = &self.proxy_config.https {
1244-
std::env::set_var("https_proxy", url.as_str());
1245-
tracing::debug!("use https proxy: {}", url.as_str());
1246-
}
1247-
if let Some(url) = &self.proxy_config.http {
1248-
std::env::set_var("http_proxy", url.as_str());
1249-
tracing::debug!("use http proxy: {}", url.as_str());
1250-
}
1251-
1252-
if config_no_proxy != env_no_proxy {
1253-
if let Some(no_proxy_str) = &config_no_proxy {
1254-
std::env::set_var("no_proxy", no_proxy_str);
1255-
tracing::debug!("no proxy hosts: {}", no_proxy_str);
1256-
} else {
1257-
std::env::remove_var("no_proxy");
1278+
if let Some(url) = &self.proxy_config.https {
1279+
result.push(Proxy::https(url.as_str())?.no_proxy(config_no_proxy));
12581280
}
12591281
}
1282+
1283+
Ok(result)
12601284
}
12611285

12621286
/// Modify this config with the given key and value

crates/pixi_utils/src/reqwest.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,18 @@ pub fn build_reqwest_clients(
117117
}
118118

119119
let timeout = 5 * 60;
120-
let client = Client::builder()
120+
let mut builder = Client::builder()
121121
.pool_max_idle_per_host(20)
122122
.user_agent(app_user_agent)
123123
.danger_accept_invalid_certs(config.tls_no_verify())
124124
.read_timeout(Duration::from_secs(timeout))
125-
.use_rustls_tls()
126-
.build()
127-
.expect("failed to create reqwest Client");
125+
.use_rustls_tls();
126+
127+
for p in config.get_proxies().into_diagnostic()? {
128+
builder = builder.proxy(p);
129+
}
130+
131+
let client = builder.build().expect("failed to create reqwest Client");
128132

129133
let mut client_builder = ClientBuilder::new(client.clone());
130134

src/cli/add.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
107107
.locate()?
108108
.with_cli_config(args.config.clone());
109109

110-
workspace.activate_proxy_envs();
111-
112110
sanity_check_project(&workspace).await?;
113111

114112
let mut workspace = workspace.modify()?;

src/cli/build.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
8989
.locate()?
9090
.with_cli_config(args.config_cli);
9191

92-
workspace.activate_proxy_envs();
93-
9492
// TODO: Implement logic to take the source code from a VCS instead of from a
9593
// local channel so that that information is also encoded in the manifest.
9694

src/cli/exec.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
6363
let config = Config::with_cli_config(&args.config);
6464
let cache_dir = pixi_config::get_cache_dir().context("failed to determine cache directory")?;
6565

66-
config.activate_proxy_envs();
67-
6866
let mut command_args = args.command.iter();
6967
let command = command_args.next().ok_or_else(|| miette::miette!(help ="i.e when specifying specs explicitly use a command at the end: `pixi exec -s python==3.12 python`", "missing required command to execute",))?;
7068
let (_, client) = build_reqwest_clients(Some(&config), None)?;

src/cli/global/expose.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ pub async fn add(args: AddArgs) -> miette::Result<()> {
7777
.await?
7878
.with_cli_config(config.clone());
7979

80-
project_original.activate_proxy_envs();
81-
8280
async fn apply_changes(
8381
args: &AddArgs,
8482
project: &mut global::Project,

src/cli/global/install.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
9090
.await?
9191
.with_cli_config(config.clone());
9292

93-
project_original.activate_proxy_envs();
94-
9593
let env_names = match &args.environment {
9694
Some(env_name) => Vec::from([env_name.clone()]),
9795
None => args

src/cli/global/remove.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
4747
miette::bail!("Environment {} doesn't exist. You can create a new environment with `pixi global install`.", env_name);
4848
}
4949

50-
project_original.activate_proxy_envs();
51-
5250
async fn apply_changes(
5351
env_name: &EnvironmentName,
5452
specs: &[MatchSpec],

src/cli/global/sync.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
1717
.await?
1818
.with_cli_config(config.clone());
1919

20-
project.activate_proxy_envs();
21-
2220
let mut has_changed = false;
2321

2422
// Prune environments that are not listed

src/cli/global/update.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
2323
.await?
2424
.with_cli_config(config.clone());
2525

26-
project_original.activate_proxy_envs();
27-
2826
async fn apply_changes(
2927
env_name: &EnvironmentName,
3028
project: &mut Project,

src/cli/install.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
4747
.locate()?
4848
.with_cli_config(args.config);
4949

50-
workspace.activate_proxy_envs();
51-
5250
// Install either:
5351
//
5452
// 1. specific environments

src/cli/lock.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
2626
.with_search_start(args.workspace_config.workspace_locator_start())
2727
.locate()?;
2828

29-
workspace.activate_proxy_envs();
30-
3129
// Save the original lockfile to compare with the new one.
3230
let original_lock_file = workspace.load_lock_file().await?;
3331
let new_lock_file = workspace

src/cli/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use clap::builder::styling::{AnsiColor, Color, Style};
22
use clap::Parser;
33
use indicatif::ProgressDrawTarget;
44
use miette::IntoDiagnostic;
5-
use pixi_config::Config;
65
use pixi_consts::consts;
76
use pixi_progress::global_multi_progress;
87
use pixi_utils::indicatif::IndicatifWriter;
@@ -271,10 +270,7 @@ pub async fn execute_command(command: Command) -> miette::Result<()> {
271270
Command::Clean(cmd) => clean::execute(cmd).await,
272271
Command::Run(cmd) => run::execute(cmd).await,
273272
Command::Global(cmd) => global::execute(cmd).await,
274-
Command::Auth(cmd) => {
275-
Config::load_global().activate_proxy_envs();
276-
rattler::cli::auth::execute(cmd).await.into_diagnostic()
277-
}
273+
Command::Auth(cmd) => rattler::cli::auth::execute(cmd).await.into_diagnostic(),
278274
Command::Install(cmd) => install::execute(cmd).await,
279275
Command::Reinstall(cmd) => reinstall::execute(cmd).await,
280276
Command::Shell(cmd) => shell::execute(cmd).await,

src/cli/remove.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
116116
// TODO: update all environments touched by this feature defined.
117117
// updating prefix after removing from toml
118118
if !lock_file_update_config.no_lockfile_update {
119-
workspace.activate_proxy_envs();
120-
121119
get_update_lock_file_and_prefix(
122120
&workspace.default_environment(),
123121
UpdateMode::Revalidate,

src/cli/run.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
102102
.locate()?
103103
.with_cli_config(cli_config);
104104

105-
workspace.activate_proxy_envs();
106-
107105
// Extract the passed in environment name.
108106
let environment = workspace.environment_from_name_or_env_var(args.environment.clone())?;
109107

src/cli/search.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,6 @@ pub async fn execute_impl<W: Write>(
147147

148148
let config = Config::load_global();
149149

150-
project
151-
.map_or(&config, |p| p.config())
152-
.activate_proxy_envs();
153-
154150
// Fetch the all names from the repodata using gateway
155151
let gateway = config.gateway().with_client(client).finish();
156152

src/cli/self_update.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ async fn latest_version() -> miette::Result<Version> {
6060
let url = format!("{}/latest", consts::RELEASES_URL);
6161

6262
// Create a client with a redirect policy
63-
let no_redirect_client = Client::builder()
64-
.redirect(Policy::none()) // Prevent automatic redirects
65-
.build()
66-
.into_diagnostic()?;
63+
let mut no_redirect_client_builder = Client::builder().redirect(Policy::none()); // Prevent automatic redirects
64+
for p in Config::load_global().get_proxies().into_diagnostic()? {
65+
no_redirect_client_builder = no_redirect_client_builder.proxy(p);
66+
}
67+
let no_redirect_client = no_redirect_client_builder.build().into_diagnostic()?;
6768

6869
let version: String = match no_redirect_client
6970
.head(&url)
@@ -111,8 +112,6 @@ async fn latest_version() -> miette::Result<Version> {
111112
}
112113

113114
pub async fn execute(args: Args) -> miette::Result<()> {
114-
Config::load_global().activate_proxy_envs();
115-
116115
// Get the target version, without 'v' prefix
117116
let target_version = match &args.version {
118117
Some(version) => version,
@@ -173,6 +172,7 @@ pub async fn execute(args: Args) -> miette::Result<()> {
173172
// Create a temp file to download the archive
174173
let mut archived_tempfile = tempfile::NamedTempFile::new().into_diagnostic()?;
175174

175+
// TODO proxy inject in https://github.com/prefix-dev/pixi/pull/3346
176176
let client = Client::new();
177177
let mut res = client
178178
.get(&download_url)

src/cli/shell.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
274274
.locate()?
275275
.with_cli_config(config);
276276

277-
workspace.activate_proxy_envs();
278-
279277
let environment = workspace.environment_from_name_or_env_var(args.environment)?;
280278

281279
// Make sure environment is up-to-date, default to install, users can avoid this with frozen or locked.

src/cli/shell_hook.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
153153
.locate()?
154154
.with_cli_config(config);
155155

156-
workspace.activate_proxy_envs();
157-
158156
let environment = workspace.environment_from_name_or_env_var(args.environment)?;
159157

160158
let (lock_file_data, _prefix) = get_update_lock_file_and_prefix(

src/cli/tree.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ pub async fn execute(args: Args) -> miette::Result<()> {
7878
.with_search_start(args.workspace_config.workspace_locator_start())
7979
.locate()?;
8080

81-
workspace.activate_proxy_envs();
82-
8381
let environment = workspace
8482
.environment_from_name_or_env_var(args.environment)
8583
.wrap_err("Environment not found")?;

0 commit comments

Comments
 (0)