Skip to content

Commit 6e8bc99

Browse files
authored
Fix incorrect 206-partial handling (#22)
Per [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests), `Accept-Ranges` response is returned ONLY when using HEAD request without the `Range` header. When asking for a specific range, the server must reply with 206 status.
1 parent c4f2c25 commit 6e8bc99

File tree

2 files changed

+10
-14
lines changed

2 files changed

+10
-14
lines changed

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub enum HttpError {
4141
ResponseBodyTooLong(usize, usize),
4242
#[error("HTTP error {0}")]
4343
Http(#[from] reqwest::Error),
44+
#[error("{0}")]
45+
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
4446
}
4547

4648
// This is required because thiserror #[from] does not support two-level conversion.

src/http.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use async_trait::async_trait;
22
use bytes::Bytes;
3-
use reqwest::header::{HeaderValue, ACCEPT_RANGES, RANGE};
4-
use reqwest::{Client, IntoUrl, Method, Request, Url};
3+
use reqwest::header::{HeaderValue, RANGE};
4+
use reqwest::{Client, IntoUrl, Method, Request, StatusCode, Url};
55

66
use crate::async_reader::AsyncBackend;
77
use crate::error::{Error, HttpError};
@@ -20,8 +20,6 @@ impl HttpBackend {
2020
}
2121
}
2222

23-
static VALID_ACCEPT_RANGES: HeaderValue = HeaderValue::from_static("bytes");
24-
2523
#[async_trait]
2624
impl AsyncBackend for HttpBackend {
2725
async fn read_exact(&self, offset: usize, length: usize) -> Result<Bytes, Error> {
@@ -35,23 +33,19 @@ impl AsyncBackend for HttpBackend {
3533
}
3634

3735
async fn read(&self, offset: usize, length: usize) -> Result<Bytes, Error> {
38-
let mut req = Request::new(Method::GET, self.pmtiles_url.clone());
39-
let range_header = req
40-
.headers_mut()
41-
.entry(RANGE)
42-
.or_insert(HeaderValue::from_static(""));
4336
let end = offset + length - 1;
44-
// This .unwrap() should be safe, since `offset` and `end` will always be valid.
45-
*range_header = HeaderValue::from_str(format!("bytes={offset}-{end}").as_str()).unwrap();
37+
let range = format!("bytes={offset}-{end}");
38+
let range = HeaderValue::try_from(range).map_err(HttpError::from)?;
4639

47-
let response = self.client.execute(req).await?.error_for_status()?;
40+
let mut req = Request::new(Method::GET, self.pmtiles_url.clone());
41+
req.headers_mut().insert(RANGE, range);
4842

49-
if response.headers().get(ACCEPT_RANGES) != Some(&VALID_ACCEPT_RANGES) {
43+
let response = self.client.execute(req).await?.error_for_status()?;
44+
if response.status() != StatusCode::PARTIAL_CONTENT {
5045
return Err(HttpError::RangeRequestsUnsupported.into());
5146
}
5247

5348
let response_bytes = response.bytes().await?;
54-
5549
if response_bytes.len() > length {
5650
Err(HttpError::ResponseBodyTooLong(response_bytes.len(), length).into())
5751
} else {

0 commit comments

Comments
 (0)