Skip to content

Commit ed0350d

Browse files
authored
Add effective_url to http_async (#16899)
### What does this PR try to resolve? Adds `effective_url` to `http_async`. The effective URL is already captured in some errors when using curl directly via the `HttpNotSuccessful::new_from_handle` function. Effective URL may differ from the original URL due to redirects. This change adds the same functionality in `http_async` with a new `HttpNotSuccessful::new_from_response` function. The new function is then used in sparse registry, which impacts error messages. Additionally, the http_async `curl` errors are changed to be transparent to avoid making even longer error chains. cc #16845
2 parents bf2f4a4 + 9cfe8e0 commit ed0350d

4 files changed

Lines changed: 52 additions & 30 deletions

File tree

src/cargo/sources/registry/http_remote.rs

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use crate::util::cache_lock::CacheLockMode;
1818
use crate::util::errors::CargoResult;
1919
use crate::util::errors::HttpNotSuccessful;
2020
use crate::util::interning::InternedString;
21-
use crate::util::network::http_async::ResponsePartsExtensions;
2221
use crate::util::network::retry::Retry;
2322
use crate::util::network::retry::RetryResult;
2423
use anyhow::Context as _;
@@ -27,6 +26,7 @@ use cargo_util::paths;
2726
use futures::lock::Mutex;
2827
use http::HeaderName;
2928
use http::HeaderValue;
29+
use http::Response;
3030
use std::cell::Cell;
3131
use std::cell::RefCell;
3232
use std::collections::HashSet;
@@ -599,17 +599,10 @@ impl<'gctx> HttpBackend<'gctx> {
599599
}
600600
}
601601

602-
let mut err = Err(HttpNotSuccessful {
603-
code: http::StatusCode::UNAUTHORIZED.as_u16() as u32,
604-
body: body,
605-
url: full_url,
606-
ip: None,
607-
headers: response
608-
.headers
609-
.iter()
610-
.map(|(k, v)| format!("{}: {}", k, v.to_str().unwrap_or_default()))
611-
.collect(),
612-
}
602+
let mut err = Err(HttpNotSuccessful::new_from_response(
603+
Response::from_parts(response, body),
604+
&full_url,
605+
)
613606
.into());
614607
if self.auth_required.get() {
615608
let auth_error = auth::AuthorizationError::new(
@@ -622,17 +615,10 @@ impl<'gctx> HttpBackend<'gctx> {
622615
}
623616
err
624617
}
625-
code => Err(HttpNotSuccessful {
626-
code: code.as_u16() as u32,
627-
body: body,
628-
url: full_url,
629-
ip: response.client_ip().map(str::to_owned),
630-
headers: response
631-
.headers
632-
.iter()
633-
.map(|(k, v)| format!("{}: {}", k, v.to_str().unwrap_or_default()))
634-
.collect(),
635-
}
618+
_ => Err(HttpNotSuccessful::new_from_response(
619+
Response::from_parts(response, body),
620+
&full_url,
621+
)
636622
.into()),
637623
}
638624
}

src/cargo/util/errors.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use anyhow::Error;
22
use curl::easy::Easy;
3+
use http::Response;
34
use std::fmt::{self, Write};
45
use std::path::PathBuf;
56

7+
use crate::util::network::http_async::ResponsePartsExtensions;
8+
69
use super::truncate_with_ellipsis;
710

811
pub type CargoResult<T> = anyhow::Result<T>;
@@ -59,6 +62,24 @@ impl HttpNotSuccessful {
5962
}
6063
}
6164

65+
pub fn new_from_response(response: Response<Vec<u8>>, url: &str) -> HttpNotSuccessful {
66+
let ip = response.client_ip().map(str::to_string);
67+
let url = response.effective_url().unwrap_or(url).to_string();
68+
let (head, body) = response.into_parts();
69+
let headers = head
70+
.headers
71+
.into_iter()
72+
.filter_map(|(k, v)| Some(format!("{}: {}", k?.as_str(), v.to_str().ok()?)))
73+
.collect();
74+
HttpNotSuccessful {
75+
code: head.status.as_u16() as u32,
76+
url,
77+
ip,
78+
body,
79+
headers,
80+
}
81+
}
82+
6283
/// Renders the error in a compact form.
6384
pub fn display_short(&self) -> String {
6485
self.render(false)

src/cargo/util/network/http_async.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ type HttpResult<T> = std::result::Result<T, Error>;
2525
#[derive(Debug, Clone, thiserror::Error)]
2626
#[non_exhaustive]
2727
pub enum Error {
28-
#[error("curl multi failed")]
28+
#[error(transparent)]
2929
Multi(#[from] curl::MultiError),
3030

31-
#[error("curl failed")]
31+
#[error(transparent)]
3232
Easy(#[from] curl::Error),
3333

3434
#[error("failed to convert header value of `{name}` to string: {bytes:?}")]
@@ -212,6 +212,7 @@ impl WorkerServer {
212212
// Would be nice to set HTTP version via `response.version_mut()`, but `curl` doesn't have it exposed.
213213
let extensions = Extensions {
214214
client_ip: easy.primary_ip().ok().flatten().map(str::to_string),
215+
effective_url: easy.effective_url().ok().flatten().map(str::to_string),
215216
};
216217
response.extensions_mut().insert(extensions);
217218
let _ = sender.send(result.map(|()| response).map_err(Into::into));
@@ -340,10 +341,12 @@ impl Handler for Collector {
340341
#[derive(Clone)]
341342
struct Extensions {
342343
client_ip: Option<String>,
344+
effective_url: Option<String>,
343345
}
344346

345347
pub trait ResponsePartsExtensions {
346348
fn client_ip(&self) -> Option<&str>;
349+
fn effective_url(&self) -> Option<&str>;
347350
}
348351

349352
impl ResponsePartsExtensions for http::response::Parts {
@@ -352,6 +355,12 @@ impl ResponsePartsExtensions for http::response::Parts {
352355
.get::<Extensions>()
353356
.and_then(|extensions| extensions.client_ip.as_deref())
354357
}
358+
359+
fn effective_url(&self) -> Option<&str> {
360+
self.extensions
361+
.get::<Extensions>()
362+
.and_then(|extensions| extensions.effective_url.as_deref())
363+
}
355364
}
356365

357366
impl ResponsePartsExtensions for Response {
@@ -360,6 +369,12 @@ impl ResponsePartsExtensions for Response {
360369
.get::<Extensions>()
361370
.and_then(|extensions| extensions.client_ip.as_deref())
362371
}
372+
373+
fn effective_url(&self) -> Option<&str> {
374+
self.extensions()
375+
.get::<Extensions>()
376+
.and_then(|extensions| extensions.effective_url.as_deref())
377+
}
363378
}
364379

365380
/// Splits HTTP `HEADER: VALUE` to a tuple.

tests/testsuite/registry_auth.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ Caused by:
234234
[NOTE] the token does not include an authentication scheme
235235
236236
Caused by:
237-
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401
237+
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json` ([..]), got 401
238238
body:
239239
Unauthorized message from server.
240240
@@ -276,7 +276,7 @@ Caused by:
276276
[NOTE] the token does not include an authentication scheme
277277
278278
Caused by:
279-
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401
279+
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json` ([..]), got 401
280280
body:
281281
Unauthorized message from server.
282282
@@ -321,7 +321,7 @@ Caused by:
321321
[NOTE] the token does not include an authentication scheme
322322
323323
Caused by:
324-
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401
324+
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json` ([..]), got 401
325325
body:
326326
Unauthorized message from server.
327327
@@ -416,7 +416,7 @@ Caused by:
416416
[NOTE] the token does not include an authentication scheme
417417
418418
Caused by:
419-
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401
419+
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json` ([..]), got 401
420420
body:
421421
Unauthorized message from server.
422422
@@ -480,7 +480,7 @@ Caused by:
480480
or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN
481481
482482
Caused by:
483-
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401
483+
failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json` ([..]), got 401
484484
body:
485485
Unauthorized message from server.
486486

0 commit comments

Comments
 (0)