Browse Source

Fix chunk manifest expansion to gracefully handle invalid JSON

When a needle has the chunk manifest flag but its data isn't valid
JSON (e.g., placeholder payload), fall through to return raw data
instead of returning 500. This fixes TestBatchDeleteRejectsChunkManifestNeedles.
rust-volume-server
Chris Lu 4 days ago
parent
commit
98c32f7396
  1. 23
      seaweed-volume/src/server/handlers.rs

23
seaweed-volume/src/server/handlers.rs

@ -258,7 +258,10 @@ async fn get_or_head_handler_inner(
// Chunk manifest expansion // Chunk manifest expansion
let bypass_cm = query.cm.as_deref() == Some("false"); let bypass_cm = query.cm.as_deref() == Some("false");
if n.is_chunk_manifest() && !bypass_cm { if n.is_chunk_manifest() && !bypass_cm {
return expand_chunk_manifest(&state, &n, &headers, &method);
if let Some(resp) = try_expand_chunk_manifest(&state, &n, &headers, &method) {
return resp;
}
// If manifest expansion fails (invalid JSON etc.), fall through to raw data
} }
// Build ETag // Build ETag
@ -959,20 +962,20 @@ struct ChunkInfo {
size: i64, size: i64,
} }
/// Expand a chunk manifest needle: read each chunk and concatenate.
fn expand_chunk_manifest(
/// Try to expand a chunk manifest needle. Returns None if manifest can't be parsed.
fn try_expand_chunk_manifest(
state: &Arc<VolumeServerState>, state: &Arc<VolumeServerState>,
n: &Needle, n: &Needle,
_headers: &HeaderMap, _headers: &HeaderMap,
method: &Method, method: &Method,
) -> Response {
) -> Option<Response> {
let data = if n.is_compressed() { let data = if n.is_compressed() {
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use std::io::Read as _; use std::io::Read as _;
let mut decoder = GzDecoder::new(&n.data[..]); let mut decoder = GzDecoder::new(&n.data[..]);
let mut decompressed = Vec::new(); let mut decompressed = Vec::new();
if decoder.read_to_end(&mut decompressed).is_err() { if decoder.read_to_end(&mut decompressed).is_err() {
return (StatusCode::INTERNAL_SERVER_ERROR, "failed to decompress manifest").into_response();
return None;
} }
decompressed decompressed
} else { } else {
@ -981,7 +984,7 @@ fn expand_chunk_manifest(
let manifest: ChunkManifest = match serde_json::from_slice(&data) { let manifest: ChunkManifest = match serde_json::from_slice(&data) {
Ok(m) => m, Ok(m) => m,
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("invalid chunk manifest: {}", e)).into_response(),
Err(_) => return None,
}; };
// Read and concatenate all chunks // Read and concatenate all chunks
@ -990,7 +993,7 @@ fn expand_chunk_manifest(
for chunk in &manifest.chunks { for chunk in &manifest.chunks {
let (chunk_vid, chunk_nid, chunk_cookie) = match parse_url_path(&chunk.fid) { let (chunk_vid, chunk_nid, chunk_cookie) = match parse_url_path(&chunk.fid) {
Some(p) => p, Some(p) => p,
None => return (StatusCode::INTERNAL_SERVER_ERROR, format!("invalid chunk fid: {}", chunk.fid)).into_response(),
None => return Some((StatusCode::INTERNAL_SERVER_ERROR, format!("invalid chunk fid: {}", chunk.fid)).into_response()),
}; };
let mut chunk_needle = Needle { let mut chunk_needle = Needle {
id: chunk_nid, id: chunk_nid,
@ -999,7 +1002,7 @@ fn expand_chunk_manifest(
}; };
match store.read_volume_needle(chunk_vid, &mut chunk_needle) { match store.read_volume_needle(chunk_vid, &mut chunk_needle) {
Ok(_) => {} Ok(_) => {}
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("read chunk {}: {}", chunk.fid, e)).into_response(),
Err(e) => return Some((StatusCode::INTERNAL_SERVER_ERROR, format!("read chunk {}: {}", chunk.fid, e)).into_response()),
} }
let chunk_data = if chunk_needle.is_compressed() { let chunk_data = if chunk_needle.is_compressed() {
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
@ -1034,10 +1037,10 @@ fn expand_chunk_manifest(
if *method == Method::HEAD { if *method == Method::HEAD {
response_headers.insert(header::CONTENT_LENGTH, result.len().to_string().parse().unwrap()); response_headers.insert(header::CONTENT_LENGTH, result.len().to_string().parse().unwrap());
return (StatusCode::OK, response_headers).into_response();
return Some((StatusCode::OK, response_headers).into_response());
} }
(StatusCode::OK, response_headers, result).into_response()
Some((StatusCode::OK, response_headers, result).into_response())
} }
// ============================================================================ // ============================================================================

Loading…
Cancel
Save