fix(tunnel): bound upstream clients and heartbeat deltas

This commit is contained in:
Entropy.Xu
2026-05-28 20:34:08 +08:00
parent bb2fc2ec00
commit eaabfb83ed
12 changed files with 273 additions and 37 deletions
@@ -810,11 +810,11 @@ pub(crate) async fn maybe_build_local_internal_proxy_response_impl(
node_id: node_id.clone(),
heartbeat_interval: payload.heartbeat_interval,
active_connections: payload.active_connections,
total_requests_delta: payload.total_requests,
total_requests_delta: payload.window_total_requests.or(payload.total_requests),
avg_latency_ms: payload.avg_latency_ms,
failed_requests_delta: payload.failed_requests,
dns_failures_delta: payload.dns_failures,
stream_errors_delta: payload.stream_errors,
failed_requests_delta: payload.window_failed_requests.or(payload.failed_requests),
dns_failures_delta: payload.window_dns_failures.or(payload.dns_failures),
stream_errors_delta: payload.window_stream_errors.or(payload.stream_errors),
proxy_metadata: payload.proxy_metadata,
proxy_version: payload.proxy_version,
};
@@ -402,10 +402,16 @@ pub(crate) fn parse_internal_tunnel_heartbeat_request(
.is_some_and(|value| !(5..=600).contains(&value))
|| payload.active_connections.is_some_and(|value| value < 0)
|| payload.total_requests.is_some_and(|value| value < 0)
|| payload.window_total_requests.is_some_and(|value| value < 0)
|| payload.avg_latency_ms.is_some_and(|value| value < 0.0)
|| payload.failed_requests.is_some_and(|value| value < 0)
|| payload
.window_failed_requests
.is_some_and(|value| value < 0)
|| payload.dns_failures.is_some_and(|value| value < 0)
|| payload.window_dns_failures.is_some_and(|value| value < 0)
|| payload.stream_errors.is_some_and(|value| value < 0)
|| payload.window_stream_errors.is_some_and(|value| value < 0)
|| payload
.proxy_version
.as_deref()
@@ -12,14 +12,22 @@ pub(crate) struct InternalTunnelHeartbeatRequest {
#[serde(default)]
pub(crate) total_requests: Option<i64>,
#[serde(default)]
pub(crate) window_total_requests: Option<i64>,
#[serde(default)]
pub(crate) avg_latency_ms: Option<f64>,
#[serde(default)]
pub(crate) failed_requests: Option<i64>,
#[serde(default)]
pub(crate) window_failed_requests: Option<i64>,
#[serde(default)]
pub(crate) dns_failures: Option<i64>,
#[serde(default)]
pub(crate) window_dns_failures: Option<i64>,
#[serde(default)]
pub(crate) stream_errors: Option<i64>,
#[serde(default)]
pub(crate) window_stream_errors: Option<i64>,
#[serde(default)]
pub(crate) proxy_metadata: Option<serde_json::Value>,
#[serde(default)]
pub(crate) proxy_version: Option<String>,
@@ -52,11 +52,15 @@ async fn gateway_handles_internal_tunnel_heartbeat_locally_with_loopback() {
"heartbeat_id": 77,
"heartbeat_interval": 45,
"active_connections": 5,
"total_requests": 9,
"total_requests": 100,
"avg_latency_ms": 12.5,
"failed_requests": 1,
"dns_failures": 2,
"stream_errors": 3,
"failed_requests": 20,
"dns_failures": 30,
"stream_errors": 40,
"window_total_requests": 9,
"window_failed_requests": 1,
"window_dns_failures": 2,
"window_stream_errors": 3,
"proxy_metadata": {"arch": "arm64"},
"proxy_version": "2.0.0",
}))
@@ -71,6 +75,15 @@ async fn gateway_handles_internal_tunnel_heartbeat_locally_with_loopback() {
assert_eq!(payload["upgrade_to"], "1.2.3");
assert_eq!(payload["remote_config"]["allowed_ports"][0], 443);
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
let node = repository
.find_proxy_node("node-123")
.await
.expect("node lookup should succeed")
.expect("node should exist");
assert_eq!(node.total_requests, 9);
assert_eq!(node.failed_requests, 1);
assert_eq!(node.dns_failures, 2);
assert_eq!(node.stream_errors, 3);
gateway_handle.abort();
upstream_handle.abort();
+18 -4
View File
@@ -72,14 +72,22 @@ struct InternalTunnelHeartbeatRequest {
#[serde(default)]
total_requests: Option<i64>,
#[serde(default)]
window_total_requests: Option<i64>,
#[serde(default)]
avg_latency_ms: Option<f64>,
#[serde(default)]
failed_requests: Option<i64>,
#[serde(default)]
window_failed_requests: Option<i64>,
#[serde(default)]
dns_failures: Option<i64>,
#[serde(default)]
window_dns_failures: Option<i64>,
#[serde(default)]
stream_errors: Option<i64>,
#[serde(default)]
window_stream_errors: Option<i64>,
#[serde(default)]
proxy_metadata: Option<serde_json::Value>,
#[serde(default)]
proxy_version: Option<String>,
@@ -932,11 +940,11 @@ async fn apply_embedded_tunnel_heartbeat(
node_id: node_id.clone(),
heartbeat_interval: payload.heartbeat_interval,
active_connections: payload.active_connections,
total_requests_delta: payload.total_requests,
total_requests_delta: payload.window_total_requests.or(payload.total_requests),
avg_latency_ms: payload.avg_latency_ms,
failed_requests_delta: payload.failed_requests,
dns_failures_delta: payload.dns_failures,
stream_errors_delta: payload.stream_errors,
failed_requests_delta: payload.window_failed_requests.or(payload.failed_requests),
dns_failures_delta: payload.window_dns_failures.or(payload.dns_failures),
stream_errors_delta: payload.window_stream_errors.or(payload.stream_errors),
proxy_metadata: payload.proxy_metadata,
proxy_version: payload.proxy_version,
};
@@ -1009,10 +1017,16 @@ fn parse_embedded_tunnel_heartbeat_request(
.is_some_and(|value| !(5..=600).contains(&value))
|| payload.active_connections.is_some_and(|value| value < 0)
|| payload.total_requests.is_some_and(|value| value < 0)
|| payload.window_total_requests.is_some_and(|value| value < 0)
|| payload.avg_latency_ms.is_some_and(|value| value < 0.0)
|| payload.failed_requests.is_some_and(|value| value < 0)
|| payload
.window_failed_requests
.is_some_and(|value| value < 0)
|| payload.dns_failures.is_some_and(|value| value < 0)
|| payload.window_dns_failures.is_some_and(|value| value < 0)
|| payload.stream_errors.is_some_and(|value| value < 0)
|| payload.window_stream_errors.is_some_and(|value| value < 0)
|| payload
.proxy_version
.as_deref()