fix(remoteingress-core): ensure upstream writes cancel promptly and reliably deliver CLOSE_BACK frames

This commit is contained in:
2026-03-16 10:51:59 +00:00
parent 51ab32f6c3
commit 4b95a3c999
3 changed files with 23 additions and 9 deletions

View File

@@ -1,5 +1,11 @@
# Changelog
## 2026-03-16 - 4.5.8 - fix(remoteingress-core)
ensure upstream writes cancel promptly and reliably deliver CLOSE_BACK frames
- listen for stream cancellation while waiting on upstream write timeouts so FRAME_CLOSE does not block for up to 60 seconds
- replace try_send with send().await when emitting CLOSE_BACK frames to avoid silently dropping close notifications when the data channel is full
## 2026-03-16 - 4.5.7 - fix(remoteingress-core)
improve tunnel reconnect and frame write efficiency

View File

@@ -541,10 +541,16 @@ async fn handle_edge_connection(
match data {
Some(data) => {
let len = data.len() as u32;
match tokio::time::timeout(
Duration::from_secs(60),
up_write.write_all(&data),
).await {
// Check cancellation alongside the write so we respond
// promptly to FRAME_CLOSE instead of blocking up to 60s.
let write_result = tokio::select! {
r = tokio::time::timeout(
Duration::from_secs(60),
up_write.write_all(&data),
) => r,
_ = writer_token.cancelled() => break,
};
match write_result {
Ok(Ok(())) => {}
Ok(Err(_)) => break,
Err(_) => {
@@ -619,10 +625,11 @@ async fn handle_edge_connection(
}
}
// Send CLOSE_BACK via DATA channel (must arrive AFTER last DATA_BACK)
// Send CLOSE_BACK via DATA channel (must arrive AFTER last DATA_BACK).
// Use send().await to guarantee delivery (try_send silently drops if full).
if !stream_token.is_cancelled() {
let close_frame = encode_frame(stream_id, FRAME_CLOSE_BACK, &[]);
let _ = data_writer_tx.try_send(close_frame);
let _ = data_writer_tx.send(close_frame).await;
}
writer_for_edge_data.abort();
@@ -632,10 +639,11 @@ async fn handle_edge_connection(
if let Err(e) = result {
log::error!("Stream {} error: {}", stream_id, e);
// Send CLOSE_BACK via DATA channel on error (must arrive after any DATA_BACK)
// Send CLOSE_BACK via DATA channel on error (must arrive after any DATA_BACK).
// Use send().await to guarantee delivery.
if !stream_token.is_cancelled() {
let close_frame = encode_frame(stream_id, FRAME_CLOSE_BACK, &[]);
let _ = data_writer_tx.try_send(close_frame);
let _ = data_writer_tx.send(close_frame).await;
}
}

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/remoteingress',
version: '4.5.7',
version: '4.5.8',
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.'
}