diff --git a/Cargo.lock b/Cargo.lock index 3b46067..b05e672 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,7 +51,7 @@ dependencies = [ [[package]] name = "async_http_range_reader" -version = "0.9.1" +version = "0.9.2" dependencies = [ "assert_matches", "async_zip", diff --git a/Cargo.toml b/Cargo.toml index 3ff7d89..9b04f5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "async_http_range_reader" authors = ["Bas Zalmstra "] -version = "0.9.1" +version = "0.9.2" edition = "2021" description = "A library for streaming reading of files over HTTP using range requests" license = "MIT" @@ -15,7 +15,9 @@ http-content-range = "0.2.0" itertools = "0.13.0" bisection = "0.1.0" memmap2 = "0.9.0" -reqwest = { version = "0.12.3", default-features = false, features = ["stream"] } +reqwest = { version = "0.12.3", default-features = false, features = [ + "stream", +] } reqwest-middleware = "0.4.0" tokio = { version = "1.33.0", default-features = false } tokio-stream = { version = "0.1.14", features = ["sync"] } @@ -24,10 +26,18 @@ thiserror = "1.0.50" tracing = "0.1.40" [dev-dependencies] -axum = { version = "0.7.5", default-features = false, features = ["tokio", "http1"] } -tokio = { version = "1.33.0", default-features = false, features = ["macros", "test-util"] } +axum = { version = "0.7.5", default-features = false, features = [ + "tokio", + "http1", +] } +tokio = { version = "1.33.0", default-features = false, features = [ + "macros", + "test-util", +] } tower-http = { version = "0.6.1", default-features = false, features = ["fs"] } -async_zip = { version = "0.0.17", default-features = false, features = ["tokio"] } +async_zip = { version = "0.0.17", default-features = false, features = [ + "tokio", +] } assert_matches = "1.5.0" rstest = { version = "0.23.0" } url = { version = "2.4.1" } diff --git a/src/lib.rs b/src/lib.rs index 8a3c6e3..e679b2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -189,7 +189,14 @@ impl AsyncHttpRangeReader { .headers(extra_headers) .send() .await - .and_then(error_for_status) + .map_err(Arc::new) + .map_err(AsyncHttpRangeReaderError::HttpError)?; + + if tail_response.status() == reqwest::StatusCode::RANGE_NOT_SATISFIABLE { + return Err(AsyncHttpRangeReaderError::HttpRangeRequestUnsupported); + } + + let tail_response = error_for_status(tail_response) .map_err(Arc::new) .map_err(AsyncHttpRangeReaderError::HttpError)?; Ok(tail_response) @@ -655,6 +662,11 @@ mod static_directory_server; #[cfg(test)] mod test { use super::*; + use axum::{ + http::{HeaderMap as AxumHeaderMap, StatusCode as AxumStatusCode}, + routing::get, + Router, + }; use crate::static_directory_server::StaticDirectoryServer; use assert_matches::assert_matches; use async_zip::tokio::read::seek::ZipFileReader; @@ -854,4 +866,33 @@ mod test { err, AsyncHttpRangeReaderError::HttpError(err) if err.status() == Some(StatusCode::NOT_FOUND) ); } + + #[tokio::test] + async fn test_negative_range_416_is_unsupported() { + async fn handler(headers: AxumHeaderMap) -> AxumStatusCode { + if headers.contains_key(reqwest::header::RANGE) { + AxumStatusCode::RANGE_NOT_SATISFIABLE + } else { + AxumStatusCode::OK + } + } + + let app = Router::new().route("/", get(handler)); + let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0)).await.unwrap(); + let addr = listener.local_addr().unwrap(); + tokio::spawn(async move { + let _ = axum::serve(listener, app).await; + }); + + let err = AsyncHttpRangeReader::new( + Client::new(), + Url::parse(&format!("http://127.0.0.1:{}/", addr.port())).unwrap(), + CheckSupportMethod::NegativeRangeRequest(8192), + HeaderMap::default(), + ) + .await + .expect_err("expected an unsupported range request error"); + + assert_matches!(err, AsyncHttpRangeReaderError::HttpRangeRequestUnsupported); + } }