const DEBUG = false;
const DPI_THR_BYTES = 64 * 1024;
const MAX_URI_X_SIZE = 7 * 1024;
const RIPE_API_URL = "https://stat.ripe.net/data/";
const ALIVE_KEY = "alive";
const ALIVE_NO = 0;
const ALIVE_YES = 1;
const ALIVE_UNKNOWN = 2;
const DPI_METHOD_KEY = "dpi";
const DPI_METHOD_NOT_DETECTED = 0;
const DPI_METHOD_DETECTED = 1;
const DPI_METHOD_PROBABLY = 2;
const DPI_METHOD_POSSIBLE = 3;
const DPI_METHOD_UNLIKELY = 4;
let testSuite = []; // Fetched from ./suite.v2.json
let timeoutMs = 15000;
let clientAsn = 0;
let resultItems = {};
const getParamsHandler = () => {
const params = new URLSearchParams(window.location.search);
const host = params.get("host");
if (host) {
const provider = params.get("provider") || "Custom";
const newTest = { id: `CUSTOM-01`, provider, host, country: "💡" };
testSuite.push(newTest);
}
timeoutMs = parseInt(params.get("timeout")) || timeoutMs;
};
const getDefaultFetchOpt = (ctrl, method = "GET",) => ({
method,
mode: "no-cors",
referrer: "",
credentials: "omit",
cache: "no-store",
signal: ctrl.signal,
redirect: "follow",
// The body size for keepalive requests is limited to 64 kibibytes.
// https://developer.mozilla.org/en-US/docs/Web/API/RequestInit#keepalive
keepalive: false
});
const headerEl = document.getElementById("header");
const startButtonEl = document.getElementById("start-btn");
const shareButtonEl = document.getElementById("share-btn");
const statusEl = document.getElementById("status");
const logEl = document.getElementById("log");
const resultsEl = document.getElementById("results");
const shareTsEl = document.getElementById("shareTs");
const asnEl = document.getElementById("asn");
const toggleUI = (locked) => {
shareButtonEl.disabled = locked;
startButtonEl.disabled = locked;
startButtonEl.textContent = locked ? "🔍 ..." : "🔍 Start";
statusEl.className = locked ? "status-checking" : "status-ready";
};
const setStatus = (col, text, cls) => {
col.textContent = text;
col.className = cls;
if (cls === "bad") statusEl.className = "status-error";
};
const logPush = (level, prefix, msg) => {
const now = new Date();
const ts = now.toLocaleTimeString([], { hour12: false }) + "." + now.getMilliseconds().toString().padStart(3, "0");
logEl.textContent += `[${ts}] ${prefix ? prefix + "/" : ""}${level}: ${msg}\n`;
logEl.scrollTop = logEl.scrollHeight;
};
const timeElapsed = t0 => `${(performance.now() - t0).toFixed(1)} ms`;
const getHttpStatus = id => httpCodes[id];
const getUniqueUrl = url => {
return url.includes('?') ? `${url}&t=${Math.random()}` : `${url}?t=${Math.random()}`;
};
const getRandomData = size => {
const data = new Uint8Array(size);
const grvMax = 64 * 1024; // https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
for (let offset = 0; offset < size; offset += grvMax) {
crypto.getRandomValues(data.subarray(offset, offset + grvMax));
}
return data;
};
const getRandomSafeData = (n) => {
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return Array.from({ length: n }, () =>
chars[Math.floor(Math.random() * chars.length)]
).join("");
};
const startOrchestrator = async () => {
statusEl.textContent = "Checking ⏰";
statusEl.className = "status-checking";
for (let i = resultsEl.rows.length - 1; i > 0; i--) {
resultsEl.deleteRow(i);
}
resultItems = {};
try {
const tasks = [];
for (let t of testSuite) {
tasks.push(checkDpi(t.id, t.provider, t.host, t.country));
}
await Promise.all(tasks);
statusEl.textContent = "Ready ⚡";
statusEl.className = "status-ready";
} catch (e) {
statusEl.textContent = "Unexpected error ⚠️";
logPush("ERR", null, `Unexpected error => ${e}`);
statusEl.className = "status-error";
}
logPush("INFO", null, "Done.");
toggleUI(false);
};
const handleDpiMethodErr = (alive, e) => {
if (e.name === "AbortError") {
if (alive) {
return DPI_METHOD_DETECTED; // alive — ok, push — timeout
}
return DPI_METHOD_PROBABLY; // alive — instant error, push — timeout
}
if (alive) {
return DPI_METHOD_POSSIBLE; // alive — ok, push — instant error
}
return DPI_METHOD_UNLIKELY; // alive — instant error, push — instant error
};
const dpiHugeBodyPostMethod = async (alive, host) => {
try {
const dpiCtrl = new AbortController();
const dpiTimeoutId = setTimeout(() => dpiCtrl.abort(), timeoutMs);
const opt = getDefaultFetchOpt(dpiCtrl, "POST")
opt.body = getRandomData(DPI_THR_BYTES)
const url = `https://${host}/`;
await fetch(getUniqueUrl(url), opt);
clearTimeout(dpiTimeoutId);
} catch (e) {
return handleDpiMethodErr(alive, e);
}
return DPI_METHOD_NOT_DETECTED;
};
const dpiHugeReqlineHeadMethod = async (alive, host) => {
try {
const times = DPI_THR_BYTES / MAX_URI_X_SIZE;
const dpiCtrl = new AbortController();
const dpiTimeoutId = setTimeout(() => dpiCtrl.abort(), timeoutMs);
for (let i = 0; i < times; i++) {
const opt = getDefaultFetchOpt(dpiCtrl, "HEAD") // HEAD seems to be stable keep-alived
const url = `https://${host}/?x=${getRandomSafeData(MAX_URI_X_SIZE)}`
await fetch(getUniqueUrl(url), opt);
}
clearTimeout(dpiTimeoutId);
} catch (e) {
return handleDpiMethodErr(alive, e);
}
return DPI_METHOD_NOT_DETECTED;
};
const checkDpi = async (id, provider, host, country) => {
const prefix = `DPI checking(#${id})`;
let t0 = performance.now();
const row = resultsEl.insertRow();
const idCell = row.insertCell();
const providerCell = row.insertCell();
const aliveStatusCell = row.insertCell();
const dpiStatusCell = row.insertCell();
let alive = false;
let possibleAlive = false;
idCell.textContent = id;
resultItems[id] = {};
setPrettyProvider(providerCell, provider, country);
setStatus(aliveStatusCell, "Checking ⏰", "");
setStatus(dpiStatusCell, "Waiting ⏰", "");
try {
// alive check
const aliveCtrl = new AbortController();
const aliveTimeoutId = setTimeout(() => aliveCtrl.abort(), timeoutMs);
const url = `https://${host}/`
await fetch(getUniqueUrl(url), getDefaultFetchOpt(aliveCtrl, "HEAD"));
clearTimeout(aliveTimeoutId);
logPush("INFO", prefix, `alived: yes 🟢, reqtime: ${timeElapsed(t0)}`);
resultItems[id][ALIVE_KEY] = ALIVE_YES;
alive = true;
possibleAlive = true;
}
catch (e) {
console.log(e);
if (e.name === "AbortError") {
logPush("INFO", prefix, `alived: no 🔴, reqtime: ${timeElapsed(t0)}`);
resultItems[id][ALIVE_KEY] = ALIVE_NO;
} else {
logPush("INFO", prefix, `alived: unknown ⚠️, reqtime: ${timeElapsed(t0)}`);
resultItems[id][ALIVE_KEY] = ALIVE_UNKNOWN;
possibleAlive = true;
}
}
setPrettyAlive(aliveStatusCell, resultItems[id][ALIVE_KEY]);
if (!alive && !possibleAlive) {
setPrettyDpi(dpiStatusCell, ALIVE_NO, null); // -> skip
resultItems[id][DPI_METHOD_KEY] = DPI_METHOD_NOT_DETECTED; // default value
return;
}
// dpi check
setStatus(dpiStatusCell, "Checking ⏰", "");
const m1 = await dpiHugeBodyPostMethod(alive, host);
if (m1 == DPI_METHOD_DETECTED) {
logPush("INFO", prefix, `tcp 16-20: detected❗️, method: 1`);
setPrettyDpi(dpiStatusCell, resultItems[id][ALIVE_KEY], m1);
resultItems[id][DPI_METHOD_KEY] = DPI_METHOD_DETECTED;
return;
}
t0 = performance.now();
const m2 = await dpiHugeReqlineHeadMethod(alive, host);
resultItems[id][DPI_METHOD_KEY] = m2;
setPrettyDpi(dpiStatusCell, resultItems[id][ALIVE_KEY], m2);
const logDpiMap = {
[DPI_METHOD_DETECTED]: `tcp 16-20: detected❗️, method: 2`,
[DPI_METHOD_PROBABLY]: `tcp 16-20: probably detected ⚠️, reqtime: ${timeElapsed(t0)}`,
[DPI_METHOD_POSSIBLE]: `tcp 16-20: possible detected ⚠️, reqtime: ${timeElapsed(t0)}`,
[DPI_METHOD_UNLIKELY]: `tcp 16-20: unlikely ⚠️, reqtime: ${timeElapsed(t0)}`,
[DPI_METHOD_NOT_DETECTED]: `tcp 16-20: not detected ✅, reqtime: ${timeElapsed(t0)}`,
}
logPush("INFO", prefix, logDpiMap[m2]);
};
const insertDebugRow = () => {
const row = resultsEl.insertRow();
const idCell = row.insertCell();
const providerCell = row.insertCell();
const aliveStatusCell = row.insertCell();
const dpiStatusCell = row.insertCell();
idCell.textContent = "XY.ABCD-01"
providerCell.textContent = "🇺🇸 AbcdefQwerty"
aliveStatusCell.textContent = "Checking ⏰"
dpiStatusCell.textContent = "Checking ⏰"
}
const fetchAsnBasic = async (asn) => {
const holder = (await (await fetch(RIPE_API_URL + "as-overview/data.json?resource=" + asn)).json()).data.holder;
asnEl.innerHTML = `ASN: AS${asn} (${holder})`;
};
const fetchAsn = async () => {
try {
const ip = (await (await fetch(RIPE_API_URL + "whats-my-ip/data.json")).json()).data.ip;
const asn = (await (await fetch(RIPE_API_URL + "prefix-overview/data.json?resource=" + ip)).json()).data.asns[0];
clientAsn = Number(asn.asn);
const geo = (await (await fetch(RIPE_API_URL + "maxmind-geo-lite/data.json?resource=" + ip)).json()).data.located_resources[0].locations[0];
asnEl.innerHTML = `ASN: AS${asn.asn} (${asn.holder})${geo.country}, ${geo.city || "—"}`;
} catch (err) {
console.error("Fetch ASN err:", err);
}
};
const fetchSuite = async () => {
try {
testSuite = await (await fetch(getUniqueUrl("./suite.v2.json"))).json();
startButtonEl.disabled = false;
} catch {
logPush("ERR", null, `Fetch suite failed. Probably a CORS issue (running locally?).`);
}
};
const prettyTs = (ts) => {
return ts.toISOString().slice(0, 16).replace('T', ' ');
}
const setPrettyProvider = (el, provider, country) => {
el.textContent = `${country} ${provider}`;
};
const setPrettyDpi = (el, alive, dpi) => {
if (alive == ALIVE_NO) {
setStatus(el, "Skip ⚠️", "skip");
return;
}
const m = {
[DPI_METHOD_NOT_DETECTED]: () => setStatus(el, "No ✅", "ok"),
[DPI_METHOD_DETECTED]: () => setStatus(el, "Detected❗️", "bad"),
[DPI_METHOD_PROBABLY]: () => setStatus(el, "Probably❗️", "skip"),
[DPI_METHOD_POSSIBLE]: () => setStatus(el, "Possible ⚠️", "skip"),
[DPI_METHOD_UNLIKELY]: () => setStatus(el, "Unlikely ⚠️", "skip"),
};
m[dpi]();
};
const setPrettyAlive = (el, alive) => {
const m = {
[ALIVE_NO]: () => setStatus(el, "No 🔴", "bad"),
[ALIVE_YES]: () => setStatus(el, "Yes 🟢", "ok"),
[ALIVE_UNKNOWN]: () => setStatus(el, "Unknown ⚠️", "skip"),
}
m[alive]();
};
const renderShare = (share) => {
shareTsEl.textContent = `Test timestamp: ${prettyTs(share.ts)}`;
for (let v of share.items) {
const row = resultsEl.insertRow();
const idCell = row.insertCell();
const providerCell = row.insertCell();
const aliveStatusCell = row.insertCell();
const dpiStatusCell = row.insertCell();
idCell.textContent = v.id;
setPrettyProvider(providerCell, v.provider, v.country);
setPrettyAlive(aliveStatusCell, v.alive);
setPrettyDpi(dpiStatusCell, v.alive, v.dpi);
}
};
// the contract should not be changed because it is used by historical functions
const rawImport = async (url) => {
const res = await fetch(url);
const code = await res.text();
const blobUrl = URL.createObjectURL(
new Blob([code], { type: 'text/javascript' })
);
return await import(blobUrl);
};
const tryHandleShare = async () => {
const params = new URLSearchParams(window.location.search);
const share = params.get("share");
if (share) {
const link = location.pathname;
headerEl.innerHTML = `Want to try it too? Click here ⚡`;
headerEl.hidden = false;
try {
resultsEl.hidden = true;
logEl.hidden = true;
const buf = Uint8Array.fromBase64(share, { alphabet: "base64url" });
const h = await import('./share/helpers.js');
const commitHex = h.getCommitHex(buf);
const relPath = "share/decoder.js";
let decoderUrl = `https://raw.githubusercontent.com/${h.REPO}/${commitHex}/ru/tcp-16-20/${relPath}`;
if (DEBUG) {
decoderUrl = "./" + relPath;
}
const { decodeShare } = await rawImport(decoderUrl);
const decoded = await decodeShare(h.REPO, commitHex, buf);
fetchAsnBasic(decoded.asn);
renderShare(decoded);
resultsEl.hidden = false;
}
catch (e) {
console.log(e);
shareTsEl.hidden = true;
asnEl.hidden = true;
if (typeof Uint8Array.prototype.fromBase64 !== "function") {
alert("To see the results, you need to update your browser.");
return true;
}
alert("The results are out of date or internal error.");
}
return true;
}
return false;
};
startButtonEl.onclick = () => {
logEl.textContent = "";
toggleUI(true);
localStorage.clear();
sessionStorage.clear();
startOrchestrator();
};
shareButtonEl.onclick = async () => {
const prevContent = shareButtonEl.textContent;
shareButtonEl.textContent = "🔗 ..."
shareButtonEl.disabled = true;
try {
const encoded = await encodeShare(clientAsn, resultItems);
const url = `${window.location.origin + window.location.pathname}?share=${encoded}`;
try {
await navigator.clipboard.writeText(url);
alert("Link to results copied to clipboard.");
} catch {
alert("Error writing to clipboard. Permissions granted?");
}
}
catch {
if (typeof Uint8Array.prototype.toBase64 !== "function") {
alert("To share the results, you should update your browser.");
return true;
}
alert("Error when encoding results.");
}
shareButtonEl.textContent = prevContent;
shareButtonEl.disabled = false;
};
document.addEventListener("DOMContentLoaded", async () => {
if (DEBUG) {
console.log("debug mode: on");
insertDebugRow();
}
if (await tryHandleShare()) {
return;
}
fetchAsn();
await fetchSuite();
getParamsHandler();
});