From 358d2c8f852c4868de7951d2fb543da6bea6158c Mon Sep 17 00:00:00 2001 From: chad-loder <26261238+chad-loder@users.noreply.github.com> Date: Sat, 1 Aug 2026 12:33:13 -0700 Subject: [PATCH] fix: release stored response body when a chunk read errors read_body_chunk removed the BODY_STORE entry only when the body completed (the None branch) or was explicitly cancelled. When body.response.frame() yielded Some(Err(_)), the `?` propagated the error without removing the entry, so the StoredBody -- and the wreq::Response it holds (the open connection and pool slot) -- stayed in BODY_STORE indefinitely. A response body that fails mid-stream (reset, read timeout, TLS drop) leaked one socket per failure. Remove the entry on the error path as well, mirroring the done-path cleanup. --- rust/src/store/body_store.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rust/src/store/body_store.rs b/rust/src/store/body_store.rs index a8a309a..dc92d36 100644 --- a/rust/src/store/body_store.rs +++ b/rust/src/store/body_store.rs @@ -50,7 +50,7 @@ fn remove_body(handle: u64) -> Option { pub fn read_body_chunk(handle: u64, _size: usize) -> Result<(Vec, bool)> { let body = get_body(handle)?; - let chunk = runtime().block_on(async { + let chunk = match runtime().block_on(async { let mut body = body.lock().await; loop { match body.response.frame().await { @@ -64,7 +64,15 @@ pub fn read_body_chunk(handle: u64, _size: usize) -> Result<(Vec, bool)> { } } .context("Failed to read response body chunk") - })?; + }) { + Ok(chunk) => chunk, + Err(error) => { + // The body errored, so the connection is unusable — drop the stored body to release it and its + // socket instead of leaking the entry in BODY_STORE (mirrors the done-path cleanup below). + remove_body(handle); + return Err(error); + } + }; let Some(chunk) = chunk else { remove_body(handle);