refactor(app-proxy): remove the stable time freeze (#2856)

This commit is contained in:
Hampus
2026-09-20 04:02:47 +02:00
committed by GitHub
parent 86043212f2
commit 3256af8d92
17 changed files with 11 additions and 1469 deletions
-3
View File
@@ -23,6 +23,3 @@ tower-http = { version = "0.7.1", features = ["compression-gzip", "trace"] }
tracing = "0.1.44"
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
[features]
default = ["time-freeze"]
time-freeze = []
+1 -7
View File
@@ -2,7 +2,6 @@
ARG BUILD_VERSION=""
ARG PUBLIC_ASSET_BASE_URL=""
ARG FLUXER_APP_PROXY_TIME_FREEZE_ENABLED="true"
ARG BUNDLE_LOCAL_ASSETS="true"
ARG APP_ASSETS_REF=app-assets
ARG APP_ASSETS_PLATFORM=$BUILDPLATFORM
@@ -111,7 +110,6 @@ FROM --platform=${APP_ASSETS_PLATFORM} ${APP_ASSETS_REF} AS app-static
# ---------- Stage 3: Build the Rust proxy binary ----------
FROM rust:1-trixie AS rust-builder
ARG FLUXER_APP_PROXY_TIME_FREEZE_ENABLED="true"
WORKDIR /usr/src/app
@@ -138,11 +136,7 @@ RUN printf '%s\n' \
'strip = "symbols"' \
> Cargo.toml
RUN if [ "${FLUXER_APP_PROXY_TIME_FREEZE_ENABLED}" = "false" ]; then \
cargo build --release -p fluxer_app_proxy --no-default-features; \
else \
cargo build --release -p fluxer_app_proxy; \
fi \
RUN cargo build --release -p fluxer_app_proxy \
&& cp target/release/fluxer_app_proxy /usr/local/bin/fluxer-app-proxy
# ---------- Stage 4: Runtime ----------
-2
View File
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
variable "BUILD_VERSION" { default = "" }
variable "PUBLIC_ASSET_BASE_URL" { default = "" }
variable "FLUXER_APP_PROXY_TIME_FREEZE_ENABLED" { default = "true" }
variable "BUNDLE_LOCAL_ASSETS" { default = "true" }
variable "IMAGE_REPO" { default = "" }
variable "CACHE_FROM" { default = "" }
@@ -26,7 +25,6 @@ target "app-proxy" {
args = {
BUILD_VERSION = BUILD_VERSION
PUBLIC_ASSET_BASE_URL = PUBLIC_ASSET_BASE_URL
FLUXER_APP_PROXY_TIME_FREEZE_ENABLED = FLUXER_APP_PROXY_TIME_FREEZE_ENABLED
BUNDLE_LOCAL_ASSETS = BUNDLE_LOCAL_ASSETS
APP_ASSETS_REF = APP_ASSETS_REF
APP_ASSETS_PLATFORM = APP_ASSETS_PLATFORM
-55
View File
@@ -348,7 +348,6 @@ pub struct AppProxyConfig {
pub discovery_upstream_url: String,
pub discovery_refresh_interval_ms: u64,
pub release_channel: ReleaseChannel,
pub time_freeze_enabled: bool,
pub build_version: String,
pub bootstrap_api_endpoint: String,
pub bootstrap_api_public_endpoint: Option<String>,
@@ -445,7 +444,6 @@ impl AppProxyConfig {
&["RELEASE_CHANNEL"],
"stable",
));
let time_freeze_enabled = resolve_time_freeze_enabled_from_env();
let geoip_source = cfg::parse_geoip_source_config(
&cfg::read_first_env(&["FLUXER_GEOIP_DB_PATH", "MAXMIND_DB_PATH"], ""),
"app_proxy",
@@ -489,7 +487,6 @@ impl AppProxyConfig {
60_000u64,
),
release_channel,
time_freeze_enabled,
build_version: cfg::read_env_preferred(
&["BUILD_VERSION", "FLUXER_BUILD_VERSION"],
env!("CARGO_PKG_VERSION"),
@@ -522,10 +519,6 @@ fn resolve_discovery_upstream_url_from_env() -> String {
resolve_discovery_upstream_url(|name| env::var(name).ok())
}
fn resolve_time_freeze_enabled_from_env() -> bool {
resolve_time_freeze_enabled(|name| env::var(name).ok())
}
fn resolve_bootstrap_api_public_endpoint_from_env() -> Option<String> {
resolve_bootstrap_api_public_endpoint(|name| env::var(name).ok())
.unwrap_or_else(|error| panic!("{error}"))
@@ -550,24 +543,6 @@ where
)))
}
fn resolve_time_freeze_enabled<F>(mut read_var: F) -> bool
where
F: FnMut(&str) -> Option<String>,
{
if let Some(value) = read_var("FLUXER_APP_PROXY_TIME_FREEZE_ENABLED") {
return parse_boolish(&value);
}
!read_var("FLUXER_SELF_HOSTED").is_some_and(|value| parse_boolish(&value))
}
fn parse_boolish(value: &str) -> bool {
matches!(
value.trim().to_ascii_lowercase().as_str(),
"1" | "true" | "yes" | "on"
)
}
fn resolve_discovery_upstream_url<F>(mut read_var: F) -> String
where
F: FnMut(&str) -> Option<String>,
@@ -613,11 +588,6 @@ mod tests {
resolve_discovery_upstream_url(|name| env.get(name).map(|value| value.to_string()))
}
fn resolve_time_freeze_from_pairs(pairs: &[(&str, &str)]) -> bool {
let env: HashMap<&str, &str> = pairs.iter().copied().collect();
resolve_time_freeze_enabled(|name| env.get(name).map(|value| value.to_string()))
}
fn resolve_bootstrap_endpoint_from_pairs(pairs: &[(&str, &str)]) -> Option<String> {
try_resolve_bootstrap_endpoint_from_pairs(pairs).expect("the boot html endpoint resolves")
}
@@ -817,29 +787,4 @@ mod tests {
DEFAULT_DISCOVERY_UPSTREAM_URL
);
}
#[test]
fn time_freeze_enabled_by_default_for_hosted_runtime() {
assert!(resolve_time_freeze_from_pairs(&[]));
}
#[test]
fn time_freeze_disabled_by_default_for_self_hosted_runtime() {
assert!(!resolve_time_freeze_from_pairs(&[(
"FLUXER_SELF_HOSTED",
"true"
)]));
}
#[test]
fn explicit_time_freeze_setting_overrides_self_hosted_default() {
assert!(resolve_time_freeze_from_pairs(&[
("FLUXER_SELF_HOSTED", "true"),
("FLUXER_APP_PROXY_TIME_FREEZE_ENABLED", "true"),
]));
assert!(!resolve_time_freeze_from_pairs(&[
("FLUXER_SELF_HOSTED", "false"),
("FLUXER_APP_PROXY_TIME_FREEZE_ENABLED", "false"),
]));
}
}
-371
View File
@@ -1,371 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use crate::time_freeze::FrozenSnapshot;
use base64::Engine;
use std::sync::LazyLock;
const STABLE_SNAPSHOT_SHA: &str = "3250c73cbf94e97e8b9ca5a0ecb03c20bdde3867";
const STABLE_INDEX_HTML: &str = "\
PCFkb2N0eXBlIGh0bWw+PGh0bWwgbGFuZz0iZW4iPjxoZWFkPjxtZXRhIGNoYXJzZXQ9InV0Zi04Ij48dGl0bGU+Rmx1eGVyPC90\
aXRsZT48bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLGluaXRpYWwtc2NhbGU9MSx2aWV3\
cG9ydC1maXQ9Y292ZXIsaW50ZXJhY3RpdmUtd2lkZ2V0PXJlc2l6ZXMtY29udGVudCxtYXhpbXVtLXNjYWxlPTEsdXNlci1zY2Fs\
YWJsZT1ubyI+PG1ldGEgbmFtZT0iZGVzY3JpcHRpb24iIGNvbnRlbnQ9IkZsdXhlciBpcyBhIGZyZWUgYW5kIG9wZW4gc291cmNl\
IGluc3RhbnQgbWVzc2FnaW5nIGFuZCBWb0lQIHBsYXRmb3JtIGJ1aWx0IGZvciBmcmllbmRzLCBncm91cHMsIGFuZCBjb21tdW5p\
dGllcy4iPjxsaW5rIHJlbD0icHJlY29ubmVjdCIgaHJlZj0iaHR0cHM6Ly9mbHV4ZXJzdGF0aWMuY29tIj48bGluayByZWw9InN0\
eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vZmx1eGVyc3RhdGljLmNvbS9mb250cy9pYm0tcGxleC5jc3MiPjxsaW5rIHJlbD0ibWFu\
aWZlc3QiIGhyZWY9Ii9tYW5pZmVzdC5qc29uIj48bGluayByZWw9Imljb24iIHR5cGU9ImltYWdlL3BuZyIgc2l6ZXM9IjMyeDMy\
IiBocmVmPSJodHRwczovL2ZsdXhlcnN0YXRpYy5jb20vd2ViL2Zhdmljb24tMzJ4MzIucG5nIj48bGluayByZWw9ImFwcGxlLXRv\
dWNoLWljb24iIHNpemVzPSIxODB4MTgwIiBocmVmPSJodHRwczovL2ZsdXhlcnN0YXRpYy5jb20vd2ViL2FwcGxlLXRvdWNoLWlj\
b24ucG5nIj48bWV0YSBuYW1lPSJhcHBsZS1tb2JpbGUtd2ViLWFwcC1jYXBhYmxlIiBjb250ZW50PSJ5ZXMiPjxtZXRhIG5hbWU9\
ImFwcGxlLW1vYmlsZS13ZWItYXBwLXN0YXR1cy1iYXItc3R5bGUiIGNvbnRlbnQ9ImRlZmF1bHQiPjxtZXRhIG5hbWU9ImFwcGxl\
LW1vYmlsZS13ZWItYXBwLXRpdGxlIiBjb250ZW50PSJGbHV4ZXIiPjxtZXRhIG5hbWU9Im1vYmlsZS13ZWItYXBwLWNhcGFibGUi\
IGNvbnRlbnQ9InllcyI+PG1ldGEgbmFtZT0idGhlbWUtY29sb3IiIGNvbnRlbnQ9IiM0NjQxRDkiPjxtZXRhIG5hbWU9Im1zYXBw\
bGljYXRpb24tbmF2YnV0dG9uLWNvbG9yIiBjb250ZW50PSIjNDY0MUQ5Ij48bWV0YSBuYW1lPSJtc2FwcGxpY2F0aW9uLVRpbGVD\
b2xvciIgY29udGVudD0iIzQ2NDFEOSI+PG1ldGEgbmFtZT0iZm9ybWF0LWRldGVjdGlvbiIgY29udGVudD0idGVsZXBob25lPW5v\
Ij48bWV0YSBuYW1lPSJkaXNwbGF5IiBjb250ZW50PSJzdGFuZGFsb25lIj48c2NyaXB0IG5vbmNlPSJ7e0NTUF9OT05DRV9QTEFD\
RUhPTERFUn19Ij4hZnVuY3Rpb24oKXt0cnl7dmFyIGU9d2luZG93LmxvY2F0aW9uO2lmKCIvIj09PWUucGF0aG5hbWUpe3ZhciBh\
PSIvY2hhbm5lbHMvQG1lIjtlLnNlYXJjaCYmKGErPWUuc2VhcmNoKSxlLmhhc2gmJihhKz1lLmhhc2gpLGUucmVwbGFjZShhKX19\
Y2F0Y2goZSl7fX0oKSxmdW5jdGlvbigpe3RyeXt2YXIgZT1sb2NhbFN0b3JhZ2UuZ2V0SXRlbSgidGhlbWUiKTtlJiZkb2N1bWVu\
dC5kb2N1bWVudEVsZW1lbnQuY2xhc3NMaXN0LmFkZCgidGhlbWUtIitlKX1jYXRjaHt9fSgpPC9zY3JpcHQ+PGxpbmsgaHJlZj0i\
aHR0cHM6Ly9mbHV4ZXJzdGF0aWMuY29tL2Fzc2V0cy9iOTNiNDgxOTU4OTA5N2NlLmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij48bGlu\
ayBocmVmPSJodHRwczovL2ZsdXhlcnN0YXRpYy5jb20vYXNzZXRzL2YzNzMyZTk1YzczYmQ3N2MuY3NzIiByZWw9InN0eWxlc2hl\
ZXQiPjwvaGVhZD48Ym9keT48ZGl2IGlkPSJyb290Ij48L2Rpdj4KPG5vc2NyaXB0PkphdmFTY3JpcHQgaXMgcmVxdWlyZWQgdG8g\
dXNlIHRoaXMgYXBwbGljYXRpb24uPC9ub3NjcmlwdD4KCgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9mbHV4ZXJzdGF0aWMuY29tL2Fz\
c2V0cy84N2JiZWI3ZDZlMzY2YjNkLmpzIiB0eXBlPSJtb2R1bGUiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSJodHRwczovL2ZsdXhl\
cnN0YXRpYy5jb20vYXNzZXRzL2JlMWRlM2U0OWI1NWM2ZWEuanMiIHR5cGU9Im1vZHVsZSI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9\
Imh0dHBzOi8vZmx1eGVyc3RhdGljLmNvbS9hc3NldHMvMWY0OWE0NzEyMTg5OTlhZC5qcyIgdHlwZT0ibW9kdWxlIj48L3Njcmlw\
dD48c2NyaXB0IHNyYz0iaHR0cHM6Ly9mbHV4ZXJzdGF0aWMuY29tL2Fzc2V0cy8wYmY4ZDMwZDM2ZTdjMzk0LmpzIiB0eXBlPSJt\
b2R1bGUiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSJodHRwczovL2ZsdXhlcnN0YXRpYy5jb20vYXNzZXRzLzY0OTAzOTNlYTEyODc4\
NmYuanMiIHR5cGU9Im1vZHVsZSI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Imh0dHBzOi8vZmx1eGVyc3RhdGljLmNvbS9hc3NldHMv\
YTJmYzQ2YTM2OTE2OGMxNy5qcyIgdHlwZT0ibW9kdWxlIj48L3NjcmlwdD48c2NyaXB0IHNyYz0iaHR0cHM6Ly9mbHV4ZXJzdGF0\
aWMuY29tL2Fzc2V0cy82ZGNkMzI5MTkxNjJhMzZiLmpzIiB0eXBlPSJtb2R1bGUiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSJodHRw\
czovL2ZsdXhlcnN0YXRpYy5jb20vYXNzZXRzLzNiYzNhNThiM2M3MDJhYmEuanMiIHR5cGU9Im1vZHVsZSI+PC9zY3JpcHQ+PHNj\
cmlwdCBzcmM9Imh0dHBzOi8vZmx1eGVyc3RhdGljLmNvbS9hc3NldHMvM2NhYWY1MzA0ODI3ODUwOC5qcyIgdHlwZT0ibW9kdWxl\
Ij48L3NjcmlwdD48c2NyaXB0IHNyYz0iaHR0cHM6Ly9mbHV4ZXJzdGF0aWMuY29tL2Fzc2V0cy8xM2EwZDZjY2I3YzllZjFjLmpz\
IiB0eXBlPSJtb2R1bGUiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSJodHRwczovL2ZsdXhlcnN0YXRpYy5jb20vYXNzZXRzL2NlZGE5\
MTIyZWMyMDI4OTIuanMiIHR5cGU9Im1vZHVsZSI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Imh0dHBzOi8vZmx1eGVyc3RhdGljLmNv\
bS9hc3NldHMvYTcyYTQzNzIyMTljNzNlYi5qcyIgdHlwZT0ibW9kdWxlIj48L3NjcmlwdD48c2NyaXB0IHNyYz0iaHR0cHM6Ly9m\
bHV4ZXJzdGF0aWMuY29tL2Fzc2V0cy80N2JmYzQ5Nzg3Yjc5MDQ4LmpzIiB0eXBlPSJtb2R1bGUiPjwvc2NyaXB0PjxzY3JpcHQg\
c3JjPSJodHRwczovL2ZsdXhlcnN0YXRpYy5jb20vYXNzZXRzL2M2ZjEyY2Q5NjA3NzE5OTUuanMiIHR5cGU9Im1vZHVsZSI+PC9z\
Y3JpcHQ+PHNjcmlwdCBzcmM9Imh0dHBzOi8vZmx1eGVyc3RhdGljLmNvbS9hc3NldHMvMjNlOGZiZTIzMDdjMWFmNS5qcyIgdHlw\
ZT0ibW9kdWxlIj48L3NjcmlwdD48c2NyaXB0IHNyYz0iaHR0cHM6Ly9mbHV4ZXJzdGF0aWMuY29tL2Fzc2V0cy9hMTkxYjQyNmI1\
MWU5MjA4LmpzIiB0eXBlPSJtb2R1bGUiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSJodHRwczovL2ZsdXhlcnN0YXRpYy5jb20vYXNz\
ZXRzLzRlZjY0NDEzYzhjZTVhMzYuanMiIHR5cGU9Im1vZHVsZSI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Imh0dHBzOi8vZmx1eGVy\
c3RhdGljLmNvbS9hc3NldHMvMzI1MGM3M2NiZjk0ZTk3ZS5qcyIgdHlwZT0ibW9kdWxlIj48L3NjcmlwdD48L2JvZHk+PC9odG1s\
Pg==\
";
const STABLE_SW_JS: &str = "\
InVzZSBzdHJpY3QiOwooKCkgPT4gewogIC8vIHNyYy9saWIvU2VydmljZVdvcmtlckxvZ0RCLnRzeAogIHZhciBTV19MT0dfREJf\
TkFNRSA9ICJGbHV4ZXJTd0xvZ3MiOwogIHZhciBTV19MT0dfREJfVkVSU0lPTiA9IDE7CiAgdmFyIExPR1NfU1RPUkUgPSAibG9n\
cyI7CiAgdmFyIE1FVEFfU1RPUkUgPSAibWV0YSI7CiAgdmFyIEVOQUJMRURfS0VZID0gImVuYWJsZWQiOwogIHZhciBNQVhfTE9H\
UyA9IDJlMzsKICB2YXIgVFJJTV9UUklHR0VSID0gMjIwMDsKICB2YXIgaWRiQXZhaWxhYmxlID0gKCkgPT4gdHlwZW9mIGluZGV4\
ZWREQiAhPT0gInVuZGVmaW5lZCI7CiAgdmFyIGRiUHJvbWlzZSA9IG51bGw7CiAgdmFyIG9wZW5EYiA9ICgpID0+IHsKICAgIGlm\
IChkYlByb21pc2UpIHJldHVybiBkYlByb21pc2U7CiAgICBpZiAoIWlkYkF2YWlsYWJsZSgpKSB7CiAgICAgIHJldHVybiBQcm9t\
aXNlLnJlamVjdChuZXcgRXJyb3IoIkluZGV4ZWREQiB1bmF2YWlsYWJsZSIpKTsKICAgIH0KICAgIGRiUHJvbWlzZSA9IG5ldyBQ\
cm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgbGV0IHJlcTsKICAgICAgdHJ5IHsKICAgICAgICByZXEgPSBpbmRl\
eGVkREIub3BlbihTV19MT0dfREJfTkFNRSwgU1dfTE9HX0RCX1ZFUlNJT04pOwogICAgICB9IGNhdGNoIChlcnIpIHsKICAgICAg\
ICByZWplY3QoZXJyKTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgcmVxLm9udXBncmFkZW5lZWRlZCA9ICgpID0+IHsK\
ICAgICAgICBjb25zdCBkYiA9IHJlcS5yZXN1bHQ7CiAgICAgICAgaWYgKCFkYi5vYmplY3RTdG9yZU5hbWVzLmNvbnRhaW5zKExP\
R1NfU1RPUkUpKSB7CiAgICAgICAgICBjb25zdCBzdG9yZSA9IGRiLmNyZWF0ZU9iamVjdFN0b3JlKExPR1NfU1RPUkUsIHsga2V5\
UGF0aDogImlkIiwgYXV0b0luY3JlbWVudDogdHJ1ZSB9KTsKICAgICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCJ0cyIsICJ0cyIp\
OwogICAgICAgIH0KICAgICAgICBpZiAoIWRiLm9iamVjdFN0b3JlTmFtZXMuY29udGFpbnMoTUVUQV9TVE9SRSkpIHsKICAgICAg\
ICAgIGRiLmNyZWF0ZU9iamVjdFN0b3JlKE1FVEFfU1RPUkUpOwogICAgICAgIH0KICAgICAgfTsKICAgICAgcmVxLm9uc3VjY2Vz\
cyA9ICgpID0+IHsKICAgICAgICBjb25zdCBkYiA9IHJlcS5yZXN1bHQ7CiAgICAgICAgZGIub25jbG9zZSA9ICgpID0+IHsKICAg\
ICAgICAgIGRiUHJvbWlzZSA9IG51bGw7CiAgICAgICAgfTsKICAgICAgICBkYi5vbnZlcnNpb25jaGFuZ2UgPSAoKSA9PiB7CiAg\
ICAgICAgICB0cnkgewogICAgICAgICAgICBkYi5jbG9zZSgpOwogICAgICAgICAgfSBjYXRjaCB7CiAgICAgICAgICB9CiAgICAg\
ICAgICBkYlByb21pc2UgPSBudWxsOwogICAgICAgIH07CiAgICAgICAgcmVzb2x2ZShkYik7CiAgICAgIH07CiAgICAgIHJlcS5v\
bmVycm9yID0gKCkgPT4gcmVqZWN0KHJlcS5lcnJvciA/PyBuZXcgRXJyb3IoIkluZGV4ZWREQiBvcGVuIGZhaWxlZCIpKTsKICAg\
ICAgcmVxLm9uYmxvY2tlZCA9ICgpID0+IHJlamVjdChuZXcgRXJyb3IoIkluZGV4ZWREQiBvcGVuIGJsb2NrZWQiKSk7CiAgICB9\
KS5jYXRjaCgoZXJyKSA9PiB7CiAgICAgIGRiUHJvbWlzZSA9IG51bGw7CiAgICAgIHRocm93IGVycjsKICAgIH0pOwogICAgcmV0\
dXJuIGRiUHJvbWlzZTsKICB9OwogIHZhciBzZXJpYWxpemVEYXRhID0gKHZhbHVlLCBkZXB0aCA9IDApID0+IHsKICAgIGlmICh2\
YWx1ZSA9PSBudWxsKSByZXR1cm4gdmFsdWU7CiAgICBjb25zdCB0ID0gdHlwZW9mIHZhbHVlOwogICAgaWYgKHQgPT09ICJzdHJp\
bmciIHx8IHQgPT09ICJudW1iZXIiIHx8IHQgPT09ICJib29sZWFuIikgcmV0dXJuIHZhbHVlOwogICAgaWYgKHQgPT09ICJiaWdp\
bnQiKSByZXR1cm4gdmFsdWUudG9TdHJpbmcoKTsKICAgIGlmICh2YWx1ZSBpbnN0YW5jZW9mIEVycm9yKSB7CiAgICAgIHJldHVy\
biB7IF9fdHlwZTogIkVycm9yIiwgbmFtZTogdmFsdWUubmFtZSwgbWVzc2FnZTogdmFsdWUubWVzc2FnZSwgc3RhY2s6IHZhbHVl\
LnN0YWNrIH07CiAgICB9CiAgICBpZiAodmFsdWUgaW5zdGFuY2VvZiBEYXRlKSByZXR1cm4gdmFsdWUudG9JU09TdHJpbmcoKTsK\
ICAgIGlmIChkZXB0aCA+IDQpIHJldHVybiAiW3RydW5jYXRlZF0iOwogICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7CiAg\
ICAgIHJldHVybiB2YWx1ZS5zbGljZSgwLCA1MCkubWFwKChpdGVtKSA9PiBzZXJpYWxpemVEYXRhKGl0ZW0sIGRlcHRoICsgMSkp\
OwogICAgfQogICAgaWYgKHQgPT09ICJvYmplY3QiKSB7CiAgICAgIGNvbnN0IG91dCA9IHt9OwogICAgICBsZXQgY291bnQgPSAw\
OwogICAgICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyh2YWx1ZSkpIHsKICAgICAgICBpZiAoY291bnQgPj0gNTApIHsK\
ICAgICAgICAgIG91dFsiX190cnVuY2F0ZWQiXSA9IHRydWU7CiAgICAgICAgICBicmVhazsKICAgICAgICB9CiAgICAgICAgdHJ5\
IHsKICAgICAgICAgIG91dFtrZXldID0gc2VyaWFsaXplRGF0YSh2YWx1ZVtrZXldLCBkZXB0aCArIDEpOwogICAgICAgIH0gY2F0\
Y2ggewogICAgICAgICAgb3V0W2tleV0gPSAiW3Vuc2VyaWFsaXphYmxlXSI7CiAgICAgICAgfQogICAgICAgIGNvdW50ICs9IDE7\
CiAgICAgIH0KICAgICAgcmV0dXJuIG91dDsKICAgIH0KICAgIHJldHVybiBgWyR7dH1dYDsKICB9OwogIGFzeW5jIGZ1bmN0aW9u\
IGFwcGVuZFN3TG9nKGxldmVsLCBzb3VyY2UsIG1lc3NhZ2UsIGRhdGEpIHsKICAgIGlmICghaWRiQXZhaWxhYmxlKCkpIHJldHVy\
bjsKICAgIGxldCBkYjsKICAgIHRyeSB7CiAgICAgIGRiID0gYXdhaXQgb3BlbkRiKCk7CiAgICB9IGNhdGNoIHsKICAgICAgcmV0\
dXJuOwogICAgfQogICAgY29uc3QgcmVjb3JkID0gewogICAgICB0czogRGF0ZS5ub3coKSwKICAgICAgbGV2ZWwsCiAgICAgIHNv\
dXJjZSwKICAgICAgbWVzc2FnZSwKICAgICAgZGF0YTogZGF0YSA9PT0gdm9pZCAwID8gdm9pZCAwIDogc2VyaWFsaXplRGF0YShk\
YXRhKQogICAgfTsKICAgIHRyeSB7CiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAg\
ICBjb25zdCB0eCA9IGRiLnRyYW5zYWN0aW9uKExPR1NfU1RPUkUsICJyZWFkd3JpdGUiKTsKICAgICAgICB0eC5vbmNvbXBsZXRl\
ID0gKCkgPT4gcmVzb2x2ZSgpOwogICAgICAgIHR4Lm9uZXJyb3IgPSAoKSA9PiByZWplY3QodHguZXJyb3IpOwogICAgICAgIHR4\
Lm9uYWJvcnQgPSAoKSA9PiByZWplY3QodHguZXJyb3IpOwogICAgICAgIHR4Lm9iamVjdFN0b3JlKExPR1NfU1RPUkUpLmFkZChy\
ZWNvcmQpOwogICAgICB9KTsKICAgIH0gY2F0Y2ggewogICAgICByZXR1cm47CiAgICB9CiAgICB2b2lkIHNjaGVkdWxlVHJpbSgp\
OwogIH0KICB2YXIgdHJpbVNjaGVkdWxlZCA9IGZhbHNlOwogIHZhciBzY2hlZHVsZVRyaW0gPSBhc3luYyAoKSA9PiB7CiAgICBp\
ZiAodHJpbVNjaGVkdWxlZCkgcmV0dXJuOwogICAgdHJpbVNjaGVkdWxlZCA9IHRydWU7CiAgICB0cnkgewogICAgICBhd2FpdCB0\
cmltSWZOZWVkZWQoKTsKICAgIH0gZmluYWxseSB7CiAgICAgIHRyaW1TY2hlZHVsZWQgPSBmYWxzZTsKICAgIH0KICB9OwogIGFz\
eW5jIGZ1bmN0aW9uIHRyaW1JZk5lZWRlZCgpIHsKICAgIGxldCBkYjsKICAgIHRyeSB7CiAgICAgIGRiID0gYXdhaXQgb3BlbkRi\
KCk7CiAgICB9IGNhdGNoIHsKICAgICAgcmV0dXJuOwogICAgfQogICAgbGV0IGNvdW50ID0gMDsKICAgIHRyeSB7CiAgICAgIGNv\
dW50ID0gYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGNvbnN0IHR4ID0gZGIudHJhbnNh\
Y3Rpb24oTE9HU19TVE9SRSwgInJlYWRvbmx5Iik7CiAgICAgICAgY29uc3QgcmVxID0gdHgub2JqZWN0U3RvcmUoTE9HU19TVE9S\
RSkuY291bnQoKTsKICAgICAgICByZXEub25zdWNjZXNzID0gKCkgPT4gcmVzb2x2ZShyZXEucmVzdWx0KTsKICAgICAgICByZXEu\
b25lcnJvciA9ICgpID0+IHJlamVjdChyZXEuZXJyb3IpOwogICAgICB9KTsKICAgIH0gY2F0Y2ggewogICAgICByZXR1cm47CiAg\
ICB9CiAgICBpZiAoY291bnQgPCBUUklNX1RSSUdHRVIpIHJldHVybjsKICAgIGNvbnN0IHRvRGVsZXRlID0gY291bnQgLSBNQVhf\
TE9HUzsKICAgIHRyeSB7CiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBjb25z\
dCB0eCA9IGRiLnRyYW5zYWN0aW9uKExPR1NfU1RPUkUsICJyZWFkd3JpdGUiKTsKICAgICAgICB0eC5vbmNvbXBsZXRlID0gKCkg\
PT4gcmVzb2x2ZSgpOwogICAgICAgIHR4Lm9uZXJyb3IgPSAoKSA9PiByZWplY3QodHguZXJyb3IpOwogICAgICAgIHR4Lm9uYWJv\
cnQgPSAoKSA9PiByZWplY3QodHguZXJyb3IpOwogICAgICAgIGNvbnN0IHN0b3JlID0gdHgub2JqZWN0U3RvcmUoTE9HU19TVE9S\
RSk7CiAgICAgICAgY29uc3QgY3Vyc29yUmVxID0gc3RvcmUuaW5kZXgoInRzIikub3BlbkN1cnNvcigpOwogICAgICAgIGxldCBk\
ZWxldGVkID0gMDsKICAgICAgICBjdXJzb3JSZXEub25zdWNjZXNzID0gKCkgPT4gewogICAgICAgICAgY29uc3QgY3Vyc29yID0g\
Y3Vyc29yUmVxLnJlc3VsdDsKICAgICAgICAgIGlmICghY3Vyc29yIHx8IGRlbGV0ZWQgPj0gdG9EZWxldGUpIHJldHVybjsKICAg\
ICAgICAgIGN1cnNvci5kZWxldGUoKTsKICAgICAgICAgIGRlbGV0ZWQgKz0gMTsKICAgICAgICAgIGN1cnNvci5jb250aW51ZSgp\
OwogICAgICAgIH07CiAgICAgICAgY3Vyc29yUmVxLm9uZXJyb3IgPSAoKSA9PiByZWplY3QoY3Vyc29yUmVxLmVycm9yKTsKICAg\
ICAgfSk7CiAgICB9IGNhdGNoIHsKICAgIH0KICB9CiAgYXN5bmMgZnVuY3Rpb24gaXNTd0xvZ2dpbmdFbmFibGVkKCkgewogICAg\
aWYgKCFpZGJBdmFpbGFibGUoKSkgcmV0dXJuIGZhbHNlOwogICAgbGV0IGRiOwogICAgdHJ5IHsKICAgICAgZGIgPSBhd2FpdCBv\
cGVuRGIoKTsKICAgIH0gY2F0Y2ggewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJl\
c29sdmUpID0+IHsKICAgICAgY29uc3QgdHggPSBkYi50cmFuc2FjdGlvbihNRVRBX1NUT1JFLCAicmVhZG9ubHkiKTsKICAgICAg\
Y29uc3QgcmVxID0gdHgub2JqZWN0U3RvcmUoTUVUQV9TVE9SRSkuZ2V0KEVOQUJMRURfS0VZKTsKICAgICAgcmVxLm9uc3VjY2Vz\
cyA9ICgpID0+IHJlc29sdmUocmVxLnJlc3VsdCA9PT0gdHJ1ZSk7CiAgICAgIHJlcS5vbmVycm9yID0gKCkgPT4gcmVzb2x2ZShm\
YWxzZSk7CiAgICB9KTsKICB9CiAgdmFyIFNXX0xPR19TRVRfRU5BQkxFRF9NRVNTQUdFID0gIlNXX0xPR19TRVRfRU5BQkxFRCI7\
CgogIC8vIHNyYy9zZXJ2aWNlX3dvcmtlci9Xb3JrZXIudHN4CiAgdmFyIGxvZ2dpbmdFbmFibGVkID0gZmFsc2U7CiAgdmFyIGVu\
c3VyZUxvZ2dpbmdFbmFibGVkTG9hZGVkID0gaXNTd0xvZ2dpbmdFbmFibGVkKCkudGhlbigKICAgICh2KSA9PiB7CiAgICAgIGxv\
Z2dpbmdFbmFibGVkID0gdjsKICAgIH0sCiAgICAoKSA9PiB2b2lkIDAKICApOwogIHZhciBsb2cgPSBhc3luYyAobGV2ZWwsIG1l\
c3NhZ2UsIGRhdGEpID0+IHsKICAgIHRyeSB7CiAgICAgIGlmICh0eXBlb2YgY29uc29sZSAhPT0gInVuZGVmaW5lZCIpIHsKICAg\
ICAgICBjb25zdCBmbiA9IGxldmVsID09PSAiZXJyb3IiID8gY29uc29sZS5lcnJvciA6IGxldmVsID09PSAid2FybiIgPyBjb25z\
b2xlLndhcm4gOiBsZXZlbCA9PT0gImluZm8iID8gY29uc29sZS5pbmZvIDogY29uc29sZS5kZWJ1ZzsKICAgICAgICBpZiAoZGF0\
YSAhPT0gdm9pZCAwKSBmbj8uY2FsbChjb25zb2xlLCAiW1NXXSIsIG1lc3NhZ2UsIGRhdGEpOwogICAgICAgIGVsc2UgZm4/LmNh\
bGwoY29uc29sZSwgIltTV10iLCBtZXNzYWdlKTsKICAgICAgfQogICAgfSBjYXRjaCB7CiAgICB9CiAgICBpZiAoIWxvZ2dpbmdF\
bmFibGVkKSByZXR1cm47CiAgICB0cnkgewogICAgICBhd2FpdCBhcHBlbmRTd0xvZyhsZXZlbCwgInN3IiwgbWVzc2FnZSwgZGF0\
YSk7CiAgICB9IGNhdGNoIHsKICAgIH0KICB9OwogIHNlbGYuYWRkRXZlbnRMaXN0ZW5lcigiaW5zdGFsbCIsIChldmVudCkgPT4g\
ewogICAgZXZlbnQud2FpdFVudGlsKAogICAgICAoYXN5bmMgKCkgPT4gewogICAgICAgIGF3YWl0IGVuc3VyZUxvZ2dpbmdFbmFi\
bGVkTG9hZGVkOwogICAgICAgIGF3YWl0IGxvZygiaW5mbyIsICJpbnN0YWxsIik7CiAgICAgICAgc2VsZi5za2lwV2FpdGluZygp\
OwogICAgICB9KSgpCiAgICApOwogIH0pOwogIHNlbGYuYWRkRXZlbnRMaXN0ZW5lcigiYWN0aXZhdGUiLCAoZXZlbnQpID0+IHsK\
ICAgIGV2ZW50LndhaXRVbnRpbCgKICAgICAgKGFzeW5jICgpID0+IHsKICAgICAgICBhd2FpdCBlbnN1cmVMb2dnaW5nRW5hYmxl\
ZExvYWRlZDsKICAgICAgICBhd2FpdCBsb2coImluZm8iLCAiYWN0aXZhdGUiKTsKICAgICAgICBhd2FpdCBzZWxmLmNsaWVudHMu\
Y2xhaW0oKTsKICAgICAgICBhd2FpdCBsb2coImRlYnVnIiwgImNsaWVudHMgY2xhaW1lZCIpOwogICAgICB9KSgpCiAgICApOwog\
IH0pOwogIHNlbGYuYWRkRXZlbnRMaXN0ZW5lcigibWVzc2FnZSIsIChldmVudCkgPT4gewogICAgY29uc3QgZGF0YSA9IGV2ZW50\
LmRhdGE7CiAgICBjb25zdCB0eXBlID0gZGF0YT8udHlwZTsKICAgIGlmICh0eXBlID09PSAiU0tJUF9XQUlUSU5HIikgewogICAg\
ICBldmVudC53YWl0VW50aWwobG9nKCJpbmZvIiwgIm1lc3NhZ2U6IFNLSVBfV0FJVElORyIpKTsKICAgICAgc2VsZi5za2lwV2Fp\
dGluZygpOwogICAgICByZXR1cm47CiAgICB9CiAgICBpZiAodHlwZSA9PT0gIkFQUF9VUERBVEVfQkFER0UiKSB7CiAgICAgIGNv\
bnN0IHJhd0NvdW50ID0gZGF0YT8uY291bnQ7CiAgICAgIGxldCBiYWRnZUNvdW50ID0gbnVsbDsKICAgICAgaWYgKHR5cGVvZiBy\
YXdDb3VudCA9PT0gIm51bWJlciIgJiYgTnVtYmVyLmlzRmluaXRlKHJhd0NvdW50KSkgewogICAgICAgIGJhZGdlQ291bnQgPSBy\
YXdDb3VudDsKICAgICAgfSBlbHNlIGlmICh0eXBlb2YgcmF3Q291bnQgPT09ICJzdHJpbmciICYmIHJhd0NvdW50Lmxlbmd0aCA+\
IDApIHsKICAgICAgICBjb25zdCBwYXJzZWQgPSBOdW1iZXIocmF3Q291bnQpOwogICAgICAgIGJhZGdlQ291bnQgPSBOdW1iZXIu\
aXNGaW5pdGUocGFyc2VkKSA/IHBhcnNlZCA6IG51bGw7CiAgICAgIH0KICAgICAgZXZlbnQud2FpdFVudGlsKAogICAgICAgIChh\
c3luYyAoKSA9PiB7CiAgICAgICAgICBhd2FpdCBsb2coImRlYnVnIiwgIm1lc3NhZ2U6IEFQUF9VUERBVEVfQkFER0UiLCB7IGJh\
ZGdlQ291bnQgfSk7CiAgICAgICAgICBhd2FpdCB1cGRhdGVBcHBCYWRnZShiYWRnZUNvdW50KTsKICAgICAgICB9KSgpCiAgICAg\
ICk7CiAgICAgIHJldHVybjsKICAgIH0KICAgIGlmICh0eXBlID09PSBTV19MT0dfU0VUX0VOQUJMRURfTUVTU0FHRSkgewogICAg\
ICBjb25zdCBlbmFibGVkID0gZGF0YT8uZW5hYmxlZCA9PT0gdHJ1ZTsKICAgICAgbG9nZ2luZ0VuYWJsZWQgPSBlbmFibGVkOwog\
ICAgICBldmVudC53YWl0VW50aWwobG9nKCJpbmZvIiwgYG1lc3NhZ2U6IGxvZ2dpbmcgJHtlbmFibGVkID8gImVuYWJsZWQiIDog\
ImRpc2FibGVkIn1gKSk7CiAgICAgIHJldHVybjsKICAgIH0KICAgIGV2ZW50LndhaXRVbnRpbChsb2coImRlYnVnIiwgIm1lc3Nh\
Z2U6IHVua25vd24iLCB7IHR5cGUgfSkpOwogIH0pOwogIHZhciBub3JtYWxpemVQdXNoUGF5bG9hZCA9IChyYXcpID0+IHsKICAg\
IGNvbnN0IHBheWxvYWQgPSByYXcgPz8ge307CiAgICBpZiAocGF5bG9hZC53ZWJfcHVzaCA9PT0gODAzMCAmJiBwYXlsb2FkLm5v\
dGlmaWNhdGlvbiAmJiB0eXBlb2YgcGF5bG9hZC5ub3RpZmljYXRpb24gPT09ICJvYmplY3QiKSB7CiAgICAgIGNvbnN0IGlubmVy\
ID0gcGF5bG9hZC5ub3RpZmljYXRpb247CiAgICAgIGNvbnN0IG5hdmlnYXRlVXJsID0gaW5uZXIubmF2aWdhdGU7CiAgICAgIGNv\
bnN0IGRhdGEgPSB7IC4uLmlubmVyLmRhdGEgPz8ge30gfTsKICAgICAgaWYgKHR5cGVvZiBuYXZpZ2F0ZVVybCA9PT0gInN0cmlu\
ZyIgJiYgIWRhdGEudXJsKSBkYXRhLnVybCA9IG5hdmlnYXRlVXJsOwogICAgICByZXR1cm4geyAuLi5pbm5lciwgZGF0YSB9Owog\
ICAgfQogICAgcmV0dXJuIHBheWxvYWQ7CiAgfTsKICB2YXIgZ2V0QmFkZ2VDb3VudCA9IChwYXlsb2FkKSA9PiB7CiAgICBjb25z\
dCBiYWRnZVZhbHVlID0gcGF5bG9hZC5kYXRhPy5iYWRnZV9jb3VudDsKICAgIGlmICh0eXBlb2YgYmFkZ2VWYWx1ZSA9PT0gIm51\
bWJlciIgJiYgTnVtYmVyLmlzRmluaXRlKGJhZGdlVmFsdWUpKSB7CiAgICAgIHJldHVybiBiYWRnZVZhbHVlOwogICAgfQogICAg\
aWYgKHR5cGVvZiBiYWRnZVZhbHVlID09PSAic3RyaW5nIiAmJiBiYWRnZVZhbHVlLmxlbmd0aCA+IDApIHsKICAgICAgY29uc3Qg\
cGFyc2VkID0gTnVtYmVyKGJhZGdlVmFsdWUpOwogICAgICByZXR1cm4gTnVtYmVyLmlzRmluaXRlKHBhcnNlZCkgPyBwYXJzZWQg\
OiBudWxsOwogICAgfQogICAgcmV0dXJuIG51bGw7CiAgfTsKICB2YXIgdXBkYXRlQXBwQmFkZ2UgPSBhc3luYyAoY291bnQpID0+\
IHsKICAgIGlmICh0eXBlb2YgbmF2aWdhdG9yLnNldEFwcEJhZGdlICE9PSAiZnVuY3Rpb24iICYmIHR5cGVvZiBuYXZpZ2F0b3Iu\
Y2xlYXJBcHBCYWRnZSAhPT0gImZ1bmN0aW9uIikgewogICAgICByZXR1cm47CiAgICB9CiAgICB0cnkgewogICAgICBpZiAoY291\
bnQgIT09IG51bGwgJiYgY291bnQgPiAwKSB7CiAgICAgICAgaWYgKHR5cGVvZiBuYXZpZ2F0b3Iuc2V0QXBwQmFkZ2UgPT09ICJm\
dW5jdGlvbiIpIHsKICAgICAgICAgIGF3YWl0IG5hdmlnYXRvci5zZXRBcHBCYWRnZShjb3VudCk7CiAgICAgICAgICBhd2FpdCBs\
b2coImRlYnVnIiwgImJhZGdlIHNldCIsIHsgY291bnQgfSk7CiAgICAgICAgfQogICAgICB9IGVsc2UgaWYgKHR5cGVvZiBuYXZp\
Z2F0b3IuY2xlYXJBcHBCYWRnZSA9PT0gImZ1bmN0aW9uIikgewogICAgICAgIGF3YWl0IG5hdmlnYXRvci5jbGVhckFwcEJhZGdl\
KCk7CiAgICAgICAgYXdhaXQgbG9nKCJkZWJ1ZyIsICJiYWRnZSBjbGVhcmVkIik7CiAgICAgIH0KICAgIH0gY2F0Y2ggKGVycm9y\
KSB7CiAgICAgIGF3YWl0IGxvZygiZXJyb3IiLCAiRmFpbGVkIHRvIHVwZGF0ZSBhcHAgYmFkZ2UiLCB7IGVycm9yOiBkZXNjcmli\
ZUVycm9yKGVycm9yKSB9KTsKICAgIH0KICB9OwogIHZhciByZXNvbHZlVGFyZ2V0VXJsID0gKHVybCkgPT4gewogICAgaWYgKCF1\
cmwpIHJldHVybiBudWxsOwogICAgdHJ5IHsKICAgICAgY29uc3QgcmVzb2x2ZWQgPSBuZXcgVVJMKHVybCwgc2VsZi5sb2NhdGlv\
bi5vcmlnaW4pOwogICAgICBpZiAocmVzb2x2ZWQucHJvdG9jb2wgIT09ICJodHRwOiIgJiYgcmVzb2x2ZWQucHJvdG9jb2wgIT09\
ICJodHRwczoiKSByZXR1cm4gbnVsbDsKICAgICAgcmV0dXJuIHJlc29sdmVkLnRvU3RyaW5nKCk7CiAgICB9IGNhdGNoIHsKICAg\
ICAgcmV0dXJuIG51bGw7CiAgICB9CiAgfTsKICB2YXIgcG9zdE1lc3NhZ2VUb0NsaWVudHMgPSBhc3luYyAobWVzc2FnZSkgPT4g\
ewogICAgdHJ5IHsKICAgICAgY29uc3QgY2xpZW50TGlzdCA9IGF3YWl0IHNlbGYuY2xpZW50cy5tYXRjaEFsbCh7CiAgICAgICAg\
dHlwZTogIndpbmRvdyIsCiAgICAgICAgaW5jbHVkZVVuY29udHJvbGxlZDogdHJ1ZQogICAgICB9KTsKICAgICAgZm9yIChjb25z\
dCBjbGllbnQgb2YgY2xpZW50TGlzdCkgewogICAgICAgIGNsaWVudC5wb3N0TWVzc2FnZShtZXNzYWdlKTsKICAgICAgfQogICAg\
ICByZXR1cm4gY2xpZW50TGlzdDsKICAgIH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgIGF3YWl0IGxvZygiZXJyb3IiLCAiVW5hYmxl\
IHRvIGJyb2FkY2FzdCB0byBjbGllbnRzIiwgeyBlcnJvcjogZGVzY3JpYmVFcnJvcihlcnJvcikgfSk7CiAgICAgIHJldHVybiBb\
XTsKICAgIH0KICB9OwogIHZhciBmb2N1c09yT3BlbkNsaWVudCA9IGFzeW5jICh0YXJnZXRVcmwsIHRhcmdldFVzZXJJZCkgPT4g\
ewogICAgY29uc3QgbWVzc2FnZSA9IHsKICAgICAgdHlwZTogIk5PVElGSUNBVElPTl9DTElDS19OQVZJR0FURSIsCiAgICAgIHVy\
bDogdGFyZ2V0VXJsCiAgICB9OwogICAgaWYgKHRhcmdldFVzZXJJZCkgewogICAgICBtZXNzYWdlLnRhcmdldFVzZXJJZCA9IHRh\
cmdldFVzZXJJZDsKICAgIH0KICAgIGNvbnN0IGNsaWVudExpc3QgPSBhd2FpdCBwb3N0TWVzc2FnZVRvQ2xpZW50cyhtZXNzYWdl\
KTsKICAgIGNvbnN0IGV4YWN0ID0gY2xpZW50TGlzdC5maW5kKChjKSA9PiBjLnVybCA9PT0gdGFyZ2V0VXJsKTsKICAgIGlmIChl\
eGFjdCkgewogICAgICBhd2FpdCBsb2coImRlYnVnIiwgImZvY3VzIGV4YWN0IGNsaWVudCIsIHsgdXJsOiB0YXJnZXRVcmwgfSk7\
CiAgICAgIGF3YWl0IGV4YWN0LmZvY3VzKCk7CiAgICAgIHJldHVybjsKICAgIH0KICAgIGNvbnN0IHNhbWVPcmlnaW4gPSBjbGll\
bnRMaXN0LmZpbmQoKGMpID0+IHsKICAgICAgdHJ5IHsKICAgICAgICByZXR1cm4gbmV3IFVSTChjLnVybCkub3JpZ2luID09PSBz\
ZWxmLmxvY2F0aW9uLm9yaWdpbjsKICAgICAgfSBjYXRjaCB7CiAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICB9CiAgICB9KTsK\
ICAgIGlmIChzYW1lT3JpZ2luKSB7CiAgICAgIGF3YWl0IGxvZygiZGVidWciLCAiZm9jdXMgc2FtZS1vcmlnaW4gY2xpZW50Iiwg\
eyB1cmw6IHNhbWVPcmlnaW4udXJsIH0pOwogICAgICBhd2FpdCBzYW1lT3JpZ2luLmZvY3VzKCk7CiAgICAgIHJldHVybjsKICAg\
IH0KICAgIGlmIChzZWxmLmNsaWVudHMub3BlbldpbmRvdykgewogICAgICBhd2FpdCBsb2coImRlYnVnIiwgIm9wZW5XaW5kb3ci\
LCB7IHVybDogdGFyZ2V0VXJsIH0pOwogICAgICBhd2FpdCBzZWxmLmNsaWVudHMub3BlbldpbmRvdyh0YXJnZXRVcmwpOwogICAg\
fQogIH07CiAgdmFyIGRlc2NyaWJlRXJyb3IgPSAoZXJyb3IpID0+IHsKICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yKSB7\
CiAgICAgIHJldHVybiB7IG5hbWU6IGVycm9yLm5hbWUsIG1lc3NhZ2U6IGVycm9yLm1lc3NhZ2UsIHN0YWNrOiBlcnJvci5zdGFj\
ayB9OwogICAgfQogICAgcmV0dXJuIHsgdmFsdWU6IFN0cmluZyhlcnJvcikgfTsKICB9OwogIHNlbGYuYWRkRXZlbnRMaXN0ZW5l\
cigicHVzaCIsIChldmVudCkgPT4gewogICAgZXZlbnQud2FpdFVudGlsKAogICAgICAoYXN5bmMgKCkgPT4gewogICAgICAgIGF3\
YWl0IGVuc3VyZUxvZ2dpbmdFbmFibGVkTG9hZGVkOwogICAgICAgIGxldCByYXc7CiAgICAgICAgbGV0IHJhd1RleHQgPSBudWxs\
OwogICAgICAgIHRyeSB7CiAgICAgICAgICByYXdUZXh0ID0gZXZlbnQuZGF0YT8udGV4dCgpID8/IG51bGw7CiAgICAgICAgfSBj\
YXRjaCB7CiAgICAgICAgICByYXdUZXh0ID0gbnVsbDsKICAgICAgICB9CiAgICAgICAgdHJ5IHsKICAgICAgICAgIHJhdyA9IGV2\
ZW50LmRhdGE/Lmpzb24/LigpOwogICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgICBhd2FpdCBsb2coImVycm9yIiwg\
InB1c2g6IEpTT04gcGFyc2UgZmFpbGVkIiwgewogICAgICAgICAgICBlcnJvcjogZGVzY3JpYmVFcnJvcihlcnJvciksCiAgICAg\
ICAgICAgIHJhd1ByZXZpZXc6IHJhd1RleHQ/LnNsaWNlKDAsIDUxMikgPz8gbnVsbAogICAgICAgICAgfSk7CiAgICAgICAgfQog\
ICAgICAgIGNvbnN0IHBheWxvYWQgPSBub3JtYWxpemVQdXNoUGF5bG9hZChyYXcgPz8geyB0aXRsZTogIkZsdXhlciIgfSk7CiAg\
ICAgICAgY29uc3QgdGl0bGUgPSBwYXlsb2FkLnRpdGxlID8/ICJGbHV4ZXIiOwogICAgICAgIGNvbnN0IGNoYW5uZWxJZCA9IHBh\
eWxvYWQuZGF0YT8uY2hhbm5lbF9pZDsKICAgICAgICBjb25zdCB0YWcgPSBwYXlsb2FkLnRhZyA/PyAodHlwZW9mIGNoYW5uZWxJ\
ZCA9PT0gInN0cmluZyIgPyBgY2hhbm5lbDoke2NoYW5uZWxJZH1gIDogdm9pZCAwKTsKICAgICAgICBjb25zdCBib2R5ID0gcGF5\
bG9hZC5ib2R5ID8/ICIiOwogICAgICAgIGNvbnN0IG9wdGlvbnMgPSB7CiAgICAgICAgICBib2R5LAogICAgICAgICAgaWNvbjog\
cGF5bG9hZC5pY29uID8/IHZvaWQgMCwKICAgICAgICAgIGJhZGdlOiBwYXlsb2FkLmJhZGdlID8/IHZvaWQgMCwKICAgICAgICAg\
IGRhdGE6IHBheWxvYWQuZGF0YSA/PyB2b2lkIDAsCiAgICAgICAgICB0YWcsCiAgICAgICAgICByZW5vdGlmeTogdGFnICE9PSB2\
b2lkIDAKICAgICAgICB9OwogICAgICAgIGNvbnN0IGJhZGdlQ291bnQgPSBnZXRCYWRnZUNvdW50KHBheWxvYWQpOwogICAgICAg\
IGF3YWl0IGxvZygiaW5mbyIsICJwdXNoIHJlY2VpdmVkIiwgewogICAgICAgICAgdGl0bGUsCiAgICAgICAgICBoYXNCb2R5OiBC\
b29sZWFuKHBheWxvYWQuYm9keSksCiAgICAgICAgICB0YWcsCiAgICAgICAgICBoYXNEYXRhOiBwYXlsb2FkLmRhdGEgIT09IHZv\
aWQgMCwKICAgICAgICAgIGJhZGdlQ291bnQsCiAgICAgICAgICB3ZWJQdXNoOiBwYXlsb2FkLndlYl9wdXNoID8/IG51bGwKICAg\
ICAgICB9KTsKICAgICAgICB0cnkgewogICAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoW3NlbGYucmVnaXN0cmF0aW9uLnNob3dO\
b3RpZmljYXRpb24odGl0bGUsIG9wdGlvbnMpLCB1cGRhdGVBcHBCYWRnZShiYWRnZUNvdW50KV0pOwogICAgICAgIH0gY2F0Y2gg\
KGVycm9yKSB7CiAgICAgICAgICBhd2FpdCBsb2coImVycm9yIiwgInB1c2g6IHNob3dOb3RpZmljYXRpb24gZmFpbGVkLCBzaG93\
aW5nIGZhbGxiYWNrIiwgeyBlcnJvcjogZGVzY3JpYmVFcnJvcihlcnJvcikgfSk7CiAgICAgICAgICB0cnkgewogICAgICAgICAg\
ICBhd2FpdCBzZWxmLnJlZ2lzdHJhdGlvbi5zaG93Tm90aWZpY2F0aW9uKCJGbHV4ZXIiLCB7IGJvZHk6ICIiIH0pOwogICAgICAg\
ICAgfSBjYXRjaCAoZmFsbGJhY2tFcnJvcikgewogICAgICAgICAgICBhd2FpdCBsb2coImVycm9yIiwgInB1c2g6IGZhbGxiYWNr\
IHNob3dOb3RpZmljYXRpb24gYWxzbyBmYWlsZWQiLCB7CiAgICAgICAgICAgICAgZXJyb3I6IGRlc2NyaWJlRXJyb3IoZmFsbGJh\
Y2tFcnJvcikKICAgICAgICAgICAgfSk7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9KSgpCiAgICApOwogIH0pOwogIHNl\
bGYuYWRkRXZlbnRMaXN0ZW5lcigibm90aWZpY2F0aW9uY2xpY2siLCAoZXZlbnQpID0+IHsKICAgIGV2ZW50Lm5vdGlmaWNhdGlv\
bi5jbG9zZSgpOwogICAgY29uc3QgdGFyZ2V0VXJsID0gcmVzb2x2ZVRhcmdldFVybChldmVudC5ub3RpZmljYXRpb24uZGF0YT8u\
dXJsKTsKICAgIGNvbnN0IHRhcmdldFVzZXJJZCA9IGV2ZW50Lm5vdGlmaWNhdGlvbi5kYXRhPy50YXJnZXRfdXNlcl9pZDsKICAg\
IGV2ZW50LndhaXRVbnRpbCgKICAgICAgKGFzeW5jICgpID0+IHsKICAgICAgICBhd2FpdCBlbnN1cmVMb2dnaW5nRW5hYmxlZExv\
YWRlZDsKICAgICAgICBhd2FpdCBsb2coImluZm8iLCAibm90aWZpY2F0aW9uY2xpY2siLCB7IHRhcmdldFVybCwgdGFyZ2V0VXNl\
cklkLCB0YWc6IGV2ZW50Lm5vdGlmaWNhdGlvbi50YWcgfSk7CiAgICAgICAgaWYgKCF0YXJnZXRVcmwpIHJldHVybjsKICAgICAg\
ICB0cnkgewogICAgICAgICAgYXdhaXQgZm9jdXNPck9wZW5DbGllbnQodGFyZ2V0VXJsLCB0YXJnZXRVc2VySWQpOwogICAgICAg\
IH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgICBhd2FpdCBsb2coImVycm9yIiwgIm5vdGlmaWNhdGlvbmNsaWNrOiBmb2N1c09y\
T3BlbiBmYWlsZWQiLCB7IGVycm9yOiBkZXNjcmliZUVycm9yKGVycm9yKSB9KTsKICAgICAgICB9CiAgICAgIH0pKCkKICAgICk7\
CiAgfSk7CiAgc2VsZi5hZGRFdmVudExpc3RlbmVyKCJub3RpZmljYXRpb25jbG9zZSIsIChldmVudCkgPT4gewogICAgZXZlbnQu\
d2FpdFVudGlsKGxvZygiZGVidWciLCAibm90aWZpY2F0aW9uY2xvc2UiLCB7IHRhZzogZXZlbnQubm90aWZpY2F0aW9uLnRhZyB9\
KSk7CiAgfSk7CiAgdmFyIGFycmF5QnVmZmVyVG9CYXNlNjRVcmwgPSAoYnVmZmVyKSA9PiB7CiAgICBpZiAoIWJ1ZmZlcikgcmV0\
dXJuIG51bGw7CiAgICBjb25zdCBieXRlcyA9IG5ldyBVaW50OEFycmF5KGJ1ZmZlcik7CiAgICBsZXQgYmluYXJ5ID0gIiI7CiAg\
ICBmb3IgKGxldCBpID0gMDsgaSA8IGJ5dGVzLmxlbmd0aDsgaSArPSAxKSBiaW5hcnkgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShi\
eXRlc1tpXSk7CiAgICByZXR1cm4gYnRvYShiaW5hcnkpLnJlcGxhY2UoL1wrL2csICItIikucmVwbGFjZSgvXC8vZywgIl8iKS5y\
ZXBsYWNlKC89KyQvLCAiIik7CiAgfTsKICB2YXIgdXJsQmFzZTY0VG9VaW50OEFycmF5ID0gKGJhc2U2NCkgPT4gewogICAgY29u\
c3QgcGFkZGluZyA9ICI9Ii5yZXBlYXQoKDQgLSBiYXNlNjQubGVuZ3RoICUgNCkgJSA0KTsKICAgIGNvbnN0IHJhdyA9IGF0b2Io\
KGJhc2U2NCArIHBhZGRpbmcpLnJlcGxhY2UoLy0vZywgIisiKS5yZXBsYWNlKC9fL2csICIvIikpOwogICAgY29uc3Qgb3V0ID0g\
bmV3IFVpbnQ4QXJyYXkocmF3Lmxlbmd0aCk7CiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJhdy5sZW5ndGg7IGkgKz0gMSkgb3V0\
W2ldID0gcmF3LmNoYXJDb2RlQXQoaSk7CiAgICByZXR1cm4gb3V0OwogIH07CiAgdmFyIGZldGNoSW5zdGFuY2VDb25maWcgPSBh\
c3luYyAoKSA9PiB7CiAgICB0cnkgewogICAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaCgiLy53ZWxsLWtub3duL2ZsdXhlciIs\
IHsgY3JlZGVudGlhbHM6ICJpbmNsdWRlIiB9KTsKICAgICAgaWYgKCFyZXMub2spIHJldHVybiBudWxsOwogICAgICBjb25zdCBk\
YXRhID0gYXdhaXQgcmVzLmpzb24oKTsKICAgICAgY29uc3QgYXBpQ2xpZW50ID0gZGF0YS5lbmRwb2ludHM/LmFwaV9jbGllbnQg\
Pz8gZGF0YS5lbmRwb2ludHM/LmFwaTsKICAgICAgaWYgKCFhcGlDbGllbnQpIHJldHVybiBudWxsOwogICAgICByZXR1cm4geyBh\
cGlDbGllbnQsIHZhcGlkS2V5OiBkYXRhLnB1c2g/LnB1YmxpY192YXBpZF9rZXkgPz8gbnVsbCB9OwogICAgfSBjYXRjaCAoZXJy\
b3IpIHsKICAgICAgYXdhaXQgbG9nKCJlcnJvciIsICJGYWlsZWQgdG8gZmV0Y2ggaW5zdGFuY2UgY29uZmlnIGluIFNXIiwgeyBl\
cnJvcjogZGVzY3JpYmVFcnJvcihlcnJvcikgfSk7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogIH07CiAgdmFyIFNXX0FQSV9W\
RVJTSU9OID0gMTsKICB2YXIgcm90YXRlU3Vic2NyaXB0aW9uT25TZXJ2ZXIgPSBhc3luYyAob2xkRW5kcG9pbnQsIG5ld1N1Yiwg\
YXBpQ2xpZW50KSA9PiB7CiAgICBjb25zdCBwMjU2ZGggPSBhcnJheUJ1ZmZlclRvQmFzZTY0VXJsKG5ld1N1Yi5nZXRLZXkoInAy\
NTZkaCIpKTsKICAgIGNvbnN0IGF1dGggPSBhcnJheUJ1ZmZlclRvQmFzZTY0VXJsKG5ld1N1Yi5nZXRLZXkoImF1dGgiKSk7CiAg\
ICBpZiAoIXAyNTZkaCB8fCAhYXV0aCkgewogICAgICBhd2FpdCBsb2coImVycm9yIiwgInJvdGF0ZTogbmV3IHN1YnNjcmlwdGlv\
biBtaXNzaW5nIGtleXMiKTsKICAgICAgcmV0dXJuOwogICAgfQogICAgY29uc3QgdXJsID0gYCR7YXBpQ2xpZW50fS92JHtTV19B\
UElfVkVSU0lPTn0vdXNlcnMvQG1lL3B1c2gvcm90YXRlYDsKICAgIHRyeSB7CiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNo\
KHVybCwgewogICAgICAgIG1ldGhvZDogIlBPU1QiLAogICAgICAgIGNyZWRlbnRpYWxzOiAiaW5jbHVkZSIsCiAgICAgICAgaGVh\
ZGVyczogeyAiQ29udGVudC1UeXBlIjogImFwcGxpY2F0aW9uL2pzb24iIH0sCiAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnko\
ewogICAgICAgICAgb2xkX2VuZHBvaW50OiBvbGRFbmRwb2ludCwKICAgICAgICAgIGVuZHBvaW50OiBuZXdTdWIuZW5kcG9pbnQs\
CiAgICAgICAgICBrZXlzOiB7IHAyNTZkaCwgYXV0aCB9LAogICAgICAgICAgdXNlcl9hZ2VudDogc2VsZi5uYXZpZ2F0b3IudXNl\
ckFnZW50CiAgICAgICAgfSkKICAgICAgfSk7CiAgICAgIGF3YWl0IGxvZyhyZXMub2sgPyAiaW5mbyIgOiAiZXJyb3IiLCAicm90\
YXRlOiBzZXJ2ZXIgcmVzcG9uc2UiLCB7IHN0YXR1czogcmVzLnN0YXR1cywgb2s6IHJlcy5vayB9KTsKICAgIH0gY2F0Y2ggKGVy\
cm9yKSB7CiAgICAgIGF3YWl0IGxvZygiZXJyb3IiLCAicm90YXRlOiBmZXRjaCBmYWlsZWQiLCB7IGVycm9yOiBkZXNjcmliZUVy\
cm9yKGVycm9yKSB9KTsKICAgIH0KICB9OwogIHNlbGYuYWRkRXZlbnRMaXN0ZW5lcigicHVzaHN1YnNjcmlwdGlvbmNoYW5nZSIs\
IChldmVudCkgPT4gewogICAgZXZlbnQud2FpdFVudGlsKAogICAgICAoYXN5bmMgKCkgPT4gewogICAgICAgIGF3YWl0IGVuc3Vy\
ZUxvZ2dpbmdFbmFibGVkTG9hZGVkOwogICAgICAgIGF3YWl0IGxvZygiaW5mbyIsICJwdXNoc3Vic2NyaXB0aW9uY2hhbmdlIiwg\
ewogICAgICAgICAgaGFzT2xkOiBCb29sZWFuKGV2ZW50Lm9sZFN1YnNjcmlwdGlvbiksCiAgICAgICAgICBoYXNOZXc6IEJvb2xl\
YW4oZXZlbnQubmV3U3Vic2NyaXB0aW9uKSwKICAgICAgICAgIG9sZEVuZHBvaW50OiBldmVudC5vbGRTdWJzY3JpcHRpb24/LmVu\
ZHBvaW50ID8/IG51bGwsCiAgICAgICAgICBuZXdFbmRwb2ludDogZXZlbnQubmV3U3Vic2NyaXB0aW9uPy5lbmRwb2ludCA/PyBu\
dWxsCiAgICAgICAgfSk7CiAgICAgICAgdHJ5IHsKICAgICAgICAgIGNvbnN0IG9sZEVuZHBvaW50ID0gZXZlbnQub2xkU3Vic2Ny\
aXB0aW9uPy5lbmRwb2ludCA/PyBudWxsOwogICAgICAgICAgbGV0IGFwcFNlcnZlcktleSA9IGV2ZW50Lm9sZFN1YnNjcmlwdGlv\
bj8ub3B0aW9ucz8uYXBwbGljYXRpb25TZXJ2ZXJLZXkgPz8gbnVsbDsKICAgICAgICAgIGNvbnN0IGNvbmZpZyA9IGF3YWl0IGZl\
dGNoSW5zdGFuY2VDb25maWcoKTsKICAgICAgICAgIGlmICghYXBwU2VydmVyS2V5ICYmIGNvbmZpZz8udmFwaWRLZXkpIHsKICAg\
ICAgICAgICAgYXBwU2VydmVyS2V5ID0gdXJsQmFzZTY0VG9VaW50OEFycmF5KGNvbmZpZy52YXBpZEtleSkuYnVmZmVyOwogICAg\
ICAgICAgfQogICAgICAgICAgaWYgKCFhcHBTZXJ2ZXJLZXkpIHsKICAgICAgICAgICAgYXdhaXQgbG9nKCJlcnJvciIsICJwdXNo\
c3Vic2NyaXB0aW9uY2hhbmdlOiBubyBWQVBJRCBrZXkgYXZhaWxhYmxlIik7CiAgICAgICAgICAgIGF3YWl0IHBvc3RNZXNzYWdl\
VG9DbGllbnRzKHsgdHlwZTogIlBVU0hfU1VCU0NSSVBUSU9OX0NIQU5HRSIgfSk7CiAgICAgICAgICAgIHJldHVybjsKICAgICAg\
ICAgIH0KICAgICAgICAgIGNvbnN0IG5ld1N1YiA9IGV2ZW50Lm5ld1N1YnNjcmlwdGlvbiA/PyBhd2FpdCBzZWxmLnJlZ2lzdHJh\
dGlvbi5wdXNoTWFuYWdlci5zdWJzY3JpYmUoewogICAgICAgICAgICB1c2VyVmlzaWJsZU9ubHk6IHRydWUsCiAgICAgICAgICAg\
IGFwcGxpY2F0aW9uU2VydmVyS2V5OiBhcHBTZXJ2ZXJLZXkKICAgICAgICAgIH0pOwogICAgICAgICAgaWYgKG9sZEVuZHBvaW50\
ICYmIGNvbmZpZykgewogICAgICAgICAgICBhd2FpdCByb3RhdGVTdWJzY3JpcHRpb25PblNlcnZlcihvbGRFbmRwb2ludCwgbmV3\
U3ViLCBjb25maWcuYXBpQ2xpZW50KTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGF3YWl0IGxvZygid2FybiIsICJw\
dXNoc3Vic2NyaXB0aW9uY2hhbmdlOiB1bmFibGUgdG8gcm90YXRlIG9uIHNlcnZlciIsIHsKICAgICAgICAgICAgICBoYXNPbGRF\
bmRwb2ludDogQm9vbGVhbihvbGRFbmRwb2ludCksCiAgICAgICAgICAgICAgaGFzQ29uZmlnOiBCb29sZWFuKGNvbmZpZykKICAg\
ICAgICAgICAgfSk7CiAgICAgICAgICB9CiAgICAgICAgICBhd2FpdCBwb3N0TWVzc2FnZVRvQ2xpZW50cyh7IHR5cGU6ICJQVVNI\
X1NVQlNDUklQVElPTl9DSEFOR0UiIH0pOwogICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgICBhd2FpdCBsb2coImVy\
cm9yIiwgInB1c2hzdWJzY3JpcHRpb25jaGFuZ2UgaGFuZGxlciBmYWlsZWQiLCB7IGVycm9yOiBkZXNjcmliZUVycm9yKGVycm9y\
KSB9KTsKICAgICAgICAgIGF3YWl0IHBvc3RNZXNzYWdlVG9DbGllbnRzKHsgdHlwZTogIlBVU0hfU1VCU0NSSVBUSU9OX0NIQU5H\
RSIgfSkuY2F0Y2goKCkgPT4gdm9pZCAwKTsKICAgICAgICB9CiAgICAgIH0pKCkKICAgICk7CiAgfSk7Cn0pKCk7Ci8vIyBzb3Vy\
Y2VNYXBwaW5nVVJMPXN3LmpzLm1hcAo=\
";
const STABLE_VERSION_JSON: &str = "\
eyJzaGEiOiIzMjUwYzczY2JmOTRlOTdlOGI5Y2E1YTBlY2IwM2MyMGJkZGUzODY3IiwiYnVpbGROdW1iZXIiOjUwOCwiYnVpbGRU\
aW1lc3RhbXAiOiIxNzgwNzgxMTY4In0=\
";
fn decode(b64: &str) -> Vec<u8> {
base64::engine::general_purpose::STANDARD
.decode(b64)
.expect("frozen snapshot base64 decode failed")
}
pub static STABLE_SNAPSHOT: LazyLock<FrozenSnapshot> = LazyLock::new(|| FrozenSnapshot {
sha: STABLE_SNAPSHOT_SHA.to_owned(),
index_html: decode(STABLE_INDEX_HTML),
sw_js: decode(STABLE_SW_JS),
version_json: decode(STABLE_VERSION_JSON),
});
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stable_snapshot_sha_matches() {
assert_eq!(STABLE_SNAPSHOT.sha, STABLE_SNAPSHOT_SHA);
}
#[test]
fn index_html_decodes_and_starts_with_doctype() {
assert_index_html(&STABLE_SNAPSHOT);
}
#[test]
fn sw_js_decodes_and_is_javascript() {
assert_sw_js(&STABLE_SNAPSHOT);
}
#[test]
fn version_json_decodes_and_is_valid_json() {
assert_version_json(&STABLE_SNAPSHOT);
}
fn assert_index_html(snapshot: &FrozenSnapshot) {
let html = std::str::from_utf8(&snapshot.index_html).unwrap();
assert!(
html.starts_with("<!doctype html>"),
"unexpected start: {}",
&html[..40]
);
}
fn assert_sw_js(snapshot: &FrozenSnapshot) {
let js = std::str::from_utf8(&snapshot.sw_js).unwrap();
assert!(
js.contains("use strict"),
"sw.js should contain 'use strict'"
);
}
fn assert_version_json(snapshot: &FrozenSnapshot) {
let json_str = std::str::from_utf8(&snapshot.version_json).unwrap();
let parsed: serde_json::Value = serde_json::from_str(json_str).unwrap();
assert!(
parsed
.get("sha")
.or_else(|| parsed.get("version"))
.is_some(),
"version.json should have a sha or version field"
);
}
}
-3
View File
@@ -4,9 +4,6 @@ pub mod bootstrap;
pub mod config;
pub mod csp;
pub mod discovery_cache;
#[cfg(feature = "time-freeze")]
pub mod frozen_snapshots;
pub mod geoip;
pub mod routes;
pub mod state;
pub mod time_freeze;
+5 -217
View File
@@ -9,9 +9,6 @@ use crate::state::{
AppProxyBudgets, AppState, MAX_RENDERED_SPA_INDEX_BYTES, MAX_SPA_INDEX_BYTES,
read_bounded_text_file,
};
use crate::time_freeze::{
load_time_freeze_config_for_request, should_serve_frozen, time_freeze_debug_header,
};
use axum::{
body::{Body, Bytes},
extract::{Request, State},
@@ -142,8 +139,6 @@ async fn serve_static_file(
}
async fn serve_spa_index(state: &AppState, headers: &HeaderMap) -> Response {
let time_freeze = load_time_freeze_config_for_request(&state.config, headers);
let debug_header = time_freeze_debug_header(&time_freeze);
let should_bust_dev_assets = state.config.index_upstream_url.is_some();
let discovery = match refresh_discovery_for_spa(state).await {
@@ -168,13 +163,9 @@ async fn serve_spa_index(state: &AppState, headers: &HeaderMap) -> Response {
let geoip = build_geoip_response(state.geoip.lookup(headers));
let script_tag = build_bootstrap_script(&state.config, &discovery, &geoip, &nonce);
let raw_html = if let Some(snapshot) = should_serve_frozen(&time_freeze) {
String::from_utf8_lossy(&snapshot.index_html).into_owned()
} else {
match load_spa_index_html(state).await {
Ok(content) => content,
Err(response) => return response,
}
let raw_html = match load_spa_index_html(state).await {
Ok(content) => content,
Err(response) => return response,
};
let dev_buster = should_bust_dev_assets.then(current_dev_asset_cache_buster);
@@ -193,7 +184,7 @@ async fn serve_spa_index(state: &AppState, headers: &HeaderMap) -> Response {
}
};
let html = html.into_boxed_str();
build_spa_response(html, csp, debug_header.as_deref(), should_bust_dev_assets)
build_spa_response(html, csp, should_bust_dev_assets)
}
#[derive(Debug)]
@@ -377,12 +368,7 @@ impl AsRef<[u8]> for SpaDocumentBody {
}
}
fn build_spa_response(
html: Box<str>,
csp: HeaderValue,
time_freeze_header: Option<&str>,
dev_no_store: bool,
) -> Response {
fn build_spa_response(html: Box<str>, csp: HeaderValue, dev_no_store: bool) -> Response {
let body = Bytes::from_owner(SpaDocumentBody { html });
let mut response = Response::new(Body::from(body));
let headers = response.headers_mut();
@@ -419,17 +405,6 @@ fn build_spa_response(
axum::http::HeaderName::from_static("critical-ch"),
HeaderValue::from_static(CRITICAL_CH_VALUE),
);
#[cfg(feature = "time-freeze")]
{
if let Some(tf) = time_freeze_header
&& let Ok(v) = HeaderValue::from_str(tf)
{
headers.insert(axum::http::HeaderName::from_static("x-time-freeze"), v);
}
}
#[cfg(not(feature = "time-freeze"))]
let _ = time_freeze_header;
response
}
@@ -667,40 +642,6 @@ mod tests {
assert!(!untouched.contains("_=9911"));
}
#[cfg(feature = "time-freeze")]
#[test]
fn the_frozen_shell_is_never_served_with_an_unfilled_nonce_or_a_missing_bootstrap() {
let frozen = String::from_utf8_lossy(&crate::frozen_snapshots::STABLE_SNAPSHOT.index_html)
.into_owned();
assert!(
frozen.contains("{{CSP_NONCE_PLACEHOLDER}}"),
"the captured shell should still carry the hole this test proves we fill"
);
let served = render_spa_document(
&frozen,
"frozennonce",
"<script>frozenboot</script>",
"https://fluxerstatic.com",
"",
None,
)
.expect("test SPA document must render within its size limit");
assert!(
!served.contains("{{CSP_NONCE_PLACEHOLDER}}"),
"a frozen-served shell shipped a literal nonce placeholder"
);
assert!(
served.contains(r#"nonce="frozennonce""#),
"a frozen-served shell lost its per-request nonce"
);
assert!(
served.contains("<script>frozenboot</script>"),
"a frozen-served shell shipped without the bootstrap script"
);
}
const SHELL_WITH_ENDPOINT_HOLES: &str = r#"<!doctype html><html><head><title>Fluxer</title><link rel="preconnect" href="{{STATIC_CDN_ENDPOINT}}">
<link rel="preconnect" href="{{STATIC_CDN_ENDPOINT}}" crossorigin>
<link rel="preconnect" href="{{MEDIA_ENDPOINT}}">
@@ -840,7 +781,6 @@ mod tests {
let discovery_upstream_url = spawn_local_origin(discovery_body, "application/json").await;
let mut config = AppProxyConfig::from_env();
config.release_channel = channel;
config.time_freeze_enabled = true;
config.index_upstream_url = index_upstream_url.map(|url| {
crate::config::HttpUrl::parse("TEST_INDEX_UPSTREAM_URL", &url)
.expect("test index upstream URL must be a valid HTTP URL")
@@ -948,158 +888,6 @@ mod tests {
);
}
#[cfg(feature = "time-freeze")]
#[tokio::test]
async fn the_frozen_document_is_served_with_its_asset_urls_untouched() {
let captured =
String::from_utf8_lossy(&crate::frozen_snapshots::STABLE_SNAPSHOT.index_html)
.into_owned();
assert!(
captured.contains(
r#"<link rel="stylesheet" href="https://fluxerstatic.com/fonts/ibm-plex.css">"#
),
"the captured shell no longer carries the asset URL this test proves we leave alone"
);
let state = spa_state_serving(ReleaseChannel::Stable, None).await;
let response = serve_spa_index(&state, &HeaderMap::new()).await;
assert_eq!(response.status(), StatusCode::OK);
let served = read_document(response).await;
assert!(
served.contains(
r#"<link rel="stylesheet" href="https://fluxerstatic.com/fonts/ibm-plex.css">"#
),
"the frozen document's stylesheet URL was rewritten on the primary hosted path"
);
assert!(
served.contains(r#"<link rel="manifest" href="/manifest.json">"#),
"the frozen document's manifest URL was rewritten on the primary hosted path"
);
assert!(
!served.contains("?_=") && !served.contains("&_="),
"the frozen document was served with a development cache-busting query"
);
}
#[cfg(feature = "time-freeze")]
#[tokio::test]
async fn the_frozen_branch_serves_a_rendered_document_and_not_the_raw_shell() {
let captured =
String::from_utf8_lossy(&crate::frozen_snapshots::STABLE_SNAPSHOT.index_html)
.into_owned();
assert!(
captured.contains("{{CSP_NONCE_PLACEHOLDER}}"),
"the captured shell no longer carries the nonce hole this test proves we fill"
);
assert!(
!captured.contains("window.__FLUXER_BOOTSTRAP__"),
"the captured shell already carries a bootstrap, so this test can no longer tell a rendered document from a raw shell"
);
let state = spa_state_serving(ReleaseChannel::Stable, None).await;
let response = serve_spa_index(&state, &HeaderMap::new()).await;
assert_eq!(response.status(), StatusCode::OK);
let granted_nonce = nonce_granted_by(&response);
let served = read_document(response).await;
assert!(
!served.contains("{{CSP_NONCE_PLACEHOLDER}}"),
"the primary hosted path shipped a literal nonce placeholder"
);
assert_eq!(
served
.matches(&format!(r#"nonce="{granted_nonce}""#))
.count(),
served.matches(r#"nonce=""#).count(),
"the frozen document carries a nonce its own policy header never granted"
);
assert!(
served.contains(&format!(
r#"<script nonce="{granted_nonce}">window.__FLUXER_BOOTSTRAP__"#
)),
"the primary hosted path shipped without a bootstrap script the browser will run"
);
let second = serve_spa_index(&state, &HeaderMap::new()).await;
assert_ne!(
nonce_granted_by(&second),
granted_nonce,
"two frozen requests were granted the same nonce"
);
}
#[cfg(feature = "time-freeze")]
#[tokio::test]
async fn every_branch_announces_which_snapshot_decision_it_took() {
let frozen_state = spa_state_serving(ReleaseChannel::Stable, None).await;
let frozen = serve_spa_index(&frozen_state, &HeaderMap::new()).await;
assert_eq!(
frozen
.headers()
.get("x-time-freeze")
.expect("the frozen response announced no snapshot decision")
.to_str()
.unwrap(),
format!(
"frozen; sha={}",
crate::frozen_snapshots::STABLE_SNAPSHOT.sha
),
"the frozen response named the wrong snapshot"
);
let live_state =
spa_state_serving(ReleaseChannel::Canary, Some(SHELL_WITH_ENDPOINT_HOLES)).await;
let live = serve_spa_index(&live_state, &HeaderMap::new()).await;
assert_eq!(
live.headers()
.get("x-time-freeze")
.expect("the live response announced no snapshot decision")
.to_str()
.unwrap(),
"no-snapshot",
"a channel with no snapshot claimed one anyway"
);
}
#[cfg(feature = "time-freeze")]
#[tokio::test]
async fn the_frozen_shell_is_never_served_with_the_asset_lifetime() {
let state = spa_state_serving(ReleaseChannel::Stable, None).await;
let response = serve_spa_index(&state, &HeaderMap::new()).await;
assert_eq!(
response
.headers()
.get("x-time-freeze")
.expect("the response announced no snapshot decision")
.to_str()
.unwrap(),
format!(
"frozen; sha={}",
crate::frozen_snapshots::STABLE_SNAPSHOT.sha
),
"this test never reached the frozen branch, so it proves nothing about it"
);
let cache_control = response
.headers()
.get(header::CACHE_CONTROL)
.expect("the frozen shell was served without a cache policy at all")
.to_str()
.unwrap();
assert_eq!(
cache_control, "no-cache",
"the frozen document naming the hashed bundle must be revalidated on every load"
);
assert_ne!(
cache_control, LONG_LIVED_ASSET_CACHE_CONTROL,
"a frozen shell cached for a year pins every returning visitor to the deployed-over bundle"
);
}
#[tokio::test]
async fn a_crawl_control_document_is_never_served_with_the_asset_lifetime() {
let root = std::env::temp_dir().join(format!(
+4 -74
View File
@@ -3,54 +3,14 @@
use crate::config::HttpEndpoint;
use crate::discovery_cache::discovery_endpoint;
use crate::state::{AppState, MAX_STATIC_TEXT_FILE_BYTES, read_bounded_file};
use crate::time_freeze::{
TimeFreezeConfig, describe_decision, load_time_freeze_config_for_request,
time_freeze_debug_header,
};
use axum::{
extract::State,
http::{HeaderMap, HeaderValue, StatusCode, header},
http::{HeaderValue, StatusCode, header},
response::{IntoResponse, Response},
};
use std::path::Path;
fn serve_frozen_file(
config: &TimeFreezeConfig,
pick: impl FnOnce(&crate::time_freeze::FrozenSnapshot) -> Option<(&[u8], &str)>,
) -> (Option<Response>, Option<String>) {
let debug = describe_decision(config);
let debug_header = time_freeze_debug_header(config);
if debug.decision != crate::time_freeze::TimeFreezeDecision::Frozen {
return (None, debug_header);
}
if let Some(snapshot) = config.snapshot
&& let Some((bytes, content_type)) = pick(snapshot)
{
let mut response = bytes.to_vec().into_response();
if let Ok(ct) = HeaderValue::from_str(content_type) {
response.headers_mut().insert(header::CONTENT_TYPE, ct);
}
response
.headers_mut()
.insert(header::CACHE_CONTROL, HeaderValue::from_static("no-cache"));
set_time_freeze_header(&mut response, debug_header.as_deref());
return (Some(response), None);
}
(None, debug_header)
}
pub async fn version_json(State(state): State<AppState>, headers: HeaderMap) -> Response {
let tf = load_time_freeze_config_for_request(&state.config, &headers);
let (frozen, debug_header) = serve_frozen_file(&tf, |snap| {
Some((snap.version_json.as_slice(), "application/json"))
});
if let Some(resp) = frozen {
return resp;
}
pub async fn version_json(State(state): State<AppState>) -> Response {
let mut result = serve_static_text_file(&state, "version.json", "application/json").await;
if result.status() == StatusCode::NOT_FOUND && !state.config.build_version.is_empty() {
@@ -61,7 +21,6 @@ pub async fn version_json(State(state): State<AppState>, headers: HeaderMap) ->
.insert(header::CACHE_CONTROL, HeaderValue::from_static("no-cache"));
}
set_time_freeze_header(&mut result, debug_header.as_deref());
result
}
@@ -87,43 +46,14 @@ pub async fn browserconfig_xml(State(state): State<AppState>) -> Response {
.await
}
pub async fn service_worker(State(state): State<AppState>, headers: HeaderMap) -> Response {
let tf = load_time_freeze_config_for_request(&state.config, &headers);
let (frozen, debug_header) = serve_frozen_file(&tf, |snap| {
Some((
snap.sw_js.as_slice(),
"application/javascript; charset=utf-8",
))
});
if let Some(resp) = frozen {
return resp;
}
let mut result =
serve_static_text_file(&state, "sw.js", "application/javascript; charset=utf-8").await;
set_time_freeze_header(&mut result, debug_header.as_deref());
result
pub async fn service_worker(State(state): State<AppState>) -> Response {
serve_static_text_file(&state, "sw.js", "application/javascript; charset=utf-8").await
}
pub async fn service_worker_map(State(state): State<AppState>) -> Response {
serve_static_text_file(&state, "sw.js.map", "application/json").await
}
fn set_time_freeze_header(response: &mut Response, value: Option<&str>) {
#[cfg(feature = "time-freeze")]
{
if let Some(v) = value
&& let Ok(hv) = HeaderValue::from_str(v)
{
response
.headers_mut()
.insert(axum::http::HeaderName::from_static("x-time-freeze"), hv);
}
}
#[cfg(not(feature = "time-freeze"))]
let _ = (response, value);
}
async fn runtime_static_cdn_endpoint(state: &AppState) -> Option<HttpEndpoint> {
if let Some(discovery) = state.discovery_cache.get().await
&& let Some(endpoint) = discovery_endpoint(&discovery, "static_cdn")
-498
View File
@@ -1,498 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use crate::config::{AppProxyConfig, ReleaseChannel};
use axum::http::{HeaderMap, header};
use std::net::{IpAddr, Ipv4Addr};
const TIME_FREEZE_EXEMPT_CLIENT_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(188, 149, 230, 148));
const CANARY_USER_AGENT_PRODUCT: &str = "FluxerCanary/";
const CANARY_LIVE_WEB_CLIENT_MIN_VERSION: CalVer = CalVer {
year: 2026,
month_day: 614,
micro: 0,
};
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct CalVer {
year: u16,
month_day: u16,
micro: u32,
}
#[derive(Clone, Debug)]
pub struct FrozenSnapshot {
pub sha: String,
pub index_html: Vec<u8>,
pub sw_js: Vec<u8>,
pub version_json: Vec<u8>,
}
#[derive(Clone, Debug)]
pub struct TimeFreezeConfig {
pub enabled: bool,
pub release_channel: ReleaseChannel,
pub snapshot: Option<&'static FrozenSnapshot>,
pub client_exempt: bool,
}
#[derive(Clone, Debug)]
pub struct TimeFreezeDebug {
pub decision: TimeFreezeDecision,
pub snapshot_sha: Option<String>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TimeFreezeDecision {
Frozen,
Live,
NoSnapshot,
}
pub fn load_time_freeze_config_for_request(
app_config: &AppProxyConfig,
headers: &HeaderMap,
) -> TimeFreezeConfig {
let client_exempt = is_time_freeze_exempt_client(
headers,
app_config.trust_client_ip_header,
&app_config.client_ip_header_name,
);
load_time_freeze_config_for_index_source(
app_config.release_channel,
client_exempt,
app_config.index_upstream_url.is_some(),
app_config.time_freeze_enabled,
)
}
pub fn load_time_freeze_config(
release_channel: ReleaseChannel,
client_exempt: bool,
) -> TimeFreezeConfig {
load_time_freeze_config_for_index_source(release_channel, client_exempt, false, true)
}
pub fn load_time_freeze_config_for_index_source(
release_channel: ReleaseChannel,
client_exempt: bool,
has_index_upstream: bool,
enabled: bool,
) -> TimeFreezeConfig {
if !enabled || has_index_upstream {
return TimeFreezeConfig {
enabled,
release_channel,
snapshot: None,
client_exempt,
};
}
let snapshot: Option<&'static FrozenSnapshot> = load_frozen_snapshot(release_channel);
TimeFreezeConfig {
enabled,
release_channel,
snapshot,
client_exempt,
}
}
#[cfg(feature = "time-freeze")]
fn load_frozen_snapshot(release_channel: ReleaseChannel) -> Option<&'static FrozenSnapshot> {
match release_channel {
ReleaseChannel::Stable => Some(&crate::frozen_snapshots::STABLE_SNAPSHOT),
ReleaseChannel::Canary => None,
}
}
#[cfg(not(feature = "time-freeze"))]
fn load_frozen_snapshot(_release_channel: ReleaseChannel) -> Option<&'static FrozenSnapshot> {
None
}
pub fn should_serve_frozen(config: &TimeFreezeConfig) -> Option<&'static FrozenSnapshot> {
if !config.enabled {
return None;
}
if config.client_exempt {
return None;
}
config.snapshot
}
pub fn describe_decision(config: &TimeFreezeConfig) -> TimeFreezeDebug {
let decision = match &config.snapshot {
None => TimeFreezeDecision::NoSnapshot,
Some(_) if should_serve_frozen(config).is_some() => TimeFreezeDecision::Frozen,
Some(_) => TimeFreezeDecision::Live,
};
TimeFreezeDebug {
decision,
snapshot_sha: config.snapshot.as_ref().map(|s| s.sha.clone()),
}
}
#[cfg(feature = "time-freeze")]
pub fn time_freeze_debug_header(config: &TimeFreezeConfig) -> Option<String> {
if !config.enabled {
return None;
}
Some(format_debug_header(&describe_decision(config)))
}
#[cfg(not(feature = "time-freeze"))]
pub fn time_freeze_debug_header(_config: &TimeFreezeConfig) -> Option<String> {
None
}
pub fn is_time_freeze_exempt_client(
headers: &HeaderMap,
trust_client_ip_header: bool,
header_name: &str,
) -> bool {
extract_client_ip(headers, trust_client_ip_header, header_name)
.is_some_and(|ip| ip == TIME_FREEZE_EXEMPT_CLIENT_IP)
|| is_time_freeze_exempt_user_agent(headers)
}
fn is_time_freeze_exempt_user_agent(headers: &HeaderMap) -> bool {
let Some(user_agent) = headers
.get(header::USER_AGENT)
.and_then(|value| value.to_str().ok())
else {
return false;
};
user_agent
.match_indices(CANARY_USER_AGENT_PRODUCT)
.any(|(index, _)| {
let version_start = index + CANARY_USER_AGENT_PRODUCT.len();
let version = user_agent[version_start..]
.chars()
.take_while(|ch| ch.is_ascii_digit() || *ch == '.')
.collect::<String>();
parse_calver(&version)
.is_some_and(|version| version >= CANARY_LIVE_WEB_CLIENT_MIN_VERSION)
})
}
fn parse_calver(value: &str) -> Option<CalVer> {
let mut parts = value.split('.');
let year = parts.next()?;
let month_day = parts.next()?;
let micro = parts.next()?;
if parts.next().is_some()
|| year.len() != 4
|| !valid_nonzero_digits(year, 4)
|| !(month_day.len() == 3 || month_day.len() == 4)
|| !valid_nonzero_digits(month_day, month_day.len())
|| !valid_micro(micro)
{
return None;
}
let year = year.parse().ok()?;
let month_day_value: u16 = month_day.parse().ok()?;
let month = month_day_value / 100;
let day = month_day_value % 100;
if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
return None;
}
let micro = micro.parse().ok()?;
let hour = micro / 10_000;
let minute = (micro / 100) % 100;
let second = micro % 100;
if hour > 23 || minute > 59 || second > 59 {
return None;
}
Some(CalVer {
year,
month_day: month_day_value,
micro,
})
}
fn valid_nonzero_digits(value: &str, max_len: usize) -> bool {
!value.is_empty()
&& value.len() <= max_len
&& !value.starts_with('0')
&& value.bytes().all(|byte| byte.is_ascii_digit())
}
fn valid_micro(value: &str) -> bool {
value == "0" || valid_nonzero_digits(value, 6)
}
fn extract_client_ip(
headers: &HeaderMap,
trust_client_ip_header: bool,
header_name: &str,
) -> Option<IpAddr> {
if !trust_client_ip_header {
return None;
}
let header_value = headers.get(header_name)?.to_str().ok()?;
let first_hop = header_value.split(',').next()?.trim();
parse_ip(first_hop)
}
fn parse_ip(value: &str) -> Option<IpAddr> {
if value.is_empty() {
return None;
}
if let Ok(ip) = value.parse() {
return Some(ip);
}
if let Some(stripped) = value
.strip_prefix('[')
.and_then(|value| value.split_once(']').map(|(host, _)| host))
{
return stripped.parse().ok();
}
if value.matches(':').count() == 1
&& let Some((host, _port)) = value.rsplit_once(':')
{
return host.parse().ok();
}
None
}
pub fn format_debug_header(debug: &TimeFreezeDebug) -> String {
let decision_str = match debug.decision {
TimeFreezeDecision::Frozen => "frozen",
TimeFreezeDecision::Live => "live",
TimeFreezeDecision::NoSnapshot => "no-snapshot",
};
match &debug.snapshot_sha {
Some(sha) => format!("{decision_str}; sha={sha}"),
None => decision_str.to_owned(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use axum::http::{HeaderMap, HeaderValue};
use std::sync::LazyLock;
#[cfg(feature = "time-freeze")]
#[test]
fn stable_channel_has_frozen_snapshot() {
let config = load_time_freeze_config(ReleaseChannel::Stable, false);
assert!(
config.snapshot.is_some(),
"stable channel should have a frozen snapshot"
);
assert!(should_serve_frozen(&config).is_some());
}
#[cfg(feature = "time-freeze")]
#[test]
fn canary_channel_has_no_frozen_snapshot() {
let config = load_time_freeze_config(ReleaseChannel::Canary, false);
assert_eq!(config.release_channel, ReleaseChannel::Canary);
assert!(config.snapshot.is_none());
assert!(should_serve_frozen(&config).is_none());
let debug = describe_decision(&config);
assert_eq!(debug.decision, TimeFreezeDecision::NoSnapshot);
assert!(debug.snapshot_sha.is_none());
}
#[test]
fn index_upstream_disables_frozen_snapshot() {
let config =
load_time_freeze_config_for_index_source(ReleaseChannel::Canary, false, true, true);
assert!(config.enabled);
assert_eq!(config.release_channel, ReleaseChannel::Canary);
assert!(config.snapshot.is_none());
assert!(should_serve_frozen(&config).is_none());
let debug = describe_decision(&config);
assert_eq!(debug.decision, TimeFreezeDecision::NoSnapshot);
assert!(debug.snapshot_sha.is_none());
}
#[test]
fn disabled_time_freeze_has_no_snapshot_or_debug_header() {
let config =
load_time_freeze_config_for_index_source(ReleaseChannel::Stable, false, false, false);
assert!(!config.enabled);
assert!(config.snapshot.is_none());
assert!(should_serve_frozen(&config).is_none());
assert!(time_freeze_debug_header(&config).is_none());
}
#[cfg(not(feature = "time-freeze"))]
#[test]
fn no_feature_build_has_no_frozen_snapshot() {
let config = load_time_freeze_config(ReleaseChannel::Stable, false);
assert!(config.enabled);
assert!(config.snapshot.is_none());
assert!(should_serve_frozen(&config).is_none());
assert!(time_freeze_debug_header(&config).is_none());
}
#[cfg(feature = "time-freeze")]
#[test]
fn exempt_client_gets_live_content_even_with_snapshot() {
let config = load_time_freeze_config(ReleaseChannel::Stable, true);
assert!(config.snapshot.is_some());
assert!(should_serve_frozen(&config).is_none());
let debug = describe_decision(&config);
assert_eq!(debug.decision, TimeFreezeDecision::Live);
}
#[test]
fn describe_decision_no_snapshot() {
let config = TimeFreezeConfig {
enabled: true,
release_channel: ReleaseChannel::Stable,
snapshot: None,
client_exempt: false,
};
let debug = describe_decision(&config);
assert_eq!(debug.decision, TimeFreezeDecision::NoSnapshot);
assert!(debug.snapshot_sha.is_none());
}
static TEST_SNAPSHOT: LazyLock<FrozenSnapshot> = LazyLock::new(|| FrozenSnapshot {
sha: "deadbeef".to_owned(),
index_html: vec![],
sw_js: vec![],
version_json: vec![],
});
#[test]
fn describe_decision_frozen() {
let config = TimeFreezeConfig {
enabled: true,
release_channel: ReleaseChannel::Stable,
snapshot: Some(&TEST_SNAPSHOT),
client_exempt: false,
};
let debug = describe_decision(&config);
assert_eq!(debug.decision, TimeFreezeDecision::Frozen);
assert_eq!(debug.snapshot_sha.as_deref(), Some("deadbeef"));
}
#[test]
fn format_debug_header_no_snapshot() {
let debug = TimeFreezeDebug {
decision: TimeFreezeDecision::NoSnapshot,
snapshot_sha: None,
};
assert_eq!(format_debug_header(&debug), "no-snapshot");
}
#[test]
fn format_debug_header_frozen() {
let debug = TimeFreezeDebug {
decision: TimeFreezeDecision::Frozen,
snapshot_sha: Some("abc".to_owned()),
};
assert_eq!(format_debug_header(&debug), "frozen; sha=abc");
}
#[test]
fn format_debug_header_live() {
let debug = TimeFreezeDebug {
decision: TimeFreezeDecision::Live,
snapshot_sha: Some("def".to_owned()),
};
assert_eq!(format_debug_header(&debug), "live; sha=def");
}
#[test]
fn recognizes_exempt_x_real_ip_when_trusted() {
let mut headers = HeaderMap::new();
headers.insert("x-real-ip", HeaderValue::from_static("188.149.230.148"));
assert!(is_time_freeze_exempt_client(&headers, true, "x-real-ip"));
assert!(!is_time_freeze_exempt_client(&headers, false, "x-real-ip"));
}
#[test]
fn recognizes_exempt_first_forwarded_ip() {
let mut headers = HeaderMap::new();
headers.insert(
"x-forwarded-for",
HeaderValue::from_static("188.149.230.148, 10.0.0.4"),
);
assert!(is_time_freeze_exempt_client(
&headers,
true,
"x-forwarded-for"
));
}
#[test]
fn non_exempt_ip_stays_frozen() {
let mut headers = HeaderMap::new();
headers.insert("x-real-ip", HeaderValue::from_static("203.0.113.10"));
assert!(!is_time_freeze_exempt_client(&headers, true, "x-real-ip"));
}
#[test]
fn canary_user_agent_at_min_version_is_exempt() {
let mut headers = HeaderMap::new();
headers.insert(
header::USER_AGENT,
HeaderValue::from_static("FluxerCanary/2026.614.0"),
);
assert!(is_time_freeze_exempt_client(&headers, false, "x-real-ip"));
}
#[test]
fn canary_user_agent_newer_than_min_version_is_exempt() {
let mut headers = HeaderMap::new();
headers.insert(
header::USER_AGENT,
HeaderValue::from_static("Mozilla/5.0 FluxerCanary/2026.614.83512 Electron/99.0"),
);
assert!(is_time_freeze_exempt_client(&headers, false, "x-real-ip"));
}
#[test]
fn canary_user_agent_before_min_version_stays_frozen() {
let mut headers = HeaderMap::new();
headers.insert(
header::USER_AGENT,
HeaderValue::from_static("FluxerCanary/2026.613.235959"),
);
assert!(!is_time_freeze_exempt_client(&headers, false, "x-real-ip"));
}
#[test]
fn malformed_canary_user_agent_stays_frozen() {
let mut headers = HeaderMap::new();
headers.insert(
header::USER_AGENT,
HeaderValue::from_static("FluxerCanary/2026.614.not-a-calver"),
);
assert!(!is_time_freeze_exempt_client(&headers, false, "x-real-ip"));
}
#[test]
fn stable_user_agent_stays_frozen() {
let mut headers = HeaderMap::new();
headers.insert(
header::USER_AGENT,
HeaderValue::from_static("FluxerStable/2026.614.83512"),
);
assert!(!is_time_freeze_exempt_client(&headers, false, "x-real-ip"));
}
}