apache / apache/arrow-rs-object-store
Errors on 200 Response for entire-object range request
- Dominant language
- Rust
- Stars
- 322
- Forks
- 212
- Avg merge
- 5d 2h
- Merged PRs (30d)
- 10
Description
**Describe the bug**
When issuing a range request matching the entire content of an object_store, the object_store refuses an HTTP/200 response containing range headers.
Trace:
```
GET /data.bin HTTP/1.1
range: bytes=0-11
accept: */*
user-agent: object_store/0.14.1
host: 127.0.0.1:8000
HTTP/1.1 200 OK
content-length: 12
content-range: bytes 0-11/12
content-type: application/octet-stream
last-modified: Fri, 17 Jul 2026 08:44:44 GMT
etag: "2c6d9c:c:6a59eb7c:2f8f2e0c"
content-disposition: attachment; filename="data.bin"
accept-ranges: bytes
date: Fri, 17 Jul 2026 08:47:00 GMT
```
Relevant code section likely here:
https://github.com/apache/arrow-rs-object-store/blob/c7316d29face118e7409eead0cda098f38589428/src/http/client.rs#L404-L410
The request/response is legal as far as I can tell. Minor, the content-range in the response should be ignored per the RFC.
**To Reproduce**
This uses miniserve, which uses Actix under the hood.
```rust
//! HTTP store rejects a range request that covers the entire object when the
//! server (miniserve, or anything actix-files based) replies `200 OK` with the
//! full body instead of `206 Partial Content`.
//!
//! Setup:
//! mkdir -p /tmp/serve && printf 'hello world!' > /tmp/serve/data.bin
//! miniserve --interfaces 127.0.0.1 --port 8000 /tmp/serve &
use object_store::http::HttpBuilder;
use object_store::path::Path;
use object_store::{ClientOptions, GetOptions, GetRange, ObjectStore, ObjectStoreExt};
#[tokio::main]
async fn main() {
let store = HttpBuilder::new()
.with_url("http://127.0.0.1:8000")
.with_client_options(ClientOptions::new().with_allow_http(true))
.build()
.unwrap();
let path = Path::from("data.bin");
let size = store.head(&path).await.unwrap().size;
println!("object size: {size}");
for range in [0..size - 1, 0..size] {
let opts = GetOptions {
range: Some(GetRange::Bounded(range.clone())),
..Default::default()
};
match store.get_opts(&path, opts).await {
Ok(r) => println!("range {range:?} -> ok, {} bytes", r.bytes().await.unwrap().len()),
Err(e) => println!("range {range:?} -> ERROR: {e}"),
}
}
}
```
gives:
(a) on miniserve:
```
object size: 12
range 0..11 -> ok, 11 bytes
range 0..12 -> ERROR: Operation not supported: Range request not supported by data.bin
```
(b) on npx http-server:
```
object size: 12
range 0..11 -> ok, 11 bytes
range 0..12 -> ok, 12 bytes
```
**Expected behavior**
It should accept a 200 response on a full-object range request.
**Additional context**
Master issue ref:
https://github.com/pola-rs/polars/issues/28400
Per AI analysis:
server | partial 0-10 | full-file 0-11
-- | -- | --
nginx 1.24 | 206 | 206, Content-Range: bytes 0-11/12
node http-server (npm) | 206 | 206
miniserve / actix-files | 206 | 200 (+ stray Content-Range)
python3 -m http.server | 200 (no range support at all) | 200
Contributor guide
Research direction
Start in src/http/client.rs around lines 404-410, where the HTTP response to a range request is validated. Run the provided Rust reproduction against miniserve and npx http-server to compare the responses. Done means a full-object range request with a legal 200 response succeeds while normal range handling remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100