mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Implement RFC 7231 compliant relative URI and fragment handling in redirects
This commit is contained in:
parent
fd5da46a83
commit
e1d9956bcd
1 changed files with 31 additions and 10 deletions
|
@ -16,6 +16,7 @@ use reqwest_retry::{
|
||||||
DefaultRetryableStrategy, RetryTransientMiddleware, Retryable, RetryableStrategy,
|
DefaultRetryableStrategy, RetryTransientMiddleware, Retryable, RetryableStrategy,
|
||||||
};
|
};
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
use url::ParseError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use uv_auth::{AuthMiddleware, UrlAuthPolicies};
|
use uv_auth::{AuthMiddleware, UrlAuthPolicies};
|
||||||
|
@ -520,12 +521,15 @@ impl RedirectClientWithMiddleware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a request. If the response is a 302 redirect, executes the
|
/// Executes a request. If the response is a redirect (one of HTTP 301, 302, 307, or 308), the
|
||||||
/// request again with the redirect location URL (up to a maximum number
|
/// request is executed again with the redirect location URL (up to a maximum number of
|
||||||
/// of redirects).
|
/// redirects).
|
||||||
///
|
///
|
||||||
/// Unlike the built-in reqwest redirect policies, this sends the
|
/// Unlike the built-in reqwest redirect policies, this sends the redirect request through the
|
||||||
/// redirect request through the entire middleware pipeline again.
|
/// entire middleware pipeline again.
|
||||||
|
///
|
||||||
|
/// See RFC 7231 7.1.2 <https://www.rfc-editor.org/rfc/rfc7231#section-7.1.2> for details on
|
||||||
|
/// redirect semantics.
|
||||||
async fn execute_with_redirect_handling(
|
async fn execute_with_redirect_handling(
|
||||||
&self,
|
&self,
|
||||||
req: Request,
|
req: Request,
|
||||||
|
@ -536,6 +540,7 @@ impl RedirectClientWithMiddleware {
|
||||||
let max_redirects = 10;
|
let max_redirects = 10;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let request_url = request.url().clone();
|
||||||
let result = self
|
let result = self
|
||||||
.client
|
.client
|
||||||
.execute(request.try_clone().expect("HTTP request must be cloneable"))
|
.execute(request.try_clone().expect("HTTP request must be cloneable"))
|
||||||
|
@ -568,11 +573,27 @@ impl RedirectClientWithMiddleware {
|
||||||
"Invalid HTTP {status} 'Location' value: must only contain visible ascii characters"
|
"Invalid HTTP {status} 'Location' value: must only contain visible ascii characters"
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let redirect_url = Url::parse(location).map_err(|err| {
|
|
||||||
reqwest_middleware::Error::Middleware(anyhow!(
|
let mut redirect_url = match Url::parse(location) {
|
||||||
"Invalid HTTP {status} 'Location' value `{location}`: {err}"
|
Ok(url) => url,
|
||||||
))
|
// Per RFC 7231, URLs should be resolved against the request URL.
|
||||||
})?;
|
Err(ParseError::RelativeUrlWithoutBase) => request_url.join(location).map_err(|err| {
|
||||||
|
reqwest_middleware::Error::Middleware(anyhow!(
|
||||||
|
"Invalid HTTP {status} 'Location' value `{location}` relative to `{request_url}`: {err}"
|
||||||
|
))
|
||||||
|
})?,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(reqwest_middleware::Error::Middleware(anyhow!(
|
||||||
|
"Invalid HTTP {status} 'Location' value `{location}`: {err}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Per RFC 7231, fragments must be propagated
|
||||||
|
if let Some(fragment) = request_url.fragment() {
|
||||||
|
redirect_url.set_fragment(Some(fragment));
|
||||||
|
}
|
||||||
|
|
||||||
debug!("Received HTTP {status} to {redirect_url}");
|
debug!("Received HTTP {status} to {redirect_url}");
|
||||||
*request.url_mut() = redirect_url;
|
*request.url_mut() = redirect_url;
|
||||||
redirects += 1;
|
redirects += 1;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue