Skip to content

Commit 601d775

Browse files
authored
Fix integer underflow in try_range_response for empty files (#3566)
1 parent 7fd17ce commit 601d775

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed

Cargo.lock

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

axum-extra/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ hyper = "1.0.0"
146146
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "multipart"] }
147147
serde = { version = "1.0.221", features = ["derive"] }
148148
serde_json = "1.0.71"
149+
tempfile = "3.23.0"
149150
tokio = { version = "1.14", features = ["full"] }
150151
tower = { version = "0.5.2", features = ["util"] }
151152
tower-http = { version = "0.6.0", features = ["map-response-body", "timeout"] }

axum-extra/src/response/file_stream.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ where
191191
let metadata = file.metadata().await?;
192192
let total_size = metadata.len();
193193

194+
if total_size == 0 {
195+
return Ok((StatusCode::RANGE_NOT_SATISFIABLE, "Range Not Satisfiable").into_response());
196+
}
197+
194198
if end == 0 {
195199
end = total_size - 1;
196200
}
@@ -596,4 +600,52 @@ mod tests {
596600
}
597601
Some((start, end))
598602
}
603+
604+
#[tokio::test]
605+
async fn response_range_empty_file() -> Result<(), Box<dyn std::error::Error>> {
606+
let file = tempfile::NamedTempFile::new()?;
607+
file.as_file().set_len(0)?;
608+
let path = file.path().to_owned();
609+
610+
let app = Router::new().route(
611+
"/range_empty",
612+
get(move |headers: HeaderMap| {
613+
let path = path.clone();
614+
async move {
615+
let range_header = headers
616+
.get(header::RANGE)
617+
.and_then(|value| value.to_str().ok());
618+
619+
let (start, end) = if let Some(range) = range_header {
620+
if let Some(range) = parse_range_header(range) {
621+
range
622+
} else {
623+
return (StatusCode::RANGE_NOT_SATISFIABLE, "Invalid Range")
624+
.into_response();
625+
}
626+
} else {
627+
(0, 0)
628+
};
629+
630+
FileStream::<ReaderStream<File>>::try_range_response(path, start, end)
631+
.await
632+
.unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response())
633+
}
634+
}),
635+
);
636+
637+
let response = app
638+
.oneshot(
639+
Request::builder()
640+
.uri("/range_empty")
641+
.header(header::RANGE, "bytes=0-")
642+
.body(Body::empty())
643+
.unwrap(),
644+
)
645+
.await
646+
.unwrap();
647+
648+
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
649+
Ok(())
650+
}
599651
}

0 commit comments

Comments
 (0)