mirror of
https://github.com/telemt/telemt.git
synced 2026-11-03 22:31:59 +03:00
WEB Carrier Counters + Status
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -248,6 +248,18 @@ impl ListenerManager {
|
||||
);
|
||||
}
|
||||
|
||||
/// Publishes one generation through the process-owned WEB policy fence.
|
||||
pub(crate) fn activate_runtime_generation(
|
||||
&self,
|
||||
generation: Arc<RuntimeGeneration>,
|
||||
) -> Arc<RuntimeGeneration> {
|
||||
if let Some(runtime) = &self.web_runtime {
|
||||
runtime.activate_generation(generation)
|
||||
} else {
|
||||
self.active_runtime.swap(generation)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stops every accept task and applies one deadline to the complete WEB ingress.
|
||||
pub(crate) async fn shutdown(&mut self) -> Result<(), String> {
|
||||
self.web_control.publish(
|
||||
|
||||
@@ -159,7 +159,13 @@ impl ReloadSupervisor {
|
||||
.mark_phase(command.reload_id, ReloadPhase::Preparing)
|
||||
.await;
|
||||
let old_runtime = self.active_runtime.load_full();
|
||||
let resolved = resolve_reload_config(&old_runtime.config(), &command.config);
|
||||
let resolved = match resolve_reload_config(&old_runtime.config(), &command.config) {
|
||||
Ok(resolved) => resolved,
|
||||
Err(error) => {
|
||||
self.control.fail(command.reload_id, error).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.control
|
||||
.set_deferred_fields(command.reload_id, resolved.deferred_process_fields.clone())
|
||||
.await;
|
||||
@@ -292,8 +298,11 @@ impl ReloadSupervisor {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
old_runtime.stop_accepting_sessions();
|
||||
let replaced = self.active_runtime.swap(new_runtime.clone());
|
||||
let replaced = {
|
||||
let listener_manager = self.listener_manager.lock().await;
|
||||
old_runtime.stop_accepting_sessions();
|
||||
listener_manager.activate_runtime_generation(new_runtime.clone())
|
||||
};
|
||||
self.web_trace
|
||||
.apply_policy(new_runtime.id, &new_runtime.config().web.debug);
|
||||
config_watcher_activation.send_replace(true);
|
||||
|
||||
@@ -5,7 +5,9 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use tokio::sync::{RwLock, Semaphore, watch};
|
||||
|
||||
use crate::config::{ProxyConfig, ServerConfig, web_debug_fits_limits};
|
||||
use crate::config::{
|
||||
ProxyConfig, ServerConfig, WEB_CARRIER_LEARNING_MIN_ENTRIES, web_debug_fits_limits,
|
||||
};
|
||||
use crate::crypto::SecureRandom;
|
||||
use crate::ip_tracker::UserIpTracker;
|
||||
use crate::network::probe::{decide_network_capabilities, run_probe};
|
||||
@@ -337,7 +339,7 @@ pub(crate) struct ResolvedReloadConfig {
|
||||
pub(crate) fn resolve_reload_config(
|
||||
old: &ProxyConfig,
|
||||
desired: &ProxyConfig,
|
||||
) -> ResolvedReloadConfig {
|
||||
) -> Result<ResolvedReloadConfig, String> {
|
||||
let mut effective = desired.clone();
|
||||
let mut fields = Vec::new();
|
||||
let listener_identity_matches = listeners_have_same_bind_identity(&old.server, &desired.server);
|
||||
@@ -424,21 +426,39 @@ pub(crate) fn resolve_reload_config(
|
||||
{
|
||||
fields.push("web.limits".to_string());
|
||||
effective.web.limits = old.web.limits.clone();
|
||||
if effective.rebuild_runtime_web().is_err() {
|
||||
fields.push("web".to_string());
|
||||
effective.web = old.web.clone();
|
||||
}
|
||||
if effective.web.carrier_negotiation_enabled()
|
||||
&& effective.web.carrier_learning
|
||||
&& effective.web.limits.max_carrier_learning_entries
|
||||
< WEB_CARRIER_LEARNING_MIN_ENTRIES
|
||||
{
|
||||
if old.web.carrier_learning != desired.web.carrier_learning {
|
||||
fields.push("web.carrier_learning".to_string());
|
||||
effective.web.carrier_learning = old.web.carrier_learning;
|
||||
} else {
|
||||
fields.push("web.carriers".to_string());
|
||||
effective.web.carriers = old.web.carriers.clone();
|
||||
}
|
||||
}
|
||||
if !web_debug_fits_limits(&effective.web.debug, &effective.web.limits) {
|
||||
fields.push("web.debug".to_string());
|
||||
effective.web.debug = old.web.debug.clone();
|
||||
}
|
||||
effective
|
||||
.validate_effective_web()
|
||||
.map_err(|error| format!("effective WEB configuration is invalid: {error}"))?;
|
||||
effective
|
||||
.rebuild_runtime_user_auth()
|
||||
.map_err(|error| format!("effective user runtime preparation failed: {error}"))?;
|
||||
effective
|
||||
.rebuild_runtime_web()
|
||||
.map_err(|error| format!("effective WEB runtime preparation failed: {error}"))?;
|
||||
let runtime_changed = !configs_equal(old, &effective);
|
||||
ResolvedReloadConfig {
|
||||
Ok(ResolvedReloadConfig {
|
||||
effective,
|
||||
deferred_process_fields: fields,
|
||||
runtime_changed,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn listeners_have_same_bind_identity(old: &ServerConfig, desired: &ServerConfig) -> bool {
|
||||
@@ -469,8 +489,11 @@ fn listener_process_fields_equal(old: &ServerConfig, desired: &ServerConfig) ->
|
||||
}
|
||||
|
||||
/// Returns process-owned fields that cannot change in the current generation.
|
||||
pub(crate) fn deferred_process_fields(old: &ProxyConfig, new: &ProxyConfig) -> Vec<String> {
|
||||
resolve_reload_config(old, new).deferred_process_fields
|
||||
pub(crate) fn deferred_process_fields(
|
||||
old: &ProxyConfig,
|
||||
new: &ProxyConfig,
|
||||
) -> Result<Vec<String>, String> {
|
||||
resolve_reload_config(old, new).map(|resolved| resolved.deferred_process_fields)
|
||||
}
|
||||
|
||||
fn configs_equal(old: &ProxyConfig, new: &ProxyConfig) -> bool {
|
||||
|
||||
@@ -31,7 +31,7 @@ fn process_socket_and_logging_changes_are_deferred() {
|
||||
new.server.listen_backlog = new.server.listen_backlog.saturating_add(1);
|
||||
new.general.disable_colors = !new.general.disable_colors;
|
||||
|
||||
let fields = deferred_process_fields(&old, &new);
|
||||
let fields = deferred_process_fields(&old, &new).unwrap();
|
||||
assert!(fields.contains(&"server.listeners".to_string()));
|
||||
assert!(fields.contains(&"general.disable_colors".to_string()));
|
||||
}
|
||||
@@ -43,7 +43,7 @@ fn global_mss_profiles_are_deferred_with_the_listener_socket_group() {
|
||||
desired.server.client_mss = Some("92".to_string());
|
||||
desired.server.client_mss_bulk = Some("1400".to_string());
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
resolved.deferred_process_fields,
|
||||
@@ -64,7 +64,7 @@ fn mixed_reload_retains_process_state_and_applies_runtime_state() {
|
||||
desired.server.client_mss = Some("92".to_string());
|
||||
desired.censorship.tls_domain = "reload.example".to_string();
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert_eq!(resolved.effective.server.client_mss, old.server.client_mss);
|
||||
assert_eq!(
|
||||
@@ -105,7 +105,7 @@ fn listener_announcement_is_runtime_owned_when_bind_identity_is_stable() {
|
||||
let mut desired = old.clone();
|
||||
desired.server.listeners[0].announce = Some("proxy.example".to_string());
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert!(resolved.deferred_process_fields.is_empty());
|
||||
assert_eq!(
|
||||
@@ -128,7 +128,7 @@ fn process_field_labels_are_stable_ordered_and_unique() {
|
||||
.saturating_add(1);
|
||||
desired.general.disable_colors = !desired.general.disable_colors;
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
resolved.deferred_process_fields,
|
||||
@@ -146,7 +146,7 @@ fn runtime_only_change_does_not_require_process_rebind() {
|
||||
let old = ProxyConfig::default();
|
||||
let mut new = old.clone();
|
||||
new.censorship.tls_domain = "reload.example".to_string();
|
||||
assert!(deferred_process_fields(&old, &new).is_empty());
|
||||
assert!(deferred_process_fields(&old, &new).unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -157,7 +157,7 @@ fn web_allocation_limits_are_deferred_until_restart() {
|
||||
let mut desired = old.clone();
|
||||
desired.web.limits.max_sessions_global += 1;
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
resolved.deferred_process_fields,
|
||||
@@ -170,6 +170,65 @@ fn web_allocation_limits_are_deferred_until_restart() {
|
||||
assert!(!resolved.runtime_changed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enabling_learning_is_deferred_when_retained_capacity_is_too_small() {
|
||||
let mut old = ProxyConfig::default();
|
||||
old.web.limits.max_carrier_learning_entries = 1;
|
||||
old.web.carriers = crate::config::WebCarriers::Disabled;
|
||||
old.web.carrier_learning = false;
|
||||
old.rebuild_runtime_user_auth().unwrap();
|
||||
old.rebuild_runtime_web().unwrap();
|
||||
let mut desired = old.clone();
|
||||
desired.web.limits.max_carrier_learning_entries = 3;
|
||||
desired.web.carriers = crate::config::WebCarriers::Enabled(vec![
|
||||
crate::config::WebCarrier::Websocket,
|
||||
crate::config::WebCarrier::Https,
|
||||
]);
|
||||
desired.web.carrier_learning = true;
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
resolved.deferred_process_fields,
|
||||
vec!["web.limits".to_string(), "web.carrier_learning".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
resolved.effective.web.limits.max_carrier_learning_entries,
|
||||
1
|
||||
);
|
||||
assert!(resolved.effective.web.carrier_negotiation_enabled());
|
||||
assert!(!resolved.effective.web.carrier_learning);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enabling_carriers_is_deferred_for_dormant_learning_with_small_capacity() {
|
||||
let mut old = ProxyConfig::default();
|
||||
old.web.limits.max_carrier_learning_entries = 1;
|
||||
old.web.carriers = crate::config::WebCarriers::Disabled;
|
||||
old.web.carrier_learning = true;
|
||||
old.rebuild_runtime_user_auth().unwrap();
|
||||
old.rebuild_runtime_web().unwrap();
|
||||
let mut desired = old.clone();
|
||||
desired.web.limits.max_carrier_learning_entries = 3;
|
||||
desired.web.carriers = crate::config::WebCarriers::Enabled(vec![
|
||||
crate::config::WebCarrier::Websocket,
|
||||
crate::config::WebCarrier::Https,
|
||||
]);
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
resolved.deferred_process_fields,
|
||||
vec!["web.limits".to_string(), "web.carriers".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
resolved.effective.web.limits.max_carrier_learning_entries,
|
||||
1
|
||||
);
|
||||
assert!(!resolved.effective.web.carrier_negotiation_enabled());
|
||||
assert!(resolved.effective.web.carrier_learning);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_debug_prefix_dependent_on_new_capacity_is_deferred_with_limits() {
|
||||
let mut old = ProxyConfig::default();
|
||||
@@ -179,7 +238,7 @@ fn web_debug_prefix_dependent_on_new_capacity_is_deferred_with_limits() {
|
||||
desired.web.limits.max_body_bytes = 4 * 1024 * 1024;
|
||||
desired.web.debug.body_prefix_bytes = 3 * 1024 * 1024;
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
resolved.deferred_process_fields,
|
||||
@@ -206,7 +265,7 @@ fn endpoint_only_listener_move_is_runtime_rebindable() {
|
||||
let mut desired = old.clone();
|
||||
desired.server.listeners[0].port = Some(8443);
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert!(resolved.deferred_process_fields.is_empty());
|
||||
assert_eq!(resolved.effective.server.listeners[0].port, Some(8443));
|
||||
@@ -221,7 +280,7 @@ fn synlimited_endpoint_move_remains_restart_only() {
|
||||
let mut desired = old.clone();
|
||||
desired.server.listeners[0].port = Some(8443);
|
||||
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
let resolved = resolve_reload_config(&old, &desired).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
resolved.deferred_process_fields,
|
||||
@@ -253,11 +312,5 @@ fn deferred_listener_identity_cannot_create_an_effective_decoy_loop() {
|
||||
];
|
||||
|
||||
assert!(desired.validate_web_decoy_listener_separation().is_ok());
|
||||
let resolved = resolve_reload_config(&old, &desired);
|
||||
assert!(
|
||||
resolved
|
||||
.effective
|
||||
.validate_web_decoy_listener_separation()
|
||||
.is_err()
|
||||
);
|
||||
assert!(resolve_reload_config(&old, &desired).is_err());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user