diff --git a/Cargo.lock b/Cargo.lock index 833bfc8403..cc03e5c4a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,36 +477,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "curl" -version = "0.4.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe 0.1.6", - "openssl-sys", - "schannel", - "socket2", - "windows-sys 0.59.0", -] - -[[package]] -name = "curl-sys" -version = "0.4.85+curl-8.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0efa6142b5ecc05f6d3eaa39e6af4888b9d3939273fb592c92b7088a8cf3fdb" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "windows-sys 0.59.0", -] - [[package]] name = "cvt" version = "0.1.2" @@ -1382,7 +1352,7 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe 0.2.1", + "openssl-probe", "openssl-sys", "schannel", "security-framework", @@ -1500,12 +1470,6 @@ dependencies = [ "syn", ] -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - [[package]] name = "openssl-probe" version = "0.2.1" @@ -2096,7 +2060,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe 0.2.1", + "openssl-probe", "rustls-pki-types", "schannel", "security-framework", @@ -2164,7 +2128,6 @@ dependencies = [ "clap-cargo", "clap_complete", "console", - "curl", "effective-limits", "enum-map", "env_proxy", diff --git a/Cargo.toml b/Cargo.toml index feea8b88c3..abbfd39fb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,7 @@ repository = "https://github.com/rust-lang/rustup" build = "build.rs" [features] -curl-backend = ["dep:curl"] -default = ["curl-backend", "reqwest-native-tls", "reqwest-rustls-tls"] +default = ["reqwest-native-tls", "reqwest-rustls-tls"] vendored-openssl = ['openssl/vendored'] @@ -50,7 +49,6 @@ clap = { version = "4", features = ["derive", "wrap_help", "string"] } clap-cargo = "0.18.3" clap_complete = "4" console = "0.16" -curl = { version = "0.4.44", optional = true } effective-limits = "0.5.5" enum-map = "2.5.0" env_proxy = { version = "0.4.1", optional = true } @@ -63,8 +61,9 @@ indicatif = "0.18" itertools = "0.14" libc = "0.2" opener = "0.8.0" -# `openssl` is used by `curl` or `reqwest` backend although it isn't imported by rustup: this -# allows controlling the vendoring status without exposing the presence of the download crate. +# `openssl` is used by the `reqwest` backend although it isn't imported by +# rustup: this allows controlling the vendoring status without exposing the +# presence of the download crate. openssl = { version = "0.10", optional = true } # HACK: Temporarily pinned due to ppc64 ELFv1/v2 ABI issue in 300.5.5, to be # removed when lands. diff --git a/build.rs b/build.rs index 7012d6119c..e6e6a69197 100644 --- a/build.rs +++ b/build.rs @@ -66,7 +66,7 @@ fn main() { // This will work on all supported Windows versions but it relies on // us using `SetDefaultDllDirectories` before any libraries are loaded. // See also: src/bin/rustup-init.rs - let delay_load_dlls = ["bcrypt", "api-ms-win-core-synch-l1-2-0"]; + let delay_load_dlls = ["api-ms-win-core-synch-l1-2-0"]; for dll in delay_load_dlls { println!("cargo::rustc-link-arg-bin=rustup-init=/delayload:{dll}.dll"); } diff --git a/ci/run.bash b/ci/run.bash index f9d28cd6e6..3c140c44b9 100644 --- a/ci/run.bash +++ b/ci/run.bash @@ -19,8 +19,7 @@ if [ -n "$INSTALL_BINDGEN" ]; then export PATH="$CARGO_HOME/bin/bindgen-cli:$PATH" fi - -FEATURES=('--no-default-features' '--features' 'curl-backend,reqwest-native-tls') +FEATURES=('--no-default-features' '--features' 'reqwest-native-tls') case "$(uname -s)" in *NT* ) ;; # Windows NT * ) FEATURES+=('--features' 'vendored-openssl') ;; @@ -60,7 +59,7 @@ build_test() { cmd="$1" shift - features=('--features' 'curl-backend,reqwest-native-tls') + features=('--features' 'reqwest-native-tls') case "$TARGET" in # these platforms aren't supported by aws-lc-rs: powerpc* ) ;; diff --git a/doc/user-guide/src/network-proxies.md b/doc/user-guide/src/network-proxies.md index 19a42090b8..e707c7813a 100644 --- a/doc/user-guide/src/network-proxies.md +++ b/doc/user-guide/src/network-proxies.md @@ -6,38 +6,25 @@ uses a proxy by setting its URL in the environment. In most cases, setting `https_proxy` should be sufficient. Commands may differ between different systems and shells: - - On a Unix-like system with a shell like __bash__ or __zsh__: - ```bash - export https_proxy=socks5://proxy.example.com:1080 - ``` - - On Windows [__Command Prompt (cmd)__][cmd]: - ```cmd - set https_proxy=socks5://proxy.example.com:1080 - ``` - - On Windows [__PowerShell__][ps] (or __PowerShell Core__): - ```cmd - $env:https_proxy="socks5://proxy.example.com:1080" - ``` - - Replace `socks5://proxy.example.com:1080` with +- On a Unix-like system with a shell like **bash** or **zsh**: + ```bash + export https_proxy=socks5://proxy.example.com:1080 + ``` +- On Windows [**Command Prompt (cmd)**][cmd]: + ```cmd + set https_proxy=socks5://proxy.example.com:1080 + ``` +- On Windows [**PowerShell**][ps] (or **PowerShell Core**): + ```cmd + $env:https_proxy="socks5://proxy.example.com:1080" + ``` +- Replace `socks5://proxy.example.com:1080` with `http://proxy.example.com:8080` when an HTTP proxy is used instead. If you need a more complex setup, `rustup` supports the convention used by the -__curl__ program, documented in the ENVIRONMENT section of [its manual +**curl** program, documented in the ENVIRONMENT section of [its manual page][curlman]. -The use of `curl` is presently **deprecated**, however it can still be used by -providing the `RUSTUP_USE_CURL` environment variable, for example: - -```bash -RUSTUP_USE_CURL=1 rustup update -``` - -Note that some versions of `libcurl` apparently require you to drop the -`http://` or `https://` prefix in environment variables. For example, `export -http_proxy=proxy.example.com:1080` (and likewise for HTTPS). If you are -getting an SSL `unknown protocol` error from `rustup` via `libcurl` but the -command-line `curl` command works fine, this may be the problem. - [curlman]: https://curl.se/docs/manpage.html#:~:text=Environment,-The%20environment%20variables [cmd]: https://en.wikipedia.org/wiki/Cmd.exe [ps]: https://en.wikipedia.org/wiki/PowerShell diff --git a/src/download/mod.rs b/src/download/mod.rs index 31f7c83624..d930d0f998 100644 --- a/src/download/mod.rs +++ b/src/download/mod.rs @@ -8,7 +8,6 @@ use std::time::Duration; use anyhow::Context; #[cfg(any( - not(feature = "curl-backend"), not(feature = "reqwest-rustls-tls"), not(feature = "reqwest-native-tls") ))] @@ -16,8 +15,6 @@ use anyhow::anyhow; use sha2::Sha256; use thiserror::Error; use tracing::debug; -#[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] -use tracing::info; use tracing::warn; use url::Url; @@ -93,7 +90,7 @@ async fn download_file_( process: &Process, ) -> anyhow::Result<()> { #[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] - use crate::download::{Backend, Event, TlsBackend}; + use crate::download::{Backend, Event}; use sha2::Digest; use std::cell::RefCell; @@ -128,15 +125,6 @@ async fn download_file_( // Download the file - // Keep the curl env var around for a bit - let use_curl_backend = process.var_os("RUSTUP_USE_CURL").map(|it| it != "0"); - if use_curl_backend == Some(true) { - warn!( - "RUSTUP_USE_CURL is set; the curl backend is deprecated, - please file an issue if the default download backend does not work for your use case" - ); - } - let use_rustls = process.var_os("RUSTUP_USE_RUSTLS").map(|it| it != "0"); if use_rustls == Some(false) { warn!( @@ -145,58 +133,32 @@ async fn download_file_( ); } - let backend = match (use_curl_backend, use_rustls) { - // If environment specifies a backend that's unavailable, error out + let backend = match use_rustls { + // If the environment explicitly selects a TLS backend that's unavailable, error out. #[cfg(not(feature = "reqwest-rustls-tls"))] - (_, Some(true)) => { + Some(true) => { return Err(anyhow!( "RUSTUP_USE_RUSTLS is set, but this rustup distribution was not built with the reqwest-rustls-tls feature" )); } #[cfg(not(feature = "reqwest-native-tls"))] - (_, Some(false)) => { + Some(false) => { return Err(anyhow!( "RUSTUP_USE_RUSTLS is set to false, but this rustup distribution was not built with the reqwest-native-tls feature" )); } - #[cfg(not(feature = "curl-backend"))] - (Some(true), _) => { - return Err(anyhow!( - "RUSTUP_USE_CURL is set, but this rustup distribution was not built with the curl-backend feature" - )); - } - // Positive selections, from least preferred to most preferred - #[cfg(feature = "curl-backend")] - (Some(true), None) => Backend::Curl, + // Prefer explicit selections before falling back to the default TLS stack. #[cfg(feature = "reqwest-native-tls")] - (_, Some(false)) => { - if use_curl_backend == Some(true) { - info!( - "RUSTUP_USE_CURL is set and RUSTUP_USE_RUSTLS is set to off, using reqwest with native-tls" - ); - } - Backend::Reqwest(TlsBackend::NativeTls) - } + Some(false) => Backend::NativeTls, + + // The default fallback is `rustls`, which should be used whenever available. #[cfg(feature = "reqwest-rustls-tls")] - _ => { - if use_curl_backend == Some(true) { - info!( - "both RUSTUP_USE_CURL and RUSTUP_USE_RUSTLS are set, using reqwest with rustls" - ); - } - Backend::Reqwest(TlsBackend::Rustls) - } + _ => Backend::Rustls, - // Falling back if only one backend is available + // The `rustls` feature is disabled, fall back to `native-tls` instead. #[cfg(all(not(feature = "reqwest-rustls-tls"), feature = "reqwest-native-tls"))] - _ => Backend::Reqwest(TlsBackend::NativeTls), - #[cfg(all( - not(feature = "reqwest-rustls-tls"), - not(feature = "reqwest-native-tls"), - feature = "curl-backend" - ))] - _ => Backend::Curl, + _ => Backend::NativeTls, }; let timeout = Duration::from_secs(match process.var("RUSTUP_DOWNLOAD_TIMEOUT") { @@ -208,12 +170,7 @@ async fn download_file_( Err(_) => 180, }); - match backend { - #[cfg(feature = "curl-backend")] - Backend::Curl => debug!("downloading with curl"), - #[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] - Backend::Reqwest(_) => debug!("downloading with reqwest"), - }; + debug!("downloading with reqwest"); let res = backend .download_to_path(url, path, resume_from_partial, Some(callback), timeout) @@ -232,9 +189,6 @@ async fn download_file_( /// User agent header value for HTTP request. /// See: https://github.com/rust-lang/rustup/issues/2860. -#[cfg(feature = "curl-backend")] -const CURL_USER_AGENT: &str = concat!("rustup/", env!("CARGO_PKG_VERSION"), " (curl)"); - #[cfg(feature = "reqwest-native-tls")] const REQWEST_DEFAULT_TLS_USER_AGENT: &str = concat!( "rustup/", @@ -248,10 +202,10 @@ const REQWEST_RUSTLS_TLS_USER_AGENT: &str = #[derive(Debug, Copy, Clone)] enum Backend { - #[cfg(feature = "curl-backend")] - Curl, - #[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] - Reqwest(TlsBackend), + #[cfg(feature = "reqwest-rustls-tls")] + Rustls, + #[cfg(feature = "reqwest-native-tls")] + NativeTls, } impl Backend { @@ -372,7 +326,6 @@ impl Backend { #[cfg_attr( all( - not(feature = "curl-backend"), not(feature = "reqwest-rustls-tls"), not(feature = "reqwest-native-tls") ), @@ -384,33 +337,6 @@ impl Backend { resume_from: u64, timeout: Duration, callback: DownloadCallback<'_>, - ) -> anyhow::Result<()> { - match self { - #[cfg(feature = "curl-backend")] - Self::Curl => curl::download(url, resume_from, callback, timeout), - #[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] - Self::Reqwest(tls) => tls.download(url, resume_from, callback, timeout).await, - } - } -} - -#[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] -#[derive(Debug, Copy, Clone)] -enum TlsBackend { - #[cfg(feature = "reqwest-rustls-tls")] - Rustls, - #[cfg(feature = "reqwest-native-tls")] - NativeTls, -} - -#[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] -impl TlsBackend { - async fn download( - self, - url: &Url, - resume_from: u64, - callback: DownloadCallback<'_>, - timeout: Duration, ) -> anyhow::Result<()> { let client = match self { #[cfg(feature = "reqwest-rustls-tls")] @@ -434,124 +360,6 @@ enum Event<'a> { type DownloadCallback<'a> = &'a dyn Fn(Event<'_>) -> anyhow::Result<()>; -/// Download via libcurl; encrypt with the native (or OpenSSl) TLS -/// stack via libcurl -#[cfg(feature = "curl-backend")] -mod curl { - use std::cell::RefCell; - use std::str; - use std::time::Duration; - - use anyhow::{Context, Result}; - use curl::easy::Easy; - use url::Url; - - use super::{DownloadError, Event}; - - pub(super) fn download( - url: &Url, - resume_from: u64, - callback: &dyn Fn(Event<'_>) -> Result<()>, - timeout: Duration, - ) -> Result<()> { - // Fetch either a cached libcurl handle (which will preserve open - // connections) or create a new one if it isn't listed. - // - // Once we've acquired it, reset the lifetime from 'static to our local - // scope. - thread_local!(static EASY: RefCell = RefCell::new(Easy::new())); - EASY.with(|handle| { - let mut handle = handle.borrow_mut(); - - handle.url(url.as_ref())?; - handle.follow_location(true)?; - handle.useragent(super::CURL_USER_AGENT)?; - - if resume_from > 0 { - handle.resume_from(resume_from)?; - } else { - // an error here indicates that the range header isn't supported by underlying curl, - // so there's nothing to "clear" - safe to ignore this error. - let _ = handle.resume_from(0); - } - - // Take at most 3m to connect if the `RUSTUP_DOWNLOAD_TIMEOUT` env var is not set. - handle.connect_timeout(timeout)?; - - { - let cberr = RefCell::new(None); - let mut transfer = handle.transfer(); - - // Data callback for libcurl which is called with data that's - // downloaded. We just feed it into our hasher and also write it out - // to disk. - transfer.write_function(|data| { - match callback(Event::DownloadDataReceived(data)) { - Ok(()) => Ok(data.len()), - Err(e) => { - *cberr.borrow_mut() = Some(e); - Ok(0) - } - } - })?; - - // Listen for headers and parse out a `Content-Length` (case-insensitive) if it - // comes so we know how much we're downloading. - transfer.header_function(|header| { - let Ok(data) = str::from_utf8(header) else { - return true; - }; - let prefix = "content-length: "; - let Some((dp, ds)) = data.split_at_checked(prefix.len()) else { - return true; - }; - if !dp.eq_ignore_ascii_case(prefix) { - return true; - } - let Ok(s) = ds.trim().parse::() else { - return true; - }; - let msg = Event::DownloadContentLengthReceived(s + resume_from); - if let Err(e) = callback(msg) { - *cberr.borrow_mut() = Some(e); - return false; - } - true - })?; - - // If an error happens check to see if we had a filesystem error up - // in `cberr`, but we always want to punt it up. - transfer.perform().or_else(|e| { - // If the original error was generated by one of our - // callbacks, return it. - match cberr.borrow_mut().take() { - Some(cberr) => Err(cberr), - None => { - // Otherwise, return the error from curl - if e.is_file_couldnt_read_file() { - Err(e).context(DownloadError::FileNotFound) - } else { - Err(e).context("error during download")? - } - } - } - })?; - } - - // If we didn't get a 20x or 0 ("OK" for files) then return an error. - // If resuming a download, we need a 206, as a 200 would mean the server ignored - // the range header, resulting in corruption. - let code = handle.response_code()?; - match (resume_from > 0, code) { - (_, 0) | (true, 206) | (false, 200..=299) => {} - _ => return Err(DownloadError::HttpStatus(code).into()), - } - - Ok(()) - }) - } -} - #[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] mod reqwest_be { #[cfg(feature = "reqwest-rustls-tls")] @@ -753,7 +561,4 @@ enum DownloadError { #[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] #[error(transparent)] Reqwest(#[from] ::reqwest::Error), - #[cfg(feature = "curl-backend")] - #[error(transparent)] - CurlError(#[from] ::curl::Error), } diff --git a/src/download/tests.rs b/src/download/tests.rs index 36552a5ce2..12d4760b9f 100644 --- a/src/download/tests.rs +++ b/src/download/tests.rs @@ -15,94 +15,6 @@ use hyper::server::conn::http1; use hyper::service::service_fn; use tempfile::TempDir; -#[cfg(feature = "curl-backend")] -mod curl { - use std::sync::Mutex; - use std::sync::atomic::{AtomicBool, Ordering}; - use std::time::Duration; - - use url::Url; - - use super::{scrub_env, serve_file, tmp_dir, write_file}; - use crate::download::{Backend, Event}; - - #[tokio::test] - async fn partially_downloaded_file_gets_resumed_from_byte_offset() { - let tmpdir = tmp_dir(); - let from_path = tmpdir.path().join("download-source"); - write_file(&from_path, "xxx45"); - - let target_path = tmpdir.path().join("downloaded"); - write_file(&target_path, "123"); - - let from_url = Url::from_file_path(&from_path).unwrap(); - Backend::Curl - .download_to_path( - &from_url, - &target_path, - true, - None, - Duration::from_secs(180), - ) - .await - .expect("Test download failed"); - - assert_eq!(std::fs::read_to_string(&target_path).unwrap(), "12345"); - } - - #[tokio::test] - async fn callback_gets_all_data_as_if_the_download_happened_all_at_once() { - let _guard = scrub_env().await; - let tmpdir = tmp_dir(); - let target_path = tmpdir.path().join("downloaded"); - write_file(&target_path, "123"); - - let addr = serve_file(b"xxx45".to_vec(), true); - - let from_url = format!("http://{addr}").parse().unwrap(); - - let callback_partial = AtomicBool::new(false); - let callback_len = Mutex::new(None); - let received_in_callback = Mutex::new(Vec::new()); - - Backend::Curl - .download_to_path( - &from_url, - &target_path, - true, - Some(&|msg| { - match msg { - Event::ResumingPartialDownload => { - assert!(!callback_partial.load(Ordering::SeqCst)); - callback_partial.store(true, Ordering::SeqCst); - } - Event::DownloadContentLengthReceived(len) => { - let mut flag = callback_len.lock().unwrap(); - assert!(flag.is_none()); - *flag = Some(len); - } - Event::DownloadDataReceived(data) => { - for b in data.iter() { - received_in_callback.lock().unwrap().push(*b); - } - } - } - - Ok(()) - }), - Duration::from_secs(180), - ) - .await - .expect("Test download failed"); - - assert!(callback_partial.into_inner()); - assert_eq!(*callback_len.lock().unwrap(), Some(5)); - let observed_bytes = received_in_callback.into_inner().unwrap(); - assert_eq!(observed_bytes, vec![b'1', b'2', b'3', b'4', b'5']); - assert_eq!(std::fs::read_to_string(&target_path).unwrap(), "12345"); - } -} - #[cfg(any(feature = "reqwest-rustls-tls", feature = "reqwest-native-tls"))] mod reqwest { use std::env::set_var; @@ -118,7 +30,13 @@ mod reqwest { use url::Url; use super::{scrub_env, serve_file, tmp_dir, write_file}; - use crate::download::{Backend, Event, TlsBackend}; + use crate::download::{Backend, Event}; + + #[cfg(feature = "reqwest-rustls-tls")] + const DOWNLOAD_BACKEND: Backend = Backend::Rustls; + + #[cfg(all(not(feature = "reqwest-rustls-tls"), feature = "reqwest-native-tls"))] + const DOWNLOAD_BACKEND: Backend = Backend::NativeTls; // Tests for correctly retrieving the proxy (host, port) tuple from $https_proxy #[tokio::test] @@ -192,7 +110,7 @@ mod reqwest { write_file(&target_path, "123"); let from_url = Url::from_file_path(&from_path).unwrap(); - Backend::Reqwest(TlsBackend::NativeTls) + DOWNLOAD_BACKEND .download_to_path( &from_url, &target_path, @@ -221,7 +139,7 @@ mod reqwest { let callback_len = Mutex::new(None); let received_in_callback = Mutex::new(Vec::new()); - Backend::Reqwest(TlsBackend::NativeTls) + DOWNLOAD_BACKEND .download_to_path( &from_url, &target_path, @@ -268,7 +186,7 @@ mod reqwest { let addr = serve_file(b"xxx45".to_vec(), false); let from_url = format!("http://{addr}").parse().unwrap(); - Backend::Reqwest(TlsBackend::NativeTls) + DOWNLOAD_BACKEND .download_to_path( &from_url, &target_path, @@ -293,7 +211,7 @@ mod reqwest { write_file(&target_path, "123"); let from_url = "http://240.0.0.0:1080".parse().unwrap(); - Backend::Reqwest(TlsBackend::NativeTls) + DOWNLOAD_BACKEND .download_to_path(&from_url, &target_path, true, None, Duration::from_secs(1)) .await .expect_err("download should fail with a connect error");