diff --git a/changelog.md b/changelog.md index 2fb4dc0..5144116 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 2026-03-17 - 4.8.11 - fix(remoteingress-core) +stop data frame send loops promptly when stream cancellation is triggered + +- Use cancellation-aware tokio::select! around data channel sends in both edge and hub stream forwarding paths +- Prevent stalled or noisy shutdown behavior when stream or client cancellation happens while awaiting frame delivery + ## 2026-03-17 - 4.8.10 - fix(remoteingress-core) guard tunnel frame sends with cancellation to prevent async send deadlocks diff --git a/rust/crates/remoteingress-core/src/edge.rs b/rust/crates/remoteingress-core/src/edge.rs index ed06ffd..9aee291 100644 --- a/rust/crates/remoteingress-core/src/edge.rs +++ b/rust/crates/remoteingress-core/src/edge.rs @@ -873,10 +873,11 @@ async fn handle_client_connection( send_window.fetch_sub(n as u32, Ordering::Release); encode_frame_header(&mut buf, stream_id, FRAME_DATA, n); let data_frame = buf[..FRAME_HEADER_SIZE + n].to_vec(); - if tunnel_data_tx.send(data_frame).await.is_err() { - log::warn!("Stream {} data channel closed, closing", stream_id); - break; - } + let sent = tokio::select! { + result = tunnel_data_tx.send(data_frame) => result.is_ok(), + _ = client_token.cancelled() => false, + }; + if !sent { break; } } Err(_) => break, } diff --git a/rust/crates/remoteingress-core/src/hub.rs b/rust/crates/remoteingress-core/src/hub.rs index 5f18d14..dfe7185 100644 --- a/rust/crates/remoteingress-core/src/hub.rs +++ b/rust/crates/remoteingress-core/src/hub.rs @@ -501,10 +501,11 @@ async fn handle_hub_frame( send_window.fetch_sub(n as u32, Ordering::Release); encode_frame_header(&mut buf, stream_id, FRAME_DATA_BACK, n); let frame = buf[..FRAME_HEADER_SIZE + n].to_vec(); - if data_writer_tx.send(frame).await.is_err() { - log::warn!("Stream {} data channel closed, closing", stream_id); - break; - } + let sent = tokio::select! { + result = data_writer_tx.send(frame) => result.is_ok(), + _ = stream_token.cancelled() => false, + }; + if !sent { break; } } Err(_) => break, } diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index e0f0b43..8307d0d 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/remoteingress', - version: '4.8.10', + version: '4.8.11', description: 'Edge ingress tunnel for DcRouter - accepts incoming TCP connections at network edge and tunnels them to DcRouter SmartProxy preserving client IP via PROXY protocol v1.' }