engine: downstream MTU and downstream train in the measurement document
Three facts the client cannot produce alone, kept deliberately separate: mtu.pmtud_down (largest datagram that arrives unfragmented — meaningful only because the server sets DF), mtu.frag_delivery (whether larger ones arrive once fragmentation is allowed), and train.udp_downstream (loss, reordering and arrival spacing in the download direction, which a round trip cannot separate from upstream loss). ServerMeasurement now runs them on the same ProbeSession as the echo train. It had to: a fresh session restarts client-side sequence numbers and the server's anti-replay window discards the lot, so the re-primed source is never recorded and every granted send goes to a socket that has already closed. That produced four confidently-wrong FAILED tests and a RED verdict on a healthy network. Live against fmr: path MTU 1500, fragments to 4000, 100/100 downstream, GREEN. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ce1aaa332a
commit
14e5fad1b2
+47
-14
@@ -3,28 +3,32 @@
|
||||
|
||||
// Everything under public/ is served straight from the edge without invoking
|
||||
// this Worker. The Worker exists for the short stable URLs (/apk, /fdroid,
|
||||
// /source) — short enough for a QR code — and to resolve "/apk" to the newest
|
||||
// release asset at request time, so tagging a release in Gitea is the only
|
||||
// publish step. No site redeploy, no URL to update.
|
||||
// /source) — short enough for a QR code — and for /api/latest, which the
|
||||
// homepage uses to show the current version. Both resolve the newest release
|
||||
// from the Gitea API at request time, so tagging a release in Gitea is the
|
||||
// only publish step. No site redeploy, no URL to update.
|
||||
|
||||
const STATIC_ROUTES = {
|
||||
"/fdroid": "FDROID_URL",
|
||||
"/source": "SOURCE_URL",
|
||||
};
|
||||
|
||||
// Resolve the newest APK from the Gitea "latest release" API. Cached at the
|
||||
// edge for 5 minutes so a release becomes visible quickly, while Gitea sees
|
||||
// at most one API hit per POP per 5 min regardless of download traffic.
|
||||
async function latestApkUrl(env) {
|
||||
// Fetch the Gitea "latest release" object. Cached at the edge for 5 minutes so
|
||||
// a new release becomes visible quickly, while Gitea sees at most one API hit
|
||||
// per POP per 5 min regardless of traffic. Returns null on any failure —
|
||||
// callers degrade to fallbacks rather than surfacing errors.
|
||||
async function latestRelease(env) {
|
||||
if (!env.GITEA_REPO_API) return null;
|
||||
const res = await fetch(`${env.GITEA_REPO_API}/releases/latest`, {
|
||||
headers: { Accept: "application/json", "User-Agent": "echolot-site" },
|
||||
cf: { cacheTtl: 300, cacheEverything: true },
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
const rel = await res.json();
|
||||
const apk = rel.assets?.find((a) => a.name?.endsWith(".apk"));
|
||||
return apk?.browser_download_url ?? null;
|
||||
return res.json();
|
||||
}
|
||||
|
||||
function asset(rel, suffix) {
|
||||
return rel?.assets?.find((a) => a.name?.endsWith(suffix)) ?? null;
|
||||
}
|
||||
|
||||
function redirect(location) {
|
||||
@@ -38,21 +42,50 @@ function redirect(location) {
|
||||
});
|
||||
}
|
||||
|
||||
function json(body, maxAge) {
|
||||
return new Response(JSON.stringify(body), {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": `public, max-age=${maxAge}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const { pathname } = new URL(request.url);
|
||||
const path = pathname.replace(/\/$/, "");
|
||||
const fallback = new URL("/#install", request.url).toString();
|
||||
|
||||
if (path === "/apk" || path === "/download") {
|
||||
// Order: live Gitea release → manual override → install section.
|
||||
if (path === "/apk" || path === "/download" || path === "/apk.sha256") {
|
||||
const suffix = path === "/apk.sha256" ? ".sha256" : ".apk";
|
||||
let target = null;
|
||||
try {
|
||||
target = await latestApkUrl(env);
|
||||
target = asset(await latestRelease(env), suffix)?.browser_download_url;
|
||||
} catch {
|
||||
// Gitea unreachable — fall through rather than 500 on a download link.
|
||||
}
|
||||
return redirect(target || env.DOWNLOAD_URL || fallback);
|
||||
const override = suffix === ".apk" ? env.DOWNLOAD_URL : null;
|
||||
return redirect(target || override || fallback);
|
||||
}
|
||||
|
||||
if (path === "/api/latest") {
|
||||
let rel = null;
|
||||
try {
|
||||
rel = await latestRelease(env);
|
||||
} catch {}
|
||||
const apk = asset(rel, ".apk");
|
||||
if (!rel || !apk) return json({ available: false }, 60);
|
||||
return json(
|
||||
{
|
||||
available: true,
|
||||
version: rel.tag_name,
|
||||
published_at: rel.published_at,
|
||||
apk: { name: apk.name, size: apk.size, url: apk.browser_download_url },
|
||||
sha256: Boolean(asset(rel, ".sha256")),
|
||||
},
|
||||
300,
|
||||
);
|
||||
}
|
||||
|
||||
const varName = STATIC_ROUTES[path];
|
||||
|
||||
Reference in New Issue
Block a user