From 4edcb04be6807ed97a5a4d74a164ca797b1fd207 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Fri, 13 Mar 2026 16:54:10 -0500 Subject: [PATCH 1/9] Adjust casing of www-authenticate header to match http crate --- crates/cargo-test-support/src/registry.rs | 2 +- tests/testsuite/credential_process.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cargo-test-support/src/registry.rs b/crates/cargo-test-support/src/registry.rs index 9cf2aaaaad5..377ad1deb36 100644 --- a/crates/cargo-test-support/src/registry.rs +++ b/crates/cargo-test-support/src/registry.rs @@ -1029,7 +1029,7 @@ impl HttpServer { Response { code: 401, headers: vec![ - r#"WWW-Authenticate: Cargo login_url="https://test-registry-login/me""#.to_string(), + r#"www-authenticate: Cargo login_url="https://test-registry-login/me""#.to_string(), ], body: b"Unauthorized message from server.".to_vec(), } diff --git a/tests/testsuite/credential_process.rs b/tests/testsuite/credential_process.rs index cd223b523db..f86e349fcb3 100644 --- a/tests/testsuite/credential_process.rs +++ b/tests/testsuite/credential_process.rs @@ -337,7 +337,7 @@ fn all_not_found() { .with_stderr_data(str![[r#" [UPDATING] crates.io index [CREDENTIAL] [..]not_found[..] get crates-io -{"v":1,"registry":{"index-url":"[..]","name":"crates-io","headers":[[..]"WWW-Authenticate: Cargo login_url=/"https://test-registry-login/me/""[..]]},"kind":"get","operation":"read"} +{"v":1,"registry":{"index-url":"[..]","name":"crates-io","headers":[[..]"www-authenticate: Cargo login_url=/"https://test-registry-login/me/""[..]]},"kind":"get","operation":"read"} [ERROR] no token found, please run `cargo login` "#]]) @@ -373,7 +373,7 @@ fn all_not_supported() { .with_stderr_data(str![[r#" [UPDATING] crates.io index [CREDENTIAL] [..]not_supported[..] get crates-io -{"v":1,"registry":{"index-url":"[..]","name":"crates-io","headers":[[..]"WWW-Authenticate: Cargo login_url=/"https://test-registry-login/me/""[..]]},"kind":"get","operation":"read"} +{"v":1,"registry":{"index-url":"[..]","name":"crates-io","headers":[[..]"www-authenticate: Cargo login_url=/"https://test-registry-login/me/""[..]]},"kind":"get","operation":"read"} [ERROR] no credential providers could handle the request "#]]) From 8086888d591d2eb6cf5812f4c3418fb7d1d9494d Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Wed, 18 Mar 2026 16:20:39 -0500 Subject: [PATCH 2/9] fix: expect messages were on the wrong items --- src/cargo/core/resolver/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs index ea7aede8366..3d2be9fda4e 100644 --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@ -848,8 +848,8 @@ fn generalize_conflicting( // to be conflicting, then we can just say that we conflict with the parent. if let Some(others) = registry .query(critical_parents_dep, first_version) - .expect("an already used dep now error!?") .expect("an already used dep now pending!?") + .expect("an already used dep now error!?") .iter() .rev() // the last one to be tried is the least likely to be in the cache, so start with that. .map(|other| { From 717ad1d26aecf715199dea61ed68bb48f6d65589 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Sun, 15 Mar 2026 01:18:17 -0500 Subject: [PATCH 3/9] remove debug printing of HTTP information in tests --- crates/cargo-test-support/src/registry.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/cargo-test-support/src/registry.rs b/crates/cargo-test-support/src/registry.rs index 377ad1deb36..bc3a6301848 100644 --- a/crates/cargo-test-support/src/registry.rs +++ b/crates/cargo-test-support/src/registry.rs @@ -823,7 +823,6 @@ impl HttpServer { url, body, }; - println!("req: {:#?}", req); let response = self.route(&req); let buf = buf.get_mut(); write!(buf, "HTTP/1.1 {}\r\n", response.code).unwrap(); From b8b3428264110b4f8856c9d823375a1b950d8d59 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Wed, 18 Mar 2026 15:46:26 -0500 Subject: [PATCH 4/9] Move http debug into a function so it can be reused by the async side --- src/cargo/util/network/http.rs | 96 +++++++++++++++++----------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/src/cargo/util/network/http.rs b/src/cargo/util/network/http.rs index 3c8133df2f1..343b2ddfe10 100644 --- a/src/cargo/util/network/http.rs +++ b/src/cargo/util/network/http.rs @@ -133,58 +133,60 @@ pub fn configure_http_handle(gctx: &GlobalContext, handle: &mut Easy) -> CargoRe if let Some(true) = http.debug { handle.verbose(true)?; tracing::debug!(target: "network", "{:#?}", curl::Version::get()); - handle.debug_function(|kind, data| { - enum LogLevel { - Debug, - Trace, + handle.debug_function(debug)?; + } + + HttpTimeout::new(gctx) +} + +pub fn debug(kind: InfoType, data: &[u8]) { + enum LogLevel { + Debug, + Trace, + } + use LogLevel::*; + let (prefix, level) = match kind { + InfoType::Text => ("*", Debug), + InfoType::HeaderIn => ("<", Debug), + InfoType::HeaderOut => (">", Debug), + InfoType::DataIn => ("{", Trace), + InfoType::DataOut => ("}", Trace), + InfoType::SslDataIn | InfoType::SslDataOut => return, + _ => return, + }; + let starts_with_ignore_case = |line: &str, text: &str| -> bool { + let line = line.as_bytes(); + let text = text.as_bytes(); + line[..line.len().min(text.len())].eq_ignore_ascii_case(text) + }; + match str::from_utf8(data) { + Ok(s) => { + for mut line in s.lines() { + if starts_with_ignore_case(line, "authorization:") { + line = "Authorization: [REDACTED]"; + } else if starts_with_ignore_case(line, "h2h3 [authorization:") { + line = "h2h3 [Authorization: [REDACTED]]"; + } else if starts_with_ignore_case(line, "set-cookie") { + line = "set-cookie: [REDACTED]"; + } + match level { + Debug => debug!(target: "network", "http-debug: {prefix} {line}"), + Trace => trace!(target: "network", "http-debug: {prefix} {line}"), + } } - use LogLevel::*; - let (prefix, level) = match kind { - InfoType::Text => ("*", Debug), - InfoType::HeaderIn => ("<", Debug), - InfoType::HeaderOut => (">", Debug), - InfoType::DataIn => ("{", Trace), - InfoType::DataOut => ("}", Trace), - InfoType::SslDataIn | InfoType::SslDataOut => return, - _ => return, - }; - let starts_with_ignore_case = |line: &str, text: &str| -> bool { - let line = line.as_bytes(); - let text = text.as_bytes(); - line[..line.len().min(text.len())].eq_ignore_ascii_case(text) - }; - match str::from_utf8(data) { - Ok(s) => { - for mut line in s.lines() { - if starts_with_ignore_case(line, "authorization:") { - line = "Authorization: [REDACTED]"; - } else if starts_with_ignore_case(line, "h2h3 [authorization:") { - line = "h2h3 [Authorization: [REDACTED]]"; - } else if starts_with_ignore_case(line, "set-cookie") { - line = "set-cookie: [REDACTED]"; - } - match level { - Debug => debug!(target: "network", "http-debug: {prefix} {line}"), - Trace => trace!(target: "network", "http-debug: {prefix} {line}"), - } - } + } + Err(_) => { + let len = data.len(); + match level { + Debug => { + debug!(target: "network", "http-debug: {prefix} ({len} bytes of data)") } - Err(_) => { - let len = data.len(); - match level { - Debug => { - debug!(target: "network", "http-debug: {prefix} ({len} bytes of data)") - } - Trace => { - trace!(target: "network", "http-debug: {prefix} ({len} bytes of data)") - } - } + Trace => { + trace!(target: "network", "http-debug: {prefix} ({len} bytes of data)") } } - })?; + } } - - HttpTimeout::new(gctx) } #[must_use] From 89c36138319fe256eede90093be3c0c66e94e1a6 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Wed, 18 Mar 2026 15:07:10 -0500 Subject: [PATCH 5/9] Remove download_now method and inline it. It was a problem since it took self by value. --- .../ops/common_for_install_and_uninstall.rs | 11 +++++---- src/cargo/sources/source.rs | 24 +------------------ 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/cargo/ops/common_for_install_and_uninstall.rs b/src/cargo/ops/common_for_install_and_uninstall.rs index c073c127580..fcc23503f21 100644 --- a/src/cargo/ops/common_for_install_and_uninstall.rs +++ b/src/cargo/ops/common_for_install_and_uninstall.rs @@ -13,13 +13,13 @@ use cargo_util_schemas::core::PartialVersion; use ops::FilterRule; use serde::{Deserialize, Serialize}; -use crate::core::Target; use crate::core::compiler::{DirtyReason, Freshness}; use crate::core::{Dependency, FeatureValue, Package, PackageId, SourceId}; +use crate::core::{PackageSet, Target}; use crate::ops::{self, CompileFilter, CompileOptions}; use crate::sources::PathSource; -use crate::sources::source::QueryKind; use crate::sources::source::Source; +use crate::sources::source::{QueryKind, SourceMap}; use crate::util::GlobalContext; use crate::util::cache_lock::CacheLockMode; use crate::util::context::{ConfigRelativePath, Definition}; @@ -673,8 +673,11 @@ cannot install package `{name} {ver}`, it requires rustc {msrv} or newer, while ) } } - let pkg = Box::new(source).download_now(summary.package_id(), gctx)?; - Ok(pkg) + // Download the package immediately. + let mut sources = SourceMap::new(); + sources.insert(Box::new(source)); + let pkg_set = PackageSet::new(&[summary.package_id()], sources, gctx)?; + Ok(pkg_set.get_one(summary.package_id())?.clone()) } None => { let is_yanked: bool = if dep.version_req().is_exact() { diff --git a/src/cargo/sources/source.rs b/src/cargo/sources/source.rs index afe28f3aa39..ac65dce4f0e 100644 --- a/src/cargo/sources/source.rs +++ b/src/cargo/sources/source.rs @@ -5,10 +5,9 @@ use std::fmt; use std::task::Poll; use crate::core::SourceId; -use crate::core::package::PackageSet; use crate::core::{Dependency, Package, PackageId}; use crate::sources::IndexSummary; -use crate::util::{CargoResult, GlobalContext}; +use crate::util::CargoResult; /// An abstraction of different sources of Cargo packages. /// @@ -89,27 +88,6 @@ pub trait Source { /// download has finished. fn download(&mut self, package: PackageId) -> CargoResult; - /// Convenience method used to **immediately** fetch a [`Package`] for the - /// given [`PackageId`]. - /// - /// This may trigger a download if necessary. This should only be used - /// when a single package is needed (as in the case for `cargo install`). - /// Otherwise downloads should be batched together via [`PackageSet`]. - fn download_now( - self: Box, - package: PackageId, - gctx: &GlobalContext, - ) -> CargoResult - where - Self: std::marker::Sized, - { - let mut sources = SourceMap::new(); - sources.insert(self); - let pkg_set = PackageSet::new(&[package], sources, gctx)?; - let pkg = pkg_set.get_one(package)?; - Ok(Package::clone(pkg)) - } - /// Gives the source the downloaded `.crate` file. /// /// When a source has returned [`MaybePackage::Download`] in the From 64b6fc5566fb7583f17d7ab9048eac3ed6b8082c Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Wed, 18 Mar 2026 15:15:27 -0500 Subject: [PATCH 6/9] Use dyn instead of generics for Source. It was a dyn Source anyway at the only caller. --- src/cargo/ops/cargo_install.rs | 13 +++++-------- src/cargo/ops/common_for_install_and_uninstall.rs | 9 +++------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index a78b09814e3..fbe3405d692 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -149,7 +149,7 @@ impl<'gctx> InstallablePackage<'gctx> { let mut source = map.load(source_id, &HashSet::new())?; if let Ok(Some(pkg)) = installed_exact_package( dep.clone(), - &mut source, + &mut *source, gctx, original_opts, &root, @@ -164,7 +164,7 @@ impl<'gctx> InstallablePackage<'gctx> { return Ok(None); } select_dep_pkg( - &mut source, + &mut *source, dep, gctx, needs_update_if_source_is_index, @@ -821,18 +821,15 @@ fn is_installed( /// Checks if vers can only be satisfied by exactly one version of a package in a registry, and it's /// already installed. If this is the case, we can skip interacting with a registry to check if /// newer versions may be installable, as no newer version can exist. -fn installed_exact_package( +fn installed_exact_package( dep: Dependency, - source: &mut T, + source: &mut dyn Source, gctx: &GlobalContext, opts: &ops::CompileOptions, root: &Filesystem, dst: &Path, force: bool, -) -> CargoResult> -where - T: Source, -{ +) -> CargoResult> { if !dep.version_req().is_exact() { // If the version isn't exact, we may need to update the registry and look for a newer // version - we can't know if the package is installed without doing so. diff --git a/src/cargo/ops/common_for_install_and_uninstall.rs b/src/cargo/ops/common_for_install_and_uninstall.rs index fcc23503f21..9eecf4fd1c3 100644 --- a/src/cargo/ops/common_for_install_and_uninstall.rs +++ b/src/cargo/ops/common_for_install_and_uninstall.rs @@ -594,16 +594,13 @@ pub fn path_source(source_id: SourceId, gctx: &GlobalContext) -> CargoResult( - source: &mut T, +pub fn select_dep_pkg( + source: &mut dyn Source, dep: Dependency, gctx: &GlobalContext, needs_update: bool, current_rust_version: Option<&PartialVersion>, -) -> CargoResult -where - T: Source, -{ +) -> CargoResult { // This operation may involve updating some sources or making a few queries // which may involve frobbing caches, as a result make sure we synchronize // with other global Cargos From 2077feebf8ebecfdb4031d4e95be98e6ea10e1b9 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Wed, 18 Mar 2026 15:29:30 -0500 Subject: [PATCH 7/9] Remove blanket impl for boxed Source --- src/cargo/ops/registry/publish.rs | 2 +- src/cargo/sources/source.rs | 72 ------------------------------- 2 files changed, 1 insertion(+), 73 deletions(-) diff --git a/src/cargo/ops/registry/publish.rs b/src/cargo/ops/registry/publish.rs index 5afcb1a9d99..ab8d0898828 100644 --- a/src/cargo/ops/registry/publish.rs +++ b/src/cargo/ops/registry/publish.rs @@ -412,7 +412,7 @@ fn wait_for_any_publish_confirmation( source.invalidate_cache(); let mut available = BTreeSet::new(); for pkg in pkgs { - if poll_one_package(registry_src, pkg, &mut source)? { + if poll_one_package(registry_src, pkg, &mut *source)? { available.insert(*pkg); } } diff --git a/src/cargo/sources/source.rs b/src/cargo/sources/source.rs index ac65dce4f0e..18dfb74cbdd 100644 --- a/src/cargo/sources/source.rs +++ b/src/cargo/sources/source.rs @@ -192,78 +192,6 @@ pub enum MaybePackage { }, } -/// A blanket implementation forwards all methods to [`Source`]. -impl<'a, T: Source + ?Sized + 'a> Source for Box { - fn source_id(&self) -> SourceId { - (**self).source_id() - } - - fn replaced_source_id(&self) -> SourceId { - (**self).replaced_source_id() - } - - fn supports_checksums(&self) -> bool { - (**self).supports_checksums() - } - - fn requires_precise(&self) -> bool { - (**self).requires_precise() - } - - fn query( - &mut self, - dep: &Dependency, - kind: QueryKind, - f: &mut dyn FnMut(IndexSummary), - ) -> Poll> { - (**self).query(dep, kind, f) - } - - fn invalidate_cache(&mut self) { - (**self).invalidate_cache() - } - - fn set_quiet(&mut self, quiet: bool) { - (**self).set_quiet(quiet) - } - - fn download(&mut self, id: PackageId) -> CargoResult { - (**self).download(id) - } - - fn finish_download(&mut self, id: PackageId, data: Vec) -> CargoResult { - (**self).finish_download(id, data) - } - - fn fingerprint(&self, pkg: &Package) -> CargoResult { - (**self).fingerprint(pkg) - } - - fn verify(&self, pkg: PackageId) -> CargoResult<()> { - (**self).verify(pkg) - } - - fn describe(&self) -> String { - (**self).describe() - } - - fn is_replaced(&self) -> bool { - (**self).is_replaced() - } - - fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]) { - (**self).add_to_yanked_whitelist(pkgs); - } - - fn is_yanked(&mut self, pkg: PackageId) -> Poll> { - (**self).is_yanked(pkg) - } - - fn block_until_ready(&mut self) -> CargoResult<()> { - (**self).block_until_ready() - } -} - /// A blanket implementation forwards all methods to [`Source`]. impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T { fn source_id(&self) -> SourceId { From 11ee66e8bc036d9b6e1b1b6e6a0bc72aa76a359d Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Wed, 18 Mar 2026 22:55:56 -0500 Subject: [PATCH 8/9] change implementation of search::not_update test to avoid manually updating --- tests/testsuite/search.rs | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/tests/testsuite/search.rs b/tests/testsuite/search.rs index 869500740c9..ce4e33d494d 100644 --- a/tests/testsuite/search.rs +++ b/tests/testsuite/search.rs @@ -1,11 +1,7 @@ //! Tests for the `cargo search` command. -use std::collections::HashSet; - use crate::prelude::*; use crate::utils::cargo_process; -use cargo::util::cache_lock::CacheLockMode; -use cargo_test_support::paths; use cargo_test_support::registry::{RegistryBuilder, Response}; use cargo_test_support::str; @@ -93,24 +89,15 @@ fn setup() -> RegistryBuilder { fn not_update() { let registry = setup().build(); - use cargo::core::{Shell, SourceId}; - use cargo::sources::RegistrySource; - use cargo::sources::source::Source; - use cargo::util::GlobalContext; - - let sid = SourceId::for_registry(registry.index_url()).unwrap(); - let gctx = GlobalContext::new( - Shell::from_write(Box::new(Vec::new())), - paths::root(), - paths::home().join(".cargo"), - ); - let lock = gctx - .acquire_package_cache_lock(CacheLockMode::DownloadExclusive) - .unwrap(); - let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &gctx).unwrap(); - regsrc.invalidate_cache(); - regsrc.block_until_ready().unwrap(); - drop(lock); + cargo_process("search postgres") + .replace_crates_io(registry.index_url()) + .with_stdout_data(SEARCH_RESULTS) + .with_stderr_data(str![[r#" +[UPDATING] crates.io index +[NOTE] to learn more about a package, run `cargo info ` + +"#]]) + .run(); cargo_process("search postgres") .replace_crates_io(registry.index_url()) From 95d90c4517c283a0e0b812e1bd1b92dd4869b748 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Wed, 18 Mar 2026 22:58:50 -0500 Subject: [PATCH 9/9] convert a few tests to use snapbox str! --- tests/testsuite/patch.rs | 8 ++++---- tests/testsuite/path.rs | 11 +++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/testsuite/patch.rs b/tests/testsuite/patch.rs index 297c18303ed..886b5129be8 100644 --- a/tests/testsuite/patch.rs +++ b/tests/testsuite/patch.rs @@ -386,8 +386,8 @@ fn patch_to_git_pull_request() { p.cargo("check -v") .with_status(101) - .with_stderr_data(format!( - r#"[UPDATING] git repository `https://github.com/rust-lang/does-not-exist/pull/123` + .with_stderr_data(str![[r#" +[UPDATING] git repository `https://github.com/rust-lang/does-not-exist/pull/123` ... [ERROR] failed to load source for dependency `bar` @@ -409,8 +409,8 @@ Caused by: Caused by: ... -"# - )) + +"#]]) .run(); } diff --git a/tests/testsuite/path.rs b/tests/testsuite/path.rs index ef831f226f8..ab3a59fbcd8 100644 --- a/tests/testsuite/path.rs +++ b/tests/testsuite/path.rs @@ -1139,23 +1139,22 @@ fn invalid_path_with_base() { p.cargo("build") .masquerade_as_nightly_cargo(&["path-bases"]) .with_status(101) - .with_stderr_data( - "\ + .with_stderr_data(str![[r#" [ERROR] failed to get `bar` as a dependency of package `foo v0.5.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: - unable to update [ROOT]/foo/shared_proj/\" + unable to update [ROOT]/foo/shared_proj/" Caused by: - failed to read `[ROOT]/foo/shared_proj/\"/Cargo.toml` + failed to read `[ROOT]/foo/shared_proj/"/Cargo.toml` Caused by: [NOT_FOUND] -", - ) + +"#]]) .run(); }