Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions tower-http/src/services/fs/serve_dir/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use http::{
HeaderValue, Request, Response, StatusCode,
};
use http_body_util::{BodyExt, Empty, Full};
use http_range_header::RangeUnsatisfiableError;
use pin_project_lite::pin_project;
use std::{
convert::Infallible,
Expand Down Expand Up @@ -297,6 +298,14 @@ fn build_response(output: FileOpened) -> Response<ResponseBody> {
}
}

Some(Err(RangeUnsatisfiableError::OverlappingRanges)) => builder
.header(header::CONTENT_RANGE, format!("bytes */{}", size))
.status(StatusCode::RANGE_NOT_SATISFIABLE)
.body(body_from_bytes(Bytes::from(
"Cannot serve multipart range requests",
)))
.unwrap(),

Some(Err(_)) => builder
.header(header::CONTENT_RANGE, format!("bytes */{}", size))
.status(StatusCode::RANGE_NOT_SATISFIABLE)
Expand Down
11 changes: 9 additions & 2 deletions tower-http/src/services/fs/serve_dir/open_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,15 @@ fn try_parse_range(
file_size: u64,
) -> Option<Result<Vec<RangeInclusive<u64>>, RangeUnsatisfiableError>> {
maybe_range_ref.map(|header_value| {
http_range_header::parse_range_header(header_value)
.and_then(|first_pass| first_pass.validate(file_size))
let parsed = http_range_header::parse_range_header(header_value)?;

if parsed.ranges.len() > 1 {
// ServeDir/ServeFile do not support multipart responses, so reject
// multi-range requests before validate() runs overlap checks.
return Err(RangeUnsatisfiableError::OverlappingRanges);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem like quite the right error, this refers to overlapping ranges within a multipart request. Meanwhile we don't support them at all.

Can we have a local error type instead that more clearly denotes what's happening, if we preserve this behavior?

}

parsed.validate(file_size)
})
}

Expand Down
21 changes: 21 additions & 0 deletions tower-http/src/services/fs/serve_dir/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,27 @@ async fn read_partial_errs_on_bad_range() {
)
}

#[tokio::test]
async fn multipart_range_valid_returns_multipart_error_body() {
let svc = ServeDir::new("..");
let req = Request::builder()
.uri("/README.md")
.header("Range", "bytes=0-0,2-2")
.body(Body::empty())
.unwrap();
let res = svc.oneshot(req).await.unwrap();

assert_eq!(res.status(), StatusCode::RANGE_NOT_SATISFIABLE);
let file_contents = std::fs::read("../README.md").unwrap();
assert_eq!(
res.headers()["content-range"],
&format!("bytes */{}", file_contents.len())
);

let body = body_into_text(res.into_body()).await;
assert_eq!(body, "Cannot serve multipart range requests");
}

#[tokio::test]
async fn accept_encoding_identity() {
let svc = ServeDir::new("..");
Expand Down
Loading