Images: a large image's missing-chunks question fits 750579ecc721
POST /flow/images/missing had axum's 2 MB body limit, though the hub
accepts up to 200,000 chunk names per question (about 13 MB), so pushing
an 11 GB image failed with 413. The route's limit now fits MAX_ASK.
closes #147
Author Andy McCall @andymccall <[email protected] >
When 23 days ago · 2026-10-12 19:32 UTC
Signature ✔ signed by Andy McCall
Hash 750579ecc7217759272630f8d070c7965c6ddaf939f1a42371162c96297e0b2e
Depends on 42fa95540468 92a797b20912 a46d15e72101 b6b7e4cf6b24 d23464c0ac60
Depended on by 951aed50c2f5 8501fd5815b9 e9b9619cd857
In bookmarks api-unknown-fields cancel-landed-runs close-proposal-140 diffusion-pin-bookmark draft-ready main oh-comment-body-file proposal-list-fast
In tags v0.5.0 v0.5.1 v0.5.2 v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits 4 atoms, 0 tree operations, 3 files
Browse the tree at this change
crates/hub/src/api/images.rs crates/hub/src/api/mod.rs crates/hub/tests/flow.rs
modified
crates/hub/src/api/images.rs
+3 −0
…
13 unchanged lines
14
14
15
15
/// Most chunk names one `missing` question may ask about.
16
16
const MAX_ASK: usize = 200_000;
17
+
/// The body that many names make: 64 hex digits, quotes and a comma each,
18
+
/// with room to spare (#147: the 2 MB default refused an 11 GB image's).
19
+
pub const MAX_ASK_BYTES: usize = MAX_ASK * 80;
17
20
18
21
/// Who sends images: an account whose token may write repositories.
19
22
async fn sender(app: &Shared, headers: &HeaderMap) -> Result<String, ApiError> {
…
102 unchanged lines
modified
crates/hub/src/api/mod.rs
+1 −1
…
43 unchanged lines
44
44
.fallback(no_route)
45
45
.route("/flow/claim", post(crate::flow::claim))
46
46
.route("/flow/runners/register", post(crate::flow::register))
47
-
.route("/flow/images/missing", post(images::missing))
47
+
.route("/flow/images/missing", post(images::missing).layer(axum::extract::DefaultBodyLimit::max(images::MAX_ASK_BYTES)) )
48
48
.route("/flow/swarm/announce", post(crate::swarm::announce))
49
49
.route("/flow/images/chunks/{hash}", axum::routing::put(images::put_chunk).get(images::get_chunk))
50
50
.route(
…
2357 unchanged lines
modified
crates/hub/tests/flow.rs
+6 −0
…
2642 unchanged lines
2643
2643
}
2644
2644
let (_, body) = call(&h.app, "POST", "/api/v1/flow/images/missing", Some("site"), Some(ask)).await;
2645
2645
assert!(body.contains("\"missing\":[]"), "{body}");
2646
+
// A large image's question fits (#147): 60,000 names, ~4 MB of JSON.
2647
+
let many: Vec<String> = (0..60_000u32).map(|i| blake3::hash(&i.to_le_bytes()).to_hex().to_string()).collect();
2648
+
let (st, body) =
2649
+
call(&h.app, "POST", "/api/v1/flow/images/missing", Some("site"), Some(serde_json::json!({ "chunks": many }).to_string())).await;
2650
+
assert_eq!(st, StatusCode::OK, "{}", &body[..body.len().min(200)]);
2651
+
assert_eq!(serde_json::from_str::<serde_json::Value>(&body).unwrap()["missing"].as_array().unwrap().len(), 60_000);
2646
2652
// A manifest naming another hash is refused; the right one is kept.
2647
2653
let mut wrong = m.clone();
2648
2654
wrong.hash = "ab".repeat(32);
…
377 unchanged lines