From 3256af8d922e09111592a847b0cee27dcb395852 Mon Sep 17 00:00:00 2001 From: Hampus Date: Sun, 20 Sep 2026 04:02:47 +0200 Subject: [PATCH] refactor(app-proxy): remove the stable time freeze (#2856) --- .../build-app-proxy-self-hosted.yaml | 2 - Cargo.lock | 10 - Cargo.toml | 1 - fluxer_app_proxy/Cargo.toml | 3 - fluxer_app_proxy/Dockerfile | 8 +- fluxer_app_proxy/docker-bake.hcl | 2 - fluxer_app_proxy/src/config.rs | 55 -- fluxer_app_proxy/src/frozen_snapshots.rs | 371 ------------- fluxer_app_proxy/src/lib.rs | 3 - fluxer_app_proxy/src/routes/spa_index.rs | 222 +------- fluxer_app_proxy/src/routes/spa_static.rs | 78 +-- fluxer_app_proxy/src/time_freeze.rs | 498 ------------------ .../content/docs/operator/configuration.mdx | 4 - tools/ci/src/app_proxy.rs | 16 +- .../content/update-frozen-snapshot/Cargo.toml | 13 - .../content/update-frozen-snapshot/src/lib.rs | 158 ------ .../update-frozen-snapshot/src/main.rs | 36 -- 17 files changed, 11 insertions(+), 1469 deletions(-) delete mode 100644 fluxer_app_proxy/src/frozen_snapshots.rs delete mode 100644 fluxer_app_proxy/src/time_freeze.rs delete mode 100644 tools/content/update-frozen-snapshot/Cargo.toml delete mode 100644 tools/content/update-frozen-snapshot/src/lib.rs delete mode 100644 tools/content/update-frozen-snapshot/src/main.rs diff --git a/.github/workflows/build-app-proxy-self-hosted.yaml b/.github/workflows/build-app-proxy-self-hosted.yaml index 1646b45dd..9cd141267 100644 --- a/.github/workflows/build-app-proxy-self-hosted.yaml +++ b/.github/workflows/build-app-proxy-self-hosted.yaml @@ -71,7 +71,6 @@ jobs: BUILD_VERSION: ${{ needs.meta.outputs.build_version }} PUBLIC_ASSET_BASE_URL: "" BUNDLE_LOCAL_ASSETS: "true" - FLUXER_APP_PROXY_TIME_FREEZE_ENABLED: "false" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 env: @@ -155,7 +154,6 @@ jobs: BUILD_VERSION=${{ needs.meta.outputs.build_version }} SOURCE_SHA=${{ github.sha }} SOURCE_DATE=${{ steps.source.outputs.date }} - FLUXER_APP_PROXY_TIME_FREEZE_ENABLED=false APP_ASSETS_REF=ghcr.io/${{ env.GHCR_OWNER }}/fluxer-app-proxy-self-hosted:${{ needs.meta.outputs.build_version }}-assets APP_ASSETS_PLATFORM=linux/amd64 cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_OWNER }}/fluxer-app-proxy-self-hosted:buildcache-${{ matrix.platform }} diff --git a/Cargo.lock b/Cargo.lock index 61e9cfd04..5847af673 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1755,16 +1755,6 @@ dependencies = [ "zip", ] -[[package]] -name = "fluxer-content-update-frozen-snapshot" -version = "0.1.0" -dependencies = [ - "anyhow", - "base64 0.23.1", - "sha2 0.11.0", - "tempfile", -] - [[package]] name = "fluxer-dev" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 277a6e72a..40ceceed3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ members = [ "fluxer_messages", "fluxer_snowflakes", "tools/ci", - "tools/content/update-frozen-snapshot", "tools/dev", "tools/i18n_auto", "fluxer_users", diff --git a/fluxer_app_proxy/Cargo.toml b/fluxer_app_proxy/Cargo.toml index 673c2c278..9c995e906 100644 --- a/fluxer_app_proxy/Cargo.toml +++ b/fluxer_app_proxy/Cargo.toml @@ -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 = [] diff --git a/fluxer_app_proxy/Dockerfile b/fluxer_app_proxy/Dockerfile index e90d7684b..9943534f4 100644 --- a/fluxer_app_proxy/Dockerfile +++ b/fluxer_app_proxy/Dockerfile @@ -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 ---------- diff --git a/fluxer_app_proxy/docker-bake.hcl b/fluxer_app_proxy/docker-bake.hcl index 44d16f891..f28660e05 100644 --- a/fluxer_app_proxy/docker-bake.hcl +++ b/fluxer_app_proxy/docker-bake.hcl @@ -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 diff --git a/fluxer_app_proxy/src/config.rs b/fluxer_app_proxy/src/config.rs index fc4de7430..714c015f6 100644 --- a/fluxer_app_proxy/src/config.rs +++ b/fluxer_app_proxy/src/config.rs @@ -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, @@ -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 { 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(mut read_var: F) -> bool -where - F: FnMut(&str) -> Option, -{ - 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(mut read_var: F) -> String where F: FnMut(&str) -> Option, @@ -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 { 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"), - ])); - } } diff --git a/fluxer_app_proxy/src/frozen_snapshots.rs b/fluxer_app_proxy/src/frozen_snapshots.rs deleted file mode 100644 index 6a8e04427..000000000 --- a/fluxer_app_proxy/src/frozen_snapshots.rs +++ /dev/null @@ -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 { - base64::engine::general_purpose::STANDARD - .decode(b64) - .expect("frozen snapshot base64 decode failed") -} - -pub static STABLE_SNAPSHOT: LazyLock = 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(""), - "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" - ); - } -} diff --git a/fluxer_app_proxy/src/lib.rs b/fluxer_app_proxy/src/lib.rs index f20f11ac2..68d507bff 100644 --- a/fluxer_app_proxy/src/lib.rs +++ b/fluxer_app_proxy/src/lib.rs @@ -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; diff --git a/fluxer_app_proxy/src/routes/spa_index.rs b/fluxer_app_proxy/src/routes/spa_index.rs index 48dce480e..041a390ae 100644 --- a/fluxer_app_proxy/src/routes/spa_index.rs +++ b/fluxer_app_proxy/src/routes/spa_index.rs @@ -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, - csp: HeaderValue, - time_freeze_header: Option<&str>, - dev_no_store: bool, -) -> Response { +fn build_spa_response(html: Box, 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", - "", - "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(""), - "a frozen-served shell shipped without the bootstrap script" - ); - } - const SHELL_WITH_ENDPOINT_HOLES: &str = r#"Fluxer @@ -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#""# - ), - "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#""# - ), - "the frozen document's stylesheet URL was rewritten on the primary hosted path" - ); - assert!( - served.contains(r#""#), - "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#"