Skip to content

Commit a9d81a2

Browse files
committed
fix: increase frame read timeout, improve connection cleanup, bump to v0.1.6
1 parent b76fd1c commit a9d81a2

File tree

7 files changed

+1276
-13
lines changed

7 files changed

+1276
-13
lines changed

.github/workflows/release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ jobs:
147147
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4
148148
with:
149149
path: artifacts
150+
pattern: utun-*
151+
merge-multiple: false
150152

151153
- name: Upload to release
152154
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.1.6] - 2026-02-14
11+
12+
### Fixed
13+
- Frame read timeout increased from 30s to 60s to prevent heartbeat timeout during idle periods
14+
- Heartbeat task now respects reconnection_enabled setting
15+
- Connection cleanup now properly removes from both demux registry and connection manager
16+
- GitHub Actions release workflow artifact download configuration
17+
1018
## [0.1.5] - 2026-02-14
1119

1220
### Added

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "utun"
3-
version = "0.1.5"
3+
version = "0.1.6"
44
edition = "2021"
55
authors = ["UTun Contributors"]
66
description = "Quantum-safe tunnel system"

src/tunnel/dest.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use tokio::time::{timeout, Duration};
1818
// Maximum frame size to prevent memory exhaustion (1MB)
1919
const MAX_FRAME_SIZE: u32 = 1024 * 1024;
2020

21-
// Frame read timeout to prevent slowloris attacks (30 seconds)
22-
const FRAME_READ_TIMEOUT: Duration = Duration::from_secs(30);
21+
// Frame read timeout (must exceed heartbeat interval to allow pings during idle periods)
22+
const FRAME_READ_TIMEOUT: Duration = Duration::from_secs(60);
2323

2424
// Handshake timeout per message (increased for large McEliece keys)
2525
const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(30);

src/tunnel/source.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ const MAX_FRAME_SIZE: u32 = 1024 * 1024;
1616
// Handshake timeout per message (increased for large McEliece keys)
1717
const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(30);
1818

19-
// Frame read timeout (30 seconds)
20-
const FRAME_READ_TIMEOUT: Duration = Duration::from_secs(30);
19+
// Frame read timeout (must exceed heartbeat interval to allow pongs during idle periods)
20+
const FRAME_READ_TIMEOUT: Duration = Duration::from_secs(60);
2121
use super::{
2222
ConnectionError, ConnectionManager, Frame, FrameCodec, FrameError, FrameType, HandshakeContext,
2323
HandshakeError, Protocol,
@@ -987,13 +987,10 @@ impl SourceContainer {
987987

988988
/// Spawns background task to send periodic heartbeat pings
989989
async fn spawn_heartbeat_task(&self) -> Result<(), SourceError> {
990-
if !self.config.reconnection_enabled {
991-
return Ok(());
992-
}
993-
994990
let interval = Duration::from_millis(self.config.heartbeat_interval_ms);
995991
let timeout_duration = Duration::from_millis(self.config.heartbeat_timeout_ms);
996992
let max_missed = self.config.max_missed_pongs;
993+
let reconnection_enabled = self.config.reconnection_enabled;
997994

998995
let state = self.heartbeat_state.clone();
999996
let container = Arc::new(self.clone());
@@ -1036,8 +1033,9 @@ impl SourceContainer {
10361033
if total >= max_missed {
10371034
tracing::error!("Heartbeat failed: {} consecutive timeouts", total);
10381035
health.set_status(HealthStatus::Unhealthy).await;
1039-
// Trigger reconnection
1040-
container.reconnection_needed.store(true, Ordering::SeqCst);
1036+
if reconnection_enabled {
1037+
container.reconnection_needed.store(true, Ordering::SeqCst);
1038+
}
10411039
} else if total >= max_missed / 2 {
10421040
health.set_status(HealthStatus::Degraded).await;
10431041
}
@@ -1100,6 +1098,7 @@ impl SourceContainer {
11001098
conn_id
11011099
);
11021100
self.unregister_connection(conn_id).await;
1101+
self.connection_manager.remove_connection(conn_id).await;
11031102
return Err(SourceError::ConfigError(
11041103
"Channel closed while waiting for CONNECT_ACK".to_string(),
11051104
));
@@ -1111,17 +1110,20 @@ impl SourceContainer {
11111110
fin_frame.set_fin();
11121111
let _ = self.send_frame(fin_frame).await;
11131112
self.unregister_connection(conn_id).await;
1113+
self.connection_manager.remove_connection(conn_id).await;
11141114
return Ok(()); // Clean exit
11151115
}
11161116
};
11171117

11181118
if ack_frame.frame_type() != FrameType::ConnectAck {
11191119
self.unregister_connection(conn_id).await;
1120+
self.connection_manager.remove_connection(conn_id).await;
11201121
return Err(SourceError::ConfigError("Expected CONNECT_ACK".to_string()));
11211122
}
11221123

11231124
if ack_frame.payload().is_empty() || ack_frame.payload()[0] == 0 {
11241125
self.unregister_connection(conn_id).await;
1126+
self.connection_manager.remove_connection(conn_id).await;
11251127
return Err(SourceError::ConfigError("Connection rejected".to_string()));
11261128
}
11271129

@@ -1186,9 +1188,13 @@ impl SourceContainer {
11861188
}
11871189
}
11881190

1189-
// Cleanup: unregister connection from demux registry
1191+
// Cleanup connection from registry and manager
11901192
self_clone.unregister_connection(conn_clone.id()).await;
11911193
conn_clone.set_state(super::ConnectionState::Closed).await;
1194+
self_clone
1195+
.connection_manager
1196+
.remove_connection(conn_clone.id())
1197+
.await;
11921198
});
11931199

11941200
Ok(())

0 commit comments

Comments
 (0)