diff --git a/Cargo.lock b/Cargo.lock index 995b6eb0..49f35ebc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,6 +390,7 @@ dependencies = [ "serde_html_form", "serde_json", "serde_path_to_error", + "tempfile", "tokio", "tokio-stream", "tokio-util", diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 96d48bd7..219d073c 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -120,6 +120,7 @@ hyper = "1.0.0" reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "multipart"] } serde = { version = "1.0.221", features = ["derive"] } serde_json = "1.0.71" +tempfile = "3.23.0" tokio = { version = "1.14", features = ["full"] } tower = { version = "0.5.2", features = ["util"] } tower-http = { version = "0.6.0", features = ["map-response-body", "timeout"] } diff --git a/axum-extra/src/response/file_stream.rs b/axum-extra/src/response/file_stream.rs index da9d0d78..c36fe61b 100644 --- a/axum-extra/src/response/file_stream.rs +++ b/axum-extra/src/response/file_stream.rs @@ -191,6 +191,10 @@ where let metadata = file.metadata().await?; let total_size = metadata.len(); + if total_size == 0 { + return Ok((StatusCode::RANGE_NOT_SATISFIABLE, "Range Not Satisfiable").into_response()); + } + if end == 0 { end = total_size - 1; } @@ -596,4 +600,52 @@ mod tests { } Some((start, end)) } + + #[tokio::test] + async fn response_range_empty_file() -> Result<(), Box> { + let file = tempfile::NamedTempFile::new()?; + file.as_file().set_len(0)?; + let path = file.path().to_owned(); + + let app = Router::new().route( + "/range_empty", + get(move |headers: HeaderMap| { + let path = path.clone(); + async move { + let range_header = headers + .get(header::RANGE) + .and_then(|value| value.to_str().ok()); + + let (start, end) = if let Some(range) = range_header { + if let Some(range) = parse_range_header(range) { + range + } else { + return (StatusCode::RANGE_NOT_SATISFIABLE, "Invalid Range") + .into_response(); + } + } else { + (0, 0) + }; + + FileStream::>::try_range_response(path, start, end) + .await + .unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response()) + } + }), + ); + + let response = app + .oneshot( + Request::builder() + .uri("/range_empty") + .header(header::RANGE, "bytes=0-") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); + Ok(()) + } }