Skip to content

Commit c7c9b8f

Browse files
committed
Show some HTTP headers in error messages.
1 parent 96f8d6c commit c7c9b8f

File tree

5 files changed

+197
-6
lines changed

5 files changed

+197
-6
lines changed

src/cargo/core/package.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
2626
use crate::core::{SourceMap, Summary, Workspace};
2727
use crate::ops;
2828
use crate::util::config::PackageCacheLock;
29-
use crate::util::errors::{CargoResult, HttpNotSuccessful};
29+
use crate::util::errors::{CargoResult, HttpNotSuccessful, DEBUG_HEADERS};
3030
use crate::util::interning::InternedString;
3131
use crate::util::network::retry::{Retry, RetryResult};
3232
use crate::util::network::sleep::SleepTracker;
@@ -379,6 +379,9 @@ struct Download<'cfg> {
379379
/// Actual downloaded data, updated throughout the lifetime of this download.
380380
data: RefCell<Vec<u8>>,
381381

382+
/// HTTP headers for debugging.
383+
headers: RefCell<Vec<String>>,
384+
382385
/// The URL that we're downloading from, cached here for error messages and
383386
/// reenqueuing.
384387
url: String,
@@ -762,6 +765,19 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
762765
});
763766
Ok(buf.len())
764767
})?;
768+
handle.header_function(move |data| {
769+
tls::with(|downloads| {
770+
if let Some(downloads) = downloads {
771+
// Headers contain trailing \r\n, trim them to make it easier
772+
// to work with.
773+
let h = String::from_utf8_lossy(data).trim().to_string();
774+
if DEBUG_HEADERS.iter().any(|p| h.starts_with(p)) {
775+
downloads.pending[&token].0.headers.borrow_mut().push(h);
776+
}
777+
}
778+
});
779+
true
780+
})?;
765781

766782
handle.progress(true)?;
767783
handle.progress_function(move |dl_total, dl_cur, _, _| {
@@ -787,6 +803,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
787803
let dl = Download {
788804
token,
789805
data: RefCell::new(Vec::new()),
806+
headers: RefCell::new(Vec::new()),
790807
id,
791808
url,
792809
descriptor,
@@ -826,6 +843,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
826843
.remove(&token)
827844
.expect("got a token for a non-in-progress transfer");
828845
let data = mem::take(&mut *dl.data.borrow_mut());
846+
let headers = mem::take(&mut *dl.headers.borrow_mut());
829847
let mut handle = self.set.multi.remove(handle)?;
830848
self.pending_ids.remove(&dl.id);
831849

@@ -867,6 +885,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
867885
code,
868886
url: url.to_string(),
869887
body: data,
888+
headers,
870889
}
871890
.into());
872891
}

src/cargo/sources/registry/http_remote.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::ops::{self};
77
use crate::sources::registry::download;
88
use crate::sources::registry::MaybeLock;
99
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
10-
use crate::util::errors::{CargoResult, HttpNotSuccessful};
10+
use crate::util::errors::{CargoResult, HttpNotSuccessful, DEBUG_HEADERS};
1111
use crate::util::network::retry::{Retry, RetryResult};
1212
use crate::util::network::sleep::SleepTracker;
1313
use crate::util::{auth, Config, Filesystem, IntoUrl, Progress, ProgressStyle};
@@ -142,6 +142,7 @@ struct Headers {
142142
last_modified: Option<String>,
143143
etag: Option<String>,
144144
www_authenticate: Vec<String>,
145+
others: Vec<String>,
145146
}
146147

147148
enum StatusCode {
@@ -293,6 +294,7 @@ impl<'cfg> HttpRegistry<'cfg> {
293294
code,
294295
url: url.to_owned(),
295296
body: data,
297+
headers: download.header_map.take().others,
296298
}
297299
.into());
298300
}
@@ -546,6 +548,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
546548
code: 401,
547549
body: result.data,
548550
url: self.full_url(path),
551+
headers: result.header_map.others,
549552
}
550553
.into());
551554
if self.auth_required {
@@ -665,7 +668,11 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
665668
LAST_MODIFIED => header_map.last_modified = Some(value.to_string()),
666669
ETAG => header_map.etag = Some(value.to_string()),
667670
WWW_AUTHENTICATE => header_map.www_authenticate.push(value.to_string()),
668-
_ => {}
671+
_ => {
672+
if DEBUG_HEADERS.iter().any(|prefix| tag.starts_with(prefix)) {
673+
header_map.others.push(format!("{tag}: {value}"));
674+
}
675+
}
669676
}
670677
}
671678
});

src/cargo/util/errors.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,22 @@ use super::truncate_with_ellipsis;
88

99
pub type CargoResult<T> = anyhow::Result<T>;
1010

11+
/// These are headers that are included in error messages to help with
12+
/// diagnosing issues.
13+
pub const DEBUG_HEADERS: &[&str] = &[
14+
"x-amz-cf-id",
15+
"x-amz-cf-pop",
16+
"x-amz-id-2",
17+
"x-cache",
18+
"x-served-by",
19+
];
20+
1121
#[derive(Debug)]
1222
pub struct HttpNotSuccessful {
1323
pub code: u32,
1424
pub url: String,
1525
pub body: Vec<u8>,
26+
pub headers: Vec<String>,
1627
}
1728

1829
impl fmt::Display for HttpNotSuccessful {
@@ -23,9 +34,13 @@ impl fmt::Display for HttpNotSuccessful {
2334

2435
write!(
2536
f,
26-
"failed to get successful HTTP response from `{}`, got {}\nbody:\n{body}",
27-
self.url, self.code
28-
)
37+
"failed to get successful HTTP response from `{}`, got {}\n",
38+
self.url, self.code,
39+
)?;
40+
if !self.headers.is_empty() {
41+
write!(f, "debug headers:\n{}\n", self.headers.join("\n"))?;
42+
}
43+
write!(f, "body:\n{body}",)
2944
}
3045
}
3146

src/cargo/util/network/retry.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,14 @@ fn with_retry_repeats_the_call_then_works() {
151151
code: 501,
152152
url: "Uri".to_string(),
153153
body: Vec::new(),
154+
headers: Vec::new(),
154155
}
155156
.into();
156157
let error2 = HttpNotSuccessful {
157158
code: 502,
158159
url: "Uri".to_string(),
159160
body: Vec::new(),
161+
headers: Vec::new(),
160162
}
161163
.into();
162164
let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
@@ -176,12 +178,14 @@ fn with_retry_finds_nested_spurious_errors() {
176178
code: 501,
177179
url: "Uri".to_string(),
178180
body: Vec::new(),
181+
headers: Vec::new(),
179182
});
180183
let error1 = anyhow::Error::from(error1.context("A non-spurious wrapping err"));
181184
let error2 = anyhow::Error::from(HttpNotSuccessful {
182185
code: 502,
183186
url: "Uri".to_string(),
184187
body: Vec::new(),
188+
headers: Vec::new(),
185189
});
186190
let error2 = anyhow::Error::from(error2.context("A second chained error"));
187191
let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
@@ -200,6 +204,7 @@ fn default_retry_schedule() {
200204
code: 500,
201205
url: "Uri".to_string(),
202206
body: Vec::new(),
207+
headers: Vec::new(),
203208
}))
204209
};
205210
let config = Config::default().unwrap();

tests/testsuite/registry.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3274,3 +3274,148 @@ or use environment variable CARGO_REGISTRY_TOKEN
32743274
.with_status(101)
32753275
.run();
32763276
}
3277+
3278+
const SAMPLE_HEADERS: &[&str] = &[
3279+
"x-amz-cf-pop: SFO53-P2",
3280+
"x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==",
3281+
"x-cache: Hit from cloudfront",
3282+
"server: AmazonS3",
3283+
"x-amz-version-id: pvsJYY_JGsWiSETZvLJKb7DeEW5wWq1W",
3284+
"x-amz-server-side-encryption: AES256",
3285+
"content-type: text/plain",
3286+
"via: 1.1 bcbc5b46216015493e082cfbcf77ef10.cloudfront.net (CloudFront)",
3287+
];
3288+
3289+
#[cargo_test]
3290+
fn debug_header_message_index() {
3291+
// The error message should include some headers for debugging purposes.
3292+
let _server = RegistryBuilder::new()
3293+
.http_index()
3294+
.add_responder("/index/3/b/bar", |_, _| Response {
3295+
code: 503,
3296+
headers: SAMPLE_HEADERS.iter().map(|s| s.to_string()).collect(),
3297+
body: b"Please slow down".to_vec(),
3298+
})
3299+
.build();
3300+
Package::new("bar", "1.0.0").publish();
3301+
3302+
let p = project()
3303+
.file(
3304+
"Cargo.toml",
3305+
r#"
3306+
[package]
3307+
name = "foo"
3308+
version = "0.1.0"
3309+
3310+
[dependencies]
3311+
bar = "1.0"
3312+
"#,
3313+
)
3314+
.file("src/lib.rs", "")
3315+
.build();
3316+
p.cargo("fetch").with_status(101).with_stderr("\
3317+
[UPDATING] `dummy-registry` index
3318+
warning: spurious network error (3 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar`, got 503
3319+
debug headers:
3320+
x-amz-cf-pop: SFO53-P2
3321+
x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==
3322+
x-cache: Hit from cloudfront
3323+
body:
3324+
Please slow down
3325+
warning: spurious network error (2 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar`, got 503
3326+
debug headers:
3327+
x-amz-cf-pop: SFO53-P2
3328+
x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==
3329+
x-cache: Hit from cloudfront
3330+
body:
3331+
Please slow down
3332+
warning: spurious network error (1 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar`, got 503
3333+
debug headers:
3334+
x-amz-cf-pop: SFO53-P2
3335+
x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==
3336+
x-cache: Hit from cloudfront
3337+
body:
3338+
Please slow down
3339+
error: failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)`
3340+
3341+
Caused by:
3342+
failed to query replaced source registry `crates-io`
3343+
3344+
Caused by:
3345+
download of 3/b/bar failed
3346+
3347+
Caused by:
3348+
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar`, got 503
3349+
debug headers:
3350+
x-amz-cf-pop: SFO53-P2
3351+
x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==
3352+
x-cache: Hit from cloudfront
3353+
body:
3354+
Please slow down
3355+
").run();
3356+
}
3357+
3358+
#[cargo_test]
3359+
fn debug_header_message_dl() {
3360+
// Same as debug_header_message_index, but for the dl endpoint which goes
3361+
// through a completely different code path.
3362+
let _server = RegistryBuilder::new()
3363+
.http_index()
3364+
.add_responder("/dl/bar/1.0.0/download", |_, _| Response {
3365+
code: 503,
3366+
headers: SAMPLE_HEADERS.iter().map(|s| s.to_string()).collect(),
3367+
body: b"Please slow down".to_vec(),
3368+
})
3369+
.build();
3370+
Package::new("bar", "1.0.0").publish();
3371+
let p = project()
3372+
.file(
3373+
"Cargo.toml",
3374+
r#"
3375+
[package]
3376+
name = "foo"
3377+
version = "0.1.0"
3378+
3379+
[dependencies]
3380+
bar = "1.0"
3381+
"#,
3382+
)
3383+
.file("src/lib.rs", "")
3384+
.build();
3385+
3386+
p.cargo("fetch").with_status(101).with_stderr("\
3387+
[UPDATING] `dummy-registry` index
3388+
[DOWNLOADING] crates ...
3389+
warning: spurious network error (3 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download`, got 503
3390+
debug headers:
3391+
x-amz-cf-pop: SFO53-P2
3392+
x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==
3393+
x-cache: Hit from cloudfront
3394+
body:
3395+
Please slow down
3396+
warning: spurious network error (2 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download`, got 503
3397+
debug headers:
3398+
x-amz-cf-pop: SFO53-P2
3399+
x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==
3400+
x-cache: Hit from cloudfront
3401+
body:
3402+
Please slow down
3403+
warning: spurious network error (1 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download`, got 503
3404+
debug headers:
3405+
x-amz-cf-pop: SFO53-P2
3406+
x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==
3407+
x-cache: Hit from cloudfront
3408+
body:
3409+
Please slow down
3410+
error: failed to download from `http://127.0.0.1:[..]/dl/bar/1.0.0/download`
3411+
3412+
Caused by:
3413+
failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download`, got 503
3414+
debug headers:
3415+
x-amz-cf-pop: SFO53-P2
3416+
x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==
3417+
x-cache: Hit from cloudfront
3418+
body:
3419+
Please slow down
3420+
").run();
3421+
}

0 commit comments

Comments
 (0)