mirror of
https://github.com/telemt/telemt.git
synced 2026-11-02 13:52:03 +03:00
WEB: skip registry sweeps before the earliest possible expiry
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
use super::*;
|
||||
use std::hint::black_box;
|
||||
use std::time::Instant;
|
||||
|
||||
fn report(label: &str, mut samples: Vec<u128>) {
|
||||
samples.sort_unstable();
|
||||
eprintln!(
|
||||
"{label}: n={} p50={}ns p95={}ns p99={}ns",
|
||||
samples.len(),
|
||||
samples[samples.len() / 2],
|
||||
samples[samples.len() * 95 / 100],
|
||||
samples[samples.len() * 99 / 100]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "manual optimized-profile latency comparison; no timing assertions"]
|
||||
fn compare_bridge_render_latency() {
|
||||
for diagnostics in [false, true] {
|
||||
let samples = (0..100)
|
||||
.map(|_| {
|
||||
let start = Instant::now();
|
||||
drop(black_box(assemble_template(diagnostics)));
|
||||
start.elapsed().as_nanos()
|
||||
})
|
||||
.collect();
|
||||
report(
|
||||
&format!("bridge template initialization diagnostics={diagnostics}"),
|
||||
samples,
|
||||
);
|
||||
}
|
||||
prepare_templates();
|
||||
for repeat in 0..3 {
|
||||
for diagnostics in [false, true] {
|
||||
for workers in [1, 32] {
|
||||
for cached in [false, true] {
|
||||
let barrier = Barrier::new(workers);
|
||||
let samples = std::thread::scope(|scope| {
|
||||
let handles = (0..workers)
|
||||
.map(|worker| {
|
||||
let barrier = &barrier;
|
||||
scope.spawn(move || {
|
||||
let rng = SecureRandom::new();
|
||||
let mut case = Case::new(worker);
|
||||
case.diagnostics = diagnostics;
|
||||
for _ in 0..20 {
|
||||
drop(case.render(&rng));
|
||||
}
|
||||
let mut samples = Vec::with_capacity(300);
|
||||
barrier.wait();
|
||||
for _ in 0..300 {
|
||||
let start = Instant::now();
|
||||
let page = if cached {
|
||||
case.render(&rng)
|
||||
} else {
|
||||
let mut nonce = [0; 18];
|
||||
rng.fill(&mut nonce);
|
||||
let nonce =
|
||||
base64::engine::general_purpose::URL_SAFE_NO_PAD
|
||||
.encode(nonce);
|
||||
case.reference(&nonce)
|
||||
};
|
||||
drop(black_box(page));
|
||||
samples.push(start.elapsed().as_nanos());
|
||||
}
|
||||
samples
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
handles
|
||||
.into_iter()
|
||||
.flat_map(|h| h.join().unwrap())
|
||||
.collect()
|
||||
});
|
||||
report(
|
||||
&format!(
|
||||
"bridge repeat={repeat} diagnostics={diagnostics} workers={workers} cached={cached}"
|
||||
),
|
||||
samples,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Frozen pre-cache renderer; callers supply the response nonce for exact comparisons.
|
||||
use super::*;
|
||||
|
||||
/// Replays the original substitution chain using the supplied response nonce.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn render(
|
||||
host: &str,
|
||||
base: &str,
|
||||
bootstrap: &str,
|
||||
batch_limit: usize,
|
||||
queue_limit: usize,
|
||||
queue_items: usize,
|
||||
max_streams: usize,
|
||||
negotiation_enabled: bool,
|
||||
candidate_count: usize,
|
||||
carrier_deadlines: [u64; 4],
|
||||
long_poll_secs: u64,
|
||||
bridge_request_secs: u64,
|
||||
bridge_retry_secs: u64,
|
||||
bridge_recovery_secs: u64,
|
||||
websocket_open_secs: u64,
|
||||
reconnect_grace_secs: u64,
|
||||
carrier_probe_coalesce_ms: u64,
|
||||
bridge_diagnostics_enabled: bool,
|
||||
carrier_method: WebCarrierMethod,
|
||||
nonce: &str,
|
||||
) -> BridgePage {
|
||||
let diagnostic_script = if bridge_diagnostics_enabled {
|
||||
format!("<script nonce=\"__NONCE__\">\n{DIAGNOSTIC_RUNTIME}\n</script>\n")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let diagnostic_hook = |method: &str| {
|
||||
bridge_diagnostics_enabled
|
||||
.then(|| {
|
||||
format!("if(clientDiagnostics)try{{clientDiagnostics.{method}()}}catch(error){{}}")
|
||||
})
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let diagnostic_runtime_started = if bridge_diagnostics_enabled {
|
||||
format!("{}\n", diagnostic_hook("runtimeStarted"))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let base_prefix = base.strip_suffix('/').unwrap_or(base);
|
||||
let body = DOCUMENT
|
||||
.replace("__DIAGNOSTIC_RUNTIME__\n", &diagnostic_script)
|
||||
.replace("__RESPONSE_RUNTIME__", RESPONSE_RUNTIME)
|
||||
.replace("__REQUEST_RUNTIME__", REQUEST_RUNTIME)
|
||||
.replace("__BUFFER_RUNTIME__", BUFFER_RUNTIME)
|
||||
.replace("__RECOVERY_RUNTIME__", RECOVERY_RUNTIME)
|
||||
.replace("__DOWNLINK_RUNTIME__", DOWNLINK_RUNTIME)
|
||||
.replace("__CONVEYOR_RUNTIME__", CONVEYOR_RUNTIME)
|
||||
.replace("__RUNTIME__", RUNTIME)
|
||||
.replace(
|
||||
"__DIAGNOSTIC_BINDING__;\n",
|
||||
if bridge_diagnostics_enabled {
|
||||
"const clientDiagnostics=globalThis.TelemtBridgeDiagnostics;\n"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
)
|
||||
.replace(
|
||||
"__DIAGNOSTIC_RUNTIME_STARTED__;\n",
|
||||
&diagnostic_runtime_started,
|
||||
)
|
||||
.replace(
|
||||
"__DIAGNOSTIC_BOUNDARY_ACTIVATED__;",
|
||||
&diagnostic_hook("boundaryActivated"),
|
||||
)
|
||||
.replace(
|
||||
"__STATUS_FUNCTION__",
|
||||
if bridge_diagnostics_enabled {
|
||||
"state=>{if(port&&!closed){port.postMessage({t:'status',state});if(clientDiagnostics)try{clientDiagnostics.statusPosted()}catch(error){}}}"
|
||||
} else {
|
||||
"state=>{if(port&&!closed)port.postMessage({t:'status',state})}"
|
||||
},
|
||||
)
|
||||
.replace(
|
||||
"__DIAGNOSTIC_HELLO_RECEIVED__;",
|
||||
&diagnostic_hook("helloReceived"),
|
||||
)
|
||||
.replace(
|
||||
"__HELLO_TIMEOUT_CALLBACK__",
|
||||
if bridge_diagnostics_enabled {
|
||||
"()=>{if(clientDiagnostics)try{clientDiagnostics.helloTimeout()}catch(error){}fail('timeout')}"
|
||||
} else {
|
||||
"()=>fail('timeout')"
|
||||
},
|
||||
)
|
||||
.replace(
|
||||
"__DIAGNOSTIC_CLIENT_CLOSE__;",
|
||||
&diagnostic_hook("clientCloseBeforeHello"),
|
||||
)
|
||||
.replace(
|
||||
"__PAGEHIDE_CALLBACK__",
|
||||
if bridge_diagnostics_enabled {
|
||||
"()=>{if(clientDiagnostics)try{clientDiagnostics.documentUnloadedBeforeHello()}catch(error){}fail('navigation')}"
|
||||
} else {
|
||||
"()=>fail('navigation')"
|
||||
},
|
||||
)
|
||||
.replace(
|
||||
"__DIAGNOSTIC_BOOTSTRAP_REPLACED__;",
|
||||
if bridge_diagnostics_enabled {
|
||||
"if(clientDiagnostics)try{clientDiagnostics.setBootstrap(bootstrap)}catch(error){}"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
)
|
||||
.replace("__NONCE__", &nonce)
|
||||
.replace("__HOST__", host)
|
||||
.replace("__BASE_PREFIX__", base_prefix)
|
||||
.replace("__BOOTSTRAP__", bootstrap)
|
||||
.replace("__CARRIER_METHOD__", carrier_method.as_str())
|
||||
.replace("__BATCH_LIMIT__", &batch_limit.to_string())
|
||||
.replace("__QUEUE_LIMIT__", &queue_limit.to_string())
|
||||
.replace("__QUEUE_ITEMS__", &queue_items.to_string())
|
||||
.replace("__MAX_STREAMS__", &max_streams.to_string())
|
||||
.replace(
|
||||
"__NEGOTIATION_ENABLED__",
|
||||
if negotiation_enabled { "true" } else { "false" },
|
||||
)
|
||||
.replace("__CANDIDATE_COUNT__", &candidate_count.to_string())
|
||||
.replace("__LONG_POLL_SECS__", &long_poll_secs.to_string())
|
||||
.replace("__BRIDGE_REQUEST_SECS__", &bridge_request_secs.to_string())
|
||||
.replace("__BRIDGE_RETRY_SECS__", &bridge_retry_secs.to_string())
|
||||
.replace(
|
||||
"__BRIDGE_RECOVERY_SECS__",
|
||||
&bridge_recovery_secs.to_string(),
|
||||
)
|
||||
.replace("__WEBSOCKET_OPEN_SECS__", &websocket_open_secs.to_string())
|
||||
.replace(
|
||||
"__RECONNECT_GRACE_SECS__",
|
||||
&reconnect_grace_secs.to_string(),
|
||||
)
|
||||
.replace(
|
||||
"__CARRIER_PROBE_COALESCE_MS__",
|
||||
&carrier_probe_coalesce_ms.to_string(),
|
||||
)
|
||||
.replace(
|
||||
"__CARRIER_DEADLINES__",
|
||||
&carrier_deadlines
|
||||
.iter()
|
||||
.map(u64::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join(","),
|
||||
);
|
||||
BridgePage {
|
||||
body,
|
||||
content_security_policy: format!(
|
||||
"default-src 'none'; base-uri 'none'; child-src 'none'; connect-src 'self' wss://{host}; font-src 'none'; form-action 'none'; frame-ancestors http://127.0.0.1:*; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'nonce-{nonce}'; style-src 'none'; worker-src 'none'; sandbox allow-same-origin allow-scripts"
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{Arc, Barrier};
|
||||
|
||||
#[path = "render_benchmarks.rs"]
|
||||
mod benchmarks;
|
||||
|
||||
#[test]
|
||||
fn disabled_runtime_prewarms_templates_before_hot_enable() {
|
||||
const CHILD: &str = "TELEMT_TEST_BRIDGE_PREWARM_CHILD";
|
||||
if std::env::var_os(CHILD).is_none() {
|
||||
// A fresh process prevents unrelated render tests from satisfying the warmup assertion.
|
||||
let output = std::process::Command::new(std::env::current_exe().unwrap())
|
||||
.args([
|
||||
"--exact",
|
||||
"web::bridge::render_tests::disabled_runtime_prewarms_templates_before_hot_enable",
|
||||
])
|
||||
.env(CHILD, "1")
|
||||
.output()
|
||||
.unwrap();
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(
|
||||
output.status.success() && stdout.contains("1 passed; 0 failed"),
|
||||
"{}\n{}",
|
||||
stdout,
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
return;
|
||||
}
|
||||
assert!(TEMPLATES.iter().all(|template| template.get().is_none()));
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
let mut config = crate::config::ProxyConfig::default();
|
||||
config.web.enabled = false;
|
||||
let generation = crate::maestro::generation::test_runtime_generation(1, config);
|
||||
let runtime = crate::web::manager::WebProcessRuntime::start(Arc::new(
|
||||
arc_swap::ArcSwap::from(Arc::clone(&generation)),
|
||||
));
|
||||
assert!(TEMPLATES.iter().all(|template| template.get().is_some()));
|
||||
runtime.shutdown().await;
|
||||
generation.stop_sessions().await;
|
||||
generation.stop_background_tasks().await;
|
||||
});
|
||||
}
|
||||
|
||||
struct Case {
|
||||
host: String,
|
||||
base: String,
|
||||
bootstrap: String,
|
||||
diagnostics: bool,
|
||||
method: WebCarrierMethod,
|
||||
negotiation: bool,
|
||||
candidates: usize,
|
||||
value: usize,
|
||||
}
|
||||
|
||||
impl Case {
|
||||
fn new(index: usize) -> Self {
|
||||
Self {
|
||||
host: format!("proxy-{index}.example.com"),
|
||||
base: "/telegram/web/".to_string(),
|
||||
bootstrap: format!("{index:043}"),
|
||||
diagnostics: index % 2 != 0,
|
||||
method: if index % 3 == 0 {
|
||||
WebCarrierMethod::Put
|
||||
} else {
|
||||
WebCarrierMethod::Post
|
||||
},
|
||||
negotiation: index % 2 == 0,
|
||||
candidates: index % 4 + 1,
|
||||
value: index + 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, rng: &SecureRandom) -> BridgePage {
|
||||
render(
|
||||
&self.host,
|
||||
&self.base,
|
||||
&self.bootstrap,
|
||||
self.value * 1024,
|
||||
self.value * 4096,
|
||||
self.value * 4,
|
||||
self.value,
|
||||
self.negotiation,
|
||||
self.candidates,
|
||||
[3, 5, 8, 12],
|
||||
self.value as u64,
|
||||
self.value as u64 + 1,
|
||||
self.value as u64 + 2,
|
||||
self.value as u64 + 3,
|
||||
self.value as u64 + 4,
|
||||
self.value as u64 + 5,
|
||||
self.value as u64 - 1,
|
||||
self.diagnostics,
|
||||
self.method,
|
||||
rng,
|
||||
)
|
||||
}
|
||||
|
||||
fn reference(&self, nonce: &str) -> BridgePage {
|
||||
render_reference::render(
|
||||
&self.host,
|
||||
&self.base,
|
||||
&self.bootstrap,
|
||||
self.value * 1024,
|
||||
self.value * 4096,
|
||||
self.value * 4,
|
||||
self.value,
|
||||
self.negotiation,
|
||||
self.candidates,
|
||||
[3, 5, 8, 12],
|
||||
self.value as u64,
|
||||
self.value as u64 + 1,
|
||||
self.value as u64 + 2,
|
||||
self.value as u64 + 3,
|
||||
self.value as u64 + 4,
|
||||
self.value as u64 + 5,
|
||||
self.value as u64 - 1,
|
||||
self.diagnostics,
|
||||
self.method,
|
||||
nonce,
|
||||
)
|
||||
}
|
||||
|
||||
fn assert_page(&self, page: &BridgePage) -> String {
|
||||
let nonce = page
|
||||
.content_security_policy
|
||||
.split_once("script-src 'nonce-")
|
||||
.unwrap()
|
||||
.1
|
||||
.split_once('\'')
|
||||
.unwrap()
|
||||
.0;
|
||||
assert_eq!(nonce.len(), 24);
|
||||
let expected = self.reference(nonce);
|
||||
assert_eq!(page.body, expected.body);
|
||||
assert_eq!(
|
||||
page.content_security_policy,
|
||||
expected.content_security_policy
|
||||
);
|
||||
assert_eq!(
|
||||
page.body.matches(&format!("nonce=\"{nonce}\"")).count(),
|
||||
if self.diagnostics { 8 } else { 7 }
|
||||
);
|
||||
nonce.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cached_templates_preserve_exact_dynamic_substitution_order() {
|
||||
prepare_templates();
|
||||
let rng = SecureRandom::new();
|
||||
let long_base = format!("/{}/", "a".repeat(126));
|
||||
let bases = [
|
||||
"/",
|
||||
"/telegram/web/",
|
||||
"/a__BOOTSTRAP__/",
|
||||
"/a__CARRIER_DEADLINES__/",
|
||||
long_base.as_str(),
|
||||
];
|
||||
for diagnostics in [false, true] {
|
||||
for method in [WebCarrierMethod::Post, WebCarrierMethod::Put] {
|
||||
for negotiation in [false, true] {
|
||||
for candidates in 1..=4 {
|
||||
for base in bases {
|
||||
let mut case = Case::new(candidates);
|
||||
case.diagnostics = diagnostics;
|
||||
case.method = method;
|
||||
case.negotiation = negotiation;
|
||||
case.candidates = candidates;
|
||||
case.base = base.to_string();
|
||||
case.assert_page(&case.render(&rng));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concurrent_responses_keep_nonces_and_configuration_response_owned() {
|
||||
prepare_templates();
|
||||
let pointers = [
|
||||
template(false).as_ptr() as usize,
|
||||
template(true).as_ptr() as usize,
|
||||
];
|
||||
let barrier = Arc::new(Barrier::new(32));
|
||||
let nonces = std::thread::scope(|scope| {
|
||||
let handles = (0..32)
|
||||
.map(|worker| {
|
||||
let barrier = Arc::clone(&barrier);
|
||||
scope.spawn(move || {
|
||||
let rng = SecureRandom::new();
|
||||
barrier.wait();
|
||||
(0..8)
|
||||
.map(|iteration| {
|
||||
let case = Case::new(worker * 8 + iteration);
|
||||
case.assert_page(&case.render(&rng))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
handles
|
||||
.into_iter()
|
||||
.flat_map(|handle| handle.join().unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
assert_eq!(nonces.iter().collect::<HashSet<_>>().len(), nonces.len());
|
||||
assert_eq!(
|
||||
pointers,
|
||||
[
|
||||
template(false).as_ptr() as usize,
|
||||
template(true).as_ptr() as usize
|
||||
]
|
||||
);
|
||||
for diagnostics in [false, true] {
|
||||
assert!(template(diagnostics).contains("__NONCE__"));
|
||||
assert!(template(diagnostics).contains("__BOOTSTRAP__"));
|
||||
assert!(template(diagnostics).contains("__BASE_PREFIX__"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user