WEB Carrier: https-lanes

Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
Alexey
2026-08-23 09:04:53 +03:00
parent 79ad4cb541
commit 90c0d65e1b
18 changed files with 1063 additions and 74 deletions
+1
View File
@@ -62,6 +62,7 @@ pub(super) fn rebuild(config: &mut ProxyConfig) -> Result<()> {
public_addr: vhost.public_addr,
user: profile.user.clone(),
secret_mode: profile.secret_mode,
carrier: config.web.carrier,
capability,
max_sessions: profile
.max_sessions
+1 -1
View File
@@ -259,7 +259,7 @@ const LISTENER_CONFIG_KEYS: &[&str] = &[
"web_trusted_proxy_cidrs",
];
const WEB_CONFIG_KEYS: &[&str] = &["enabled", "limits", "timeouts", "vhosts"];
const WEB_CONFIG_KEYS: &[&str] = &["enabled", "carrier", "limits", "timeouts", "vhosts"];
const WEB_LIMITS_CONFIG_KEYS: &[&str] = &[
"max_header_bytes",
+5
View File
@@ -59,6 +59,11 @@ pub(super) fn validate(config: &mut ProxyConfig) -> Result<()> {
}
validate_limits(&config.web.limits)?;
if config.web.carrier == WebCarrier::HttpsLanes
&& config.web.limits.max_http_handlers < 2
{
return config_error("web.carrier=https-lanes requires web.limits.max_http_handlers >= 2");
}
validate_timeouts(&config.web.timeouts)?;
validate_vhosts(config)?;
Ok(())
+16 -4
View File
@@ -14,6 +14,7 @@ web_trusted_proxy_cidrs = ["127.0.0.1/32"]
[web]
enabled = true
carrier = "https-lanes"
[[web.vhosts]]
host = "Proxy.Example.COM"
@@ -42,11 +43,22 @@ fn web_config_builds_canonical_runtime_snapshot() {
assert_eq!(vhost.profiles.len(), 1);
assert_eq!(vhost.profiles[0].user, "alice");
assert_eq!(vhost.profiles[0].secret_mode, WebSecretMode::Dd);
assert_eq!(vhost.profiles[0].carrier, WebCarrier::HttpsLanes);
assert_eq!(vhost.profiles[0].max_sessions, 4);
assert_eq!(vhost.profiles[0].max_streams, 64);
assert_eq!(vhost.profiles[0].max_streams_per_session, 16);
}
#[test]
fn https_lanes_requires_separate_poll_and_control_handler_capacity() {
let invalid = WEB_CONFIG.replace(
"carrier = \"https-lanes\"",
"carrier = \"https-lanes\"\n\n[web.limits]\nmax_http_handlers = 1\nmax_body_readers = 1",
);
let error = load_config_error_from_temp_toml(&invalid);
assert!(error.contains("web.carrier=https-lanes requires"));
}
#[test]
fn web_listener_requires_an_explicit_trusted_proxy() {
let invalid = WEB_CONFIG.replace(
@@ -60,8 +72,8 @@ fn web_listener_requires_an_explicit_trusted_proxy() {
#[test]
fn web_queue_limits_preserve_control_and_uplink_progress() {
let invalid = WEB_CONFIG.replace(
"[web]\nenabled = true",
"[web]\nenabled = true\n\n[web.limits]\ncontrol_bytes_per_session = 1",
"carrier = \"https-lanes\"",
"carrier = \"https-lanes\"\n\n[web.limits]\ncontrol_bytes_per_session = 1",
);
let error = load_config_error_from_temp_toml(&invalid);
assert!(error.contains("control reserves must cover bounded control frames"));
@@ -70,9 +82,9 @@ fn web_queue_limits_preserve_control_and_uplink_progress() {
#[test]
fn web_semaphore_limits_are_rejected_before_runtime_construction() {
let invalid = WEB_CONFIG.replace(
"[web]\nenabled = true",
"carrier = \"https-lanes\"",
&format!(
"[web]\nenabled = true\n\n[web.limits]\nmax_http_connections = {}",
"carrier = \"https-lanes\"\n\n[web.limits]\nmax_http_connections = {}",
tokio::sync::Semaphore::MAX_PERMITS + 1,
),
);
+1 -1
View File
@@ -49,7 +49,7 @@ pub use server::{
};
#[allow(unused_imports)]
pub use web::{
WebConfig, WebDecoyConfig, WebLimitsConfig, WebProfileConfig, WebSecretMode,
WebCarrier, WebConfig, WebDecoyConfig, WebLimitsConfig, WebProfileConfig, WebSecretMode,
WebTimeoutsConfig, WebVhostConfig,
};
pub(crate) use web::{
+26
View File
@@ -16,6 +16,27 @@ pub enum WebSecretMode {
Dd,
}
/// HTTP carrier selected for newly issued WEB bridge sessions.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum WebCarrier {
/// Serialize all logical streams through one uplink and one downlink sequence.
#[default]
Https,
/// Give every logical stream independent HTTPS sequencing and polling state.
HttpsLanes,
}
impl WebCarrier {
/// Returns the exact carrier token advertised to the browser bridge.
pub(crate) const fn as_str(self) -> &'static str {
match self {
Self::Https => "https",
Self::HttpsLanes => "https-lanes",
}
}
}
/// One access user explicitly exposed through a WEB virtual host.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WebProfileConfig {
@@ -273,6 +294,9 @@ pub struct WebConfig {
/// Enables issuance of new WEB bridge and session credentials.
#[serde(default)]
pub enabled: bool,
/// Carrier selected for newly issued WEB bridge sessions.
#[serde(default)]
pub carrier: WebCarrier,
/// Hard process and protocol limits.
#[serde(default)]
pub limits: WebLimitsConfig,
@@ -320,6 +344,8 @@ pub(crate) struct WebRuntimeProfile {
pub(crate) user: String,
/// Client secret representation and inner protocol policy.
pub(crate) secret_mode: WebSecretMode,
/// Carrier frozen into bridge and session state at issuance time.
pub(crate) carrier: WebCarrier,
/// HMAC-derived bridge capability.
pub(crate) capability: [u8; 32],
/// Per-profile live session ceiling.