HWLAB_PROXY_PROBE_PHASE=__HWLAB_PROXY_PHASE_SHELL__ HWLAB_PROXY_PROBE_URL=__HWLAB_PROXY_TARGET_SHELL__ node <<'NODE' || echo '{"event":"dependency-proxy-probe-nonblocking","phase":"__HWLAB_PROXY_PHASE__","ok":false,"reason":"probe-failed-but-continuing"}' const net = require("node:net"); const phase = process.env.HWLAB_PROXY_PROBE_PHASE || "unknown"; const target = new URL(process.env.HWLAB_PROXY_PROBE_URL || "http://deb.debian.org/debian/dists/bookworm/InRelease"); const proxyRaw = process.env.HTTP_PROXY || process.env.http_proxy || ""; function redactProxy(value) { if (!value) return ""; try { const parsed = new URL(value); if (parsed.username || parsed.password) { parsed.username = "***"; parsed.password = ""; } return parsed.toString(); } catch { return ""; } } function emit(payload, exitCode = 0) { console.log(JSON.stringify({ event: "dependency-proxy-probe", phase, target: target.href, proxy: redactProxy(proxyRaw), ...payload })); if (exitCode) process.exit(exitCode); } if (!proxyRaw) emit({ ok: false, reason: "proxy-env-missing" }, 21); let proxy; try { proxy = new URL(proxyRaw); } catch (error) { emit({ ok: false, reason: "proxy-url-invalid", error: error.message }, 22); } if (proxy.protocol !== "http:" && proxy.protocol !== "https:") { emit({ ok: false, reason: "http-proxy-required", protocol: proxy.protocol }, 23); } const startedAt = Date.now(); const socket = net.createConnection({ host: proxy.hostname, port: Number(proxy.port || (proxy.protocol === "https:" ? 443 : 80)) }); let bytes = 0; let firstByteMs = null; let responseHead = ""; let finished = false; const timeout = setTimeout(() => { if (finished) return; finished = true; socket.destroy(); emit({ ok: false, reason: "timeout", timeoutMs: 15000 }, 24); }, 15000); socket.on("connect", () => { socket.write("GET " + target.href + " HTTP/1.1\r\nHost: " + target.host + "\r\nUser-Agent: hwlab-node-proxy-probe\r\nConnection: close\r\n\r\n"); }); socket.on("data", (chunk) => { if (firstByteMs === null) firstByteMs = Date.now() - startedAt; bytes += chunk.length; if (responseHead.length < 240) responseHead += chunk.toString("utf8", 0, Math.min(chunk.length, 240 - responseHead.length)); }); socket.on("end", () => { if (finished) return; finished = true; clearTimeout(timeout); const totalMs = Date.now() - startedAt; const statusLine = responseHead.split("\r\n", 1)[0] || ""; const statusCode = Number(statusLine.split(" ")[1] || 0); const ok = statusLine.startsWith("HTTP/") && statusCode >= 200 && statusCode < 400; emit({ ok, reason: ok ? null : "bad-http-status", statusLine, statusCode, firstByteMs, totalMs, bytes, speedBytesPerSecond: totalMs > 0 ? Math.round(bytes * 1000 / totalMs) : 0 }, ok ? 0 : 25); }); socket.on("error", (error) => { if (finished) return; finished = true; clearTimeout(timeout); emit({ ok: false, reason: "proxy-connect-failed", error: error.message, totalMs: Date.now() - startedAt }, 26); }); NODE