Handle broken range request implementations (#18780)
This updates async_http_range_reader to v0.11.0 to add the missing range request bounds validation: https://github.com/astral-sh/async_http_range_reader/pull/8. An open question is how we want to behave when the server has an incorrect range request implementation (while advertising range request support). In the current implementation, it warns with the index URL, so that the user is aware that the massive slowdown is caused by a server advertising broken features. Also removes a dependency where the corresponding repo was deleted. Fixes https://github.com/astral-sh/uv/issues/18316
This commit is contained in:
Generated
+2
-9
@@ -326,12 +326,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "astral_async_http_range_reader"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab9a05148c7b3e85c17806fef4b2889c44f1a60575c5da9e9be80ce13227185"
|
||||
checksum = "4a8647866aee8d9707ae6ccc35205803a6df47c0ba83c5339ea6061b79131e4f"
|
||||
dependencies = [
|
||||
"astral-reqwest-middleware",
|
||||
"bisection",
|
||||
"futures",
|
||||
"http-content-range",
|
||||
"itertools 0.14.0",
|
||||
@@ -548,12 +547,6 @@ version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bisection"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "021e079a1bab0ecce6cf4b4b74c0c37afa4a697136eb3b127875c84a8f04a8c3"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ async-compression = { version = "0.4.12", features = [
|
||||
"zstd",
|
||||
] }
|
||||
async-trait = { version = "0.1.82" }
|
||||
async_http_range_reader = { version = "0.10.0", package = "astral_async_http_range_reader" }
|
||||
async_http_range_reader = { version = "0.11.0", package = "astral_async_http_range_reader" }
|
||||
async_zip = { version = "0.0.17", package = "astral_async_zip", features = [
|
||||
"bzip2",
|
||||
"deflate",
|
||||
|
||||
@@ -9,13 +9,13 @@ use reqwest::Response;
|
||||
use serde::Deserialize;
|
||||
use tracing::warn;
|
||||
|
||||
use uv_cache::Error as CacheError;
|
||||
use uv_distribution_filename::{WheelFilename, WheelFilenameError};
|
||||
use uv_normalize::PackageName;
|
||||
use uv_redacted::DisplaySafeUrl;
|
||||
|
||||
use crate::middleware::OfflineError;
|
||||
use crate::{FlatIndexError, html};
|
||||
use uv_cache::Error as CacheError;
|
||||
use uv_distribution_filename::{WheelFilename, WheelFilenameError};
|
||||
use uv_distribution_types::IndexUrl;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_redacted::DisplaySafeUrl;
|
||||
|
||||
/// RFC 9457 Problem Details for HTTP APIs
|
||||
///
|
||||
@@ -242,7 +242,11 @@ impl Error {
|
||||
}
|
||||
|
||||
/// Returns `true` if the error is due to the server not supporting HTTP range requests.
|
||||
pub fn is_http_range_requests_unsupported(&self) -> bool {
|
||||
pub fn is_http_range_requests_unsupported(
|
||||
&self,
|
||||
url: &DisplaySafeUrl,
|
||||
index: Option<&IndexUrl>,
|
||||
) -> bool {
|
||||
match &*self.kind {
|
||||
// The server doesn't support range requests (as reported by the `HEAD` check).
|
||||
ErrorKind::AsyncHttpRangeReader(
|
||||
@@ -261,6 +265,25 @@ impl Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The server advertises range request support, but doesn't implement it correctly.
|
||||
ErrorKind::AsyncHttpRangeReader(
|
||||
_,
|
||||
AsyncHttpRangeReaderError::RangeMismatch { .. }
|
||||
| AsyncHttpRangeReaderError::ResponseTooShort { .. }
|
||||
| AsyncHttpRangeReaderError::ResponseTooLong { .. },
|
||||
) => {
|
||||
let url = if let Some(index) = index {
|
||||
index.url()
|
||||
} else {
|
||||
url
|
||||
};
|
||||
warn!(
|
||||
"Invalid range request response from server that declares HTTP range request \
|
||||
support, falling back to streaming: {url}"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The server returned a "Method Not Allowed" error, indicating it doesn't support
|
||||
// HEAD requests, so we can't check for range requests.
|
||||
ErrorKind::WrappedReqwestError(_, err) => {
|
||||
@@ -295,14 +318,31 @@ impl Error {
|
||||
// The server doesn't support range requests, but we only discovered this while
|
||||
// unzipping due to erroneous server behavior.
|
||||
ErrorKind::Zip(_, ZipError::UpstreamReadError(err)) => {
|
||||
if let Some(inner) = err.get_ref() {
|
||||
if let Some(inner) = inner.downcast_ref::<AsyncHttpRangeReaderError>() {
|
||||
if matches!(
|
||||
inner,
|
||||
AsyncHttpRangeReaderError::HttpRangeRequestUnsupported
|
||||
) {
|
||||
if let Some(inner) = err.get_ref()
|
||||
&& let Some(range_reader_error) =
|
||||
inner.downcast_ref::<AsyncHttpRangeReaderError>()
|
||||
{
|
||||
match range_reader_error {
|
||||
AsyncHttpRangeReaderError::HttpRangeRequestUnsupported
|
||||
| AsyncHttpRangeReaderError::ContentLengthMissing
|
||||
| AsyncHttpRangeReaderError::ContentRangeMissing => {
|
||||
return true;
|
||||
}
|
||||
AsyncHttpRangeReaderError::RangeMismatch { .. }
|
||||
| AsyncHttpRangeReaderError::ResponseTooShort { .. }
|
||||
| AsyncHttpRangeReaderError::ResponseTooLong { .. } => {
|
||||
let url = if let Some(index) = index {
|
||||
index.url()
|
||||
} else {
|
||||
url
|
||||
};
|
||||
warn!(
|
||||
"Invalid range request response from server that declares HTTP \
|
||||
range request support, falling back to streaming: {url}"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,7 +1191,7 @@ impl RegistryClient {
|
||||
match result {
|
||||
Ok(metadata) => return Ok(metadata),
|
||||
Err(err) => {
|
||||
if err.is_http_range_requests_unsupported() {
|
||||
if err.is_http_range_requests_unsupported(url, index) {
|
||||
// The range request version failed. Fall back to streaming the file to search
|
||||
// for the METADATA file.
|
||||
warn!("Range requests not supported for {filename}; streaming wheel");
|
||||
|
||||
Reference in New Issue
Block a user