mirror of
https://github.com/telemt/telemt.git
synced 2026-10-29 03:57:15 +03:00
WEB Carriers Safe-matrix Refactored
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -65,7 +65,8 @@ pub(super) fn rebuild(config: &mut ProxyConfig) -> Result<()> {
|
||||
secret_mode: profile.secret_mode,
|
||||
carrier: config.web.carrier,
|
||||
carrier_negotiation_enabled: config.web.carrier_negotiation_enabled(),
|
||||
carrier_learning: config.web.carrier_learning,
|
||||
carrier_learning: config.web.carrier_negotiation_enabled()
|
||||
&& config.web.carrier_learning,
|
||||
carriers: Arc::clone(&carrier_candidates),
|
||||
carrier_negotiation_deadlines_secs: config
|
||||
.web
|
||||
|
||||
@@ -72,6 +72,9 @@ pub(super) fn validate(config: &mut ProxyConfig) -> Result<()> {
|
||||
validate_limits(&config.web.limits)?;
|
||||
debug::validate(&config.web.debug, &config.web.limits)?;
|
||||
let carriers = negotiation::validate(&config.web)?;
|
||||
if carriers.contains(&WebCarrier::Https) && config.web.limits.max_http_handlers < 2 {
|
||||
return config_error("WEB https candidates require web.limits.max_http_handlers >= 2");
|
||||
}
|
||||
if carriers.contains(&WebCarrier::HttpsLanes) && config.web.limits.max_http_handlers < 4 {
|
||||
return config_error(
|
||||
"WEB https-lanes candidates require web.limits.max_http_handlers >= 4",
|
||||
@@ -143,6 +146,11 @@ fn validate_limits(limits: &WebLimitsConfig) -> Result<()> {
|
||||
if !(1..=MAX_WEB_TOMBSTONES_PER_SESSION).contains(&limits.max_tombstones_per_session) {
|
||||
return config_error("web.limits.max_tombstones_per_session must be within [1, 4096]");
|
||||
}
|
||||
if limits.pending_bytes_per_lane <= WEB_FRAME_HEADER_BYTES + WEB_QUEUE_ITEM_COST {
|
||||
return config_error(
|
||||
"web.limits.pending_bytes_per_lane must preserve one non-empty DATA frame",
|
||||
);
|
||||
}
|
||||
if limits.carrier_batch_bytes > limits.max_body_bytes
|
||||
|| limits.carrier_batch_bytes
|
||||
< limits
|
||||
@@ -248,6 +256,7 @@ fn validate_limits(limits: &WebLimitsConfig) -> Result<()> {
|
||||
|| limits.max_bootstraps_per_ip > limits.max_bootstraps_global
|
||||
|| limits.max_http_handlers > limits.max_http_connections
|
||||
|| limits.max_body_readers > limits.max_http_handlers
|
||||
|| limits.max_lane_open_waits_per_session > limits.max_streams_per_session
|
||||
|| limits.pending_bytes_per_session > limits.pending_bytes_global
|
||||
|| limits.pending_items_per_session > limits.pending_items_global
|
||||
|| limits.pending_bytes_per_lane > limits.pending_bytes_per_session
|
||||
|
||||
@@ -3,7 +3,8 @@ use super::*;
|
||||
const WEB_DEBUG_RENDERERS: usize = 2;
|
||||
const WEB_DEBUG_STATUS_PAGE_BYTES: usize = 8 * 1024 * 1024;
|
||||
const WEB_DEBUG_GROUP_SCRATCH_BYTES: usize = 4 * 1024 * 1024;
|
||||
const WEB_CARRIER_LEARNING_ENTRY_BYTES: usize = 256;
|
||||
const WEB_CARRIER_LEARNING_ENTRY_BYTES: usize = 512;
|
||||
const WEB_LANE_STATE_BYTES: usize = 512;
|
||||
|
||||
/// Validates process-wide body, header, queue, static, and debug reservations.
|
||||
pub(super) fn validate(limits: &WebLimitsConfig) -> Result<()> {
|
||||
@@ -57,6 +58,15 @@ pub(super) fn validate(limits: &WebLimitsConfig) -> Result<()> {
|
||||
.ok_or_else(|| {
|
||||
ProxyError::Config("web.carrier learning reservation overflowed usize".to_string())
|
||||
})?;
|
||||
let lane_state_reservation = limits
|
||||
.max_streams_per_session
|
||||
.checked_add(limits.max_tombstones_per_session)
|
||||
.and_then(|value| value.checked_add(1))
|
||||
.and_then(|value| value.checked_mul(limits.max_sessions_global))
|
||||
.and_then(|value| value.checked_mul(WEB_LANE_STATE_BYTES))
|
||||
.ok_or_else(|| {
|
||||
ProxyError::Config("web.limits lane state reservation overflowed usize".to_string())
|
||||
})?;
|
||||
let reserved = limits
|
||||
.pending_bytes_global
|
||||
.checked_add(limits.max_body_bytes_global)
|
||||
@@ -65,6 +75,7 @@ pub(super) fn validate(limits: &WebLimitsConfig) -> Result<()> {
|
||||
.and_then(|value| value.checked_add(status_pages))
|
||||
.and_then(|value| value.checked_add(debug_reservation))
|
||||
.and_then(|value| value.checked_add(carrier_learning_reservation))
|
||||
.and_then(|value| value.checked_add(lane_state_reservation))
|
||||
.and_then(|value| value.checked_add(http_header_reservation))
|
||||
.ok_or_else(|| ProxyError::Config("web.limits byte ceilings overflow usize".to_string()))?;
|
||||
if reserved > limits.memory_envelope_bytes
|
||||
@@ -76,3 +87,20 @@ pub(super) fn validate(limits: &WebLimitsConfig) -> Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default_envelope_includes_bounded_lane_and_learning_metadata() {
|
||||
let limits = WebLimitsConfig::default();
|
||||
assert!(validate(&limits).is_ok());
|
||||
|
||||
let previous_envelope = WebLimitsConfig {
|
||||
memory_envelope_bytes: 768 * 1024 * 1024,
|
||||
..limits
|
||||
};
|
||||
assert!(validate(&previous_envelope).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,12 @@ pub(super) fn validate(config: &WebConfig) -> Result<Vec<WebCarrier>> {
|
||||
"web.timeouts.carrier_negotiation_deadlines_secs must be non-zero and strictly increasing",
|
||||
);
|
||||
}
|
||||
if deadlines[3] > config.timeouts.bootstrap_lifetime_secs {
|
||||
let retained_chain_secs = deadlines[3]
|
||||
.checked_add(config.timeouts.carrier_health_secs)
|
||||
.and_then(|value| value.checked_add(1));
|
||||
if retained_chain_secs.is_none_or(|value| value >= config.timeouts.bootstrap_lifetime_secs) {
|
||||
return config_error(
|
||||
"web.timeouts carrier negotiation deadline must not exceed bootstrap_lifetime_secs",
|
||||
"web.timeouts final carrier deadline plus health and cleanup must be lower than bootstrap_lifetime_secs",
|
||||
);
|
||||
}
|
||||
Ok(candidates)
|
||||
@@ -112,5 +115,9 @@ mod tests {
|
||||
assert!(validate(&config).is_err());
|
||||
config.timeouts.carrier_negotiation_deadlines_secs = [3, 5, 8, 121];
|
||||
assert!(validate(&config).is_err());
|
||||
config.timeouts.carrier_negotiation_deadlines_secs = [3, 5, 8, 89];
|
||||
assert!(validate(&config).is_err());
|
||||
config.timeouts.carrier_negotiation_deadlines_secs = [3, 5, 8, 88];
|
||||
assert!(validate(&config).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,27 @@ pub(super) fn validate(timeouts: &WebTimeoutsConfig) -> Result<()> {
|
||||
{
|
||||
return config_error(&format!("web.timeouts.{field} must be within [1, 3600]"));
|
||||
}
|
||||
if timeouts.carrier_learning_secs == 0 {
|
||||
return config_error("web.timeouts.carrier_learning_secs must be > 0");
|
||||
if !(2..=86_400).contains(&timeouts.carrier_learning_secs) {
|
||||
return config_error("web.timeouts.carrier_learning_secs must be within [2, 86400]");
|
||||
}
|
||||
if timeouts.stream_first_byte_secs > 300 {
|
||||
return config_error("web.timeouts.stream_first_byte_secs must be within [1, 300]");
|
||||
}
|
||||
if timeouts.websocket_upgrade_secs > 60 {
|
||||
return config_error("web.timeouts.websocket_upgrade_secs must be within [1, 60]");
|
||||
}
|
||||
if timeouts.websocket_open_secs > 300 {
|
||||
return config_error("web.timeouts.websocket_open_secs must be within [1, 300]");
|
||||
}
|
||||
if timeouts.lane_open_wait_secs > timeouts.long_poll_secs {
|
||||
return config_error(
|
||||
"web.timeouts.lane_open_wait_secs must not exceed long_poll_secs",
|
||||
);
|
||||
}
|
||||
if timeouts.carrier_health_secs > timeouts.reconnect_grace_secs {
|
||||
return config_error(
|
||||
"web.timeouts.carrier_health_secs must not exceed reconnect_grace_secs",
|
||||
);
|
||||
}
|
||||
let request_deadline = timeouts
|
||||
.header_secs
|
||||
|
||||
@@ -42,6 +42,14 @@ pub(super) fn validate(
|
||||
"WebSocket carriers require websocket_http_connection_reserve within [1, max_http_connections)",
|
||||
);
|
||||
}
|
||||
let websocket_capacity = limits
|
||||
.max_http_connections
|
||||
.saturating_sub(limits.websocket_http_connection_reserve);
|
||||
if limits.max_websocket_evictions_in_flight > websocket_capacity {
|
||||
return config_error(
|
||||
"web.limits.max_websocket_evictions_in_flight must not exceed WebSocket connection capacity",
|
||||
);
|
||||
}
|
||||
let socket_base = WEBSOCKET_IO_BUFFER_BYTES
|
||||
.checked_mul(2)
|
||||
.and_then(|value| value.checked_add(WEBSOCKET_DRIVER_OVERHEAD_BYTES))
|
||||
|
||||
Reference in New Issue
Block a user