Skip to content

Commit 9759340

Browse files
committed
Always nicely show errors from crates.io if possible
Currently if Cargo ever gets a non-200 response, it will either not show the error at all (if it was a 403 or 404), or spit out the entire response body. Historically crates.io has served a 200 for most errors to work around this, but we've stopped doing this as it causes problems for other clients. Additionally, we're starting to server more errors that have semantic meaning (429 for rate limiting, 503 when we're in read only mode). If the request specifies "Accept: application/json", we should ideally return the errors formatted nicely. This isn't always true, but it's what we'd like to do going forward. While the output that Cargo puts out at least contains the actual message, it's buried under a ton of useless info. This changes the behavior so that if the response was valid JSON in the format that Cargo expects, it just shows that (along with a description of the response status), and only falls back to spitting out everything if it can't parse the response body. I'd love to add some more tests for this, but I've had trouble finding anywhere in the test suite that exercises these paths.
1 parent bd60ac8 commit 9759340

File tree

2 files changed

+20
-17
lines changed

2 files changed

+20
-17
lines changed

src/crates-io/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ path = "lib.rs"
1616
[dependencies]
1717
curl = "0.4"
1818
failure = "0.1.1"
19+
http = "0.1"
1920
serde = { version = "1.0", features = ['derive'] }
2021
serde_derive = "1.0"
2122
serde_json = "1.0"

src/crates-io/lib.rs

+19-17
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::io::Cursor;
88

99
use curl::easy::{Easy, List};
1010
use failure::bail;
11+
use http::status::StatusCode;
1112
use serde::{Deserialize, Serialize};
1213
use serde_json;
1314
use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET};
@@ -323,30 +324,31 @@ fn handle(handle: &mut Easy, read: &mut dyn FnMut(&mut [u8]) -> usize) -> Result
323324
handle.perform()?;
324325
}
325326

326-
match handle.response_code()? {
327-
0 => {} // file upload url sometimes
328-
200 => {}
329-
403 => bail!("received 403 unauthorized response code"),
330-
404 => bail!("received 404 not found response code"),
331-
code => bail!(
327+
let body = match String::from_utf8(body) {
328+
Ok(body) => body,
329+
Err(..) => bail!("response body was not valid utf-8"),
330+
};
331+
let errors = serde_json::from_str::<ApiErrorList>(&body).ok().map(|s| {
332+
s.errors.into_iter().map(|s| s.detail).collect::<Vec<_>>()
333+
});
334+
335+
match (handle.response_code()?, errors) {
336+
(0, None) | (200, None) => {},
337+
(code, Some(errors)) => {
338+
let code = StatusCode::from_u16(code as _)?;
339+
bail!("api errors (status {}): {}", code, errors.join(", "))
340+
}
341+
(code, None) => bail!(
332342
"failed to get a 200 OK response, got {}\n\
333343
headers:\n\
334344
\t{}\n\
335345
body:\n\
336346
{}",
337-
code,
338-
headers.join("\n\t"),
339-
String::from_utf8_lossy(&body)
347+
code,
348+
headers.join("\n\t"),
349+
body,
340350
),
341351
}
342352

343-
let body = match String::from_utf8(body) {
344-
Ok(body) => body,
345-
Err(..) => bail!("response body was not valid utf-8"),
346-
};
347-
if let Ok(errors) = serde_json::from_str::<ApiErrorList>(&body) {
348-
let errors = errors.errors.into_iter().map(|s| s.detail);
349-
bail!("api errors: {}", errors.collect::<Vec<_>>().join(", "));
350-
}
351353
Ok(body)
352354
}

0 commit comments

Comments
 (0)