Compare commits

...

2 Commits

Author SHA1 Message Date
51ab32f6c3 v4.5.7 2026-03-16 09:44:31 +00:00
ed52520d50 fix(remoteingress-core): improve tunnel reconnect and frame write efficiency 2026-03-16 09:44:31 +00:00
5 changed files with 32 additions and 21 deletions

View File

@@ -1,5 +1,11 @@
# Changelog # Changelog
## 2026-03-16 - 4.5.7 - fix(remoteingress-core)
improve tunnel reconnect and frame write efficiency
- Reuse the TLS connector across edge reconnections to preserve session resumption state and reduce reconnect latency.
- Buffer hub and edge frame writes to coalesce small control and data frames into fewer TLS records and syscalls while still flushing each frame promptly.
## 2026-03-16 - 4.5.6 - fix(remoteingress-core) ## 2026-03-16 - 4.5.6 - fix(remoteingress-core)
disable Nagle's algorithm on edge, hub, and upstream TCP sockets to reduce control-frame latency disable Nagle's algorithm on edge, hub, and upstream TCP sockets to reduce control-frame latency

View File

@@ -1,6 +1,6 @@
{ {
"name": "@serve.zone/remoteingress", "name": "@serve.zone/remoteingress",
"version": "4.5.6", "version": "4.5.7",
"private": false, "private": false,
"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.", "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.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",

View File

@@ -194,6 +194,14 @@ async fn edge_main_loop(
let mut backoff_ms: u64 = 1000; let mut backoff_ms: u64 = 1000;
let max_backoff_ms: u64 = 30000; let max_backoff_ms: u64 = 30000;
// Build TLS config ONCE outside the reconnect loop — preserves session
// cache across reconnections for TLS session resumption (saves 1 RTT).
let tls_config = rustls::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoCertVerifier))
.with_no_client_auth();
let connector = TlsConnector::from(Arc::new(tls_config));
loop { loop {
// Create a per-connection child token // Create a per-connection child token
let connection_token = cancel_token.child_token(); let connection_token = cancel_token.child_token();
@@ -209,6 +217,7 @@ async fn edge_main_loop(
&listen_ports, &listen_ports,
&mut shutdown_rx, &mut shutdown_rx,
&connection_token, &connection_token,
&connector,
) )
.await; .await;
@@ -259,14 +268,8 @@ async fn connect_to_hub_and_run(
listen_ports: &Arc<RwLock<Vec<u16>>>, listen_ports: &Arc<RwLock<Vec<u16>>>,
shutdown_rx: &mut mpsc::Receiver<()>, shutdown_rx: &mut mpsc::Receiver<()>,
connection_token: &CancellationToken, connection_token: &CancellationToken,
connector: &TlsConnector,
) -> EdgeLoopResult { ) -> EdgeLoopResult {
// Build TLS connector that skips cert verification (auth is via secret)
let tls_config = rustls::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoCertVerifier))
.with_no_client_auth();
let connector = TlsConnector::from(Arc::new(tls_config));
let addr = format!("{}:{}", config.hub_host, config.hub_port); let addr = format!("{}:{}", config.hub_host, config.hub_port);
let tcp = match TcpStream::connect(&addr).await { let tcp = match TcpStream::connect(&addr).await {
@@ -378,15 +381,17 @@ async fn connect_to_hub_and_run(
let tunnel_writer_tx = tunnel_ctrl_tx.clone(); let tunnel_writer_tx = tunnel_ctrl_tx.clone();
let tw_token = connection_token.clone(); let tw_token = connection_token.clone();
let tunnel_writer_handle = tokio::spawn(async move { let tunnel_writer_handle = tokio::spawn(async move {
// BufWriter coalesces small writes (frame headers, control frames) into fewer
// TLS records and syscalls. Flushed after each frame to avoid holding data.
let mut writer = tokio::io::BufWriter::with_capacity(65536, write_half);
loop { loop {
tokio::select! { tokio::select! {
biased; // control frames always take priority over data biased; // control frames always take priority over data
ctrl = tunnel_ctrl_rx.recv() => { ctrl = tunnel_ctrl_rx.recv() => {
match ctrl { match ctrl {
Some(frame_data) => { Some(frame_data) => {
if write_half.write_all(&frame_data).await.is_err() { if writer.write_all(&frame_data).await.is_err() { break; }
break; if writer.flush().await.is_err() { break; }
}
} }
None => break, None => break,
} }
@@ -394,9 +399,8 @@ async fn connect_to_hub_and_run(
data = tunnel_data_rx.recv() => { data = tunnel_data_rx.recv() => {
match data { match data {
Some(frame_data) => { Some(frame_data) => {
if write_half.write_all(&frame_data).await.is_err() { if writer.write_all(&frame_data).await.is_err() { break; }
break; if writer.flush().await.is_err() { break; }
}
} }
None => break, None => break,
} }

View File

@@ -381,15 +381,17 @@ async fn handle_edge_connection(
let frame_writer_tx = ctrl_tx.clone(); let frame_writer_tx = ctrl_tx.clone();
let writer_token = edge_token.clone(); let writer_token = edge_token.clone();
let writer_handle = tokio::spawn(async move { let writer_handle = tokio::spawn(async move {
// BufWriter coalesces small writes (frame headers, control frames) into fewer
// TLS records and syscalls. Flushed after each frame to avoid holding data.
let mut writer = tokio::io::BufWriter::with_capacity(65536, write_half);
loop { loop {
tokio::select! { tokio::select! {
biased; // control frames always take priority over data biased; // control frames always take priority over data
ctrl = ctrl_rx.recv() => { ctrl = ctrl_rx.recv() => {
match ctrl { match ctrl {
Some(frame_data) => { Some(frame_data) => {
if write_half.write_all(&frame_data).await.is_err() { if writer.write_all(&frame_data).await.is_err() { break; }
break; if writer.flush().await.is_err() { break; }
}
} }
None => break, None => break,
} }
@@ -397,9 +399,8 @@ async fn handle_edge_connection(
data = data_rx.recv() => { data = data_rx.recv() => {
match data { match data {
Some(frame_data) => { Some(frame_data) => {
if write_half.write_all(&frame_data).await.is_err() { if writer.write_all(&frame_data).await.is_err() { break; }
break; if writer.flush().await.is_err() { break; }
}
} }
None => break, None => break,
} }

View File

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