Nach der Umstellung auf abgeleitete Nummern bekam pruef-chat-kanaele
die 5060 und stuerzte ab:
TypeError: fetch failed
Der Server lief nachweislich ("Server laeuft auf http://127.0.0.1:5060"),
die Anmeldung ueber `http.request` hatte sogar geklappt, und BASIS war
sauber ("http://127.0.0.1:5060", 21 Zeichen, PORT eine Zahl). Nur die
`fetch`-Aufrufe scheiterten.
5060 ist SIP und steht auf der "bad ports"-Liste der
Fetch-Spezifikation. Node hat sie uebernommen: `fetch` baut dorthin
keine Verbindung auf, noch bevor ein Paket fliegt. Die Ursache steht
nur in `.cause` -- "bad port" statt "ECONNREFUSED". Die Meldung
obendrueber ist beide Male dieselbe, und wer sie liest, sucht am
Server.
Gegengeprueft mit einem echten Zuhoerer auf beiden Nummern:
5060 -> fetch failed (bad port)
5062 -> fetch failed (ECONNREFUSED) <- also normal erreichbar
`eigenerPort` ueberspringt die gesperrten Nummern jetzt (portNummer).
Uebersprungen wird nach der REGEL, nicht nach einer Tabelle von
Sonderfaellen: die n-te brauchbare Nummer ab 5000. Eindeutig bleibt es,
weil die Reihenfolge feststeht.
pruef-portnummern (11 statt 8) prueft es doppelt: gegen die Liste UND
am echten Verhalten -- ein Zuhoerer auf 5060 muss fuer `fetch` tot
sein, einer auf einer abgeleiteten Nummer erreichbar. Ohne die zweite
Haelfte waere die Liste nur eine Behauptung ueber Node, und
Behauptungen altern.
Nebenbefund: Auch im alten, von Hand vergebenen Bereich lag eine
gesperrte Nummer -- die 4190. Sie war nie vergeben, rein zufaellig.
pruef-chat-kanaele: 79 Pruefungen, 0 Fehler -- wieder auf der
Grundlinie, die vor der Umstellung gemessen wurde.
Co-Authored-By: Claude Opus 5 <[email protected]>
144 lines
6.9 KiB
JavaScript
144 lines
6.9 KiB
JavaScript
/* =====================================================================
|
|
pruef-portnummern.mjs — Zwei Pruefungen, eine Nummer
|
|
|
|
WAS HIER SCHIEFGING (20.09.2026 gemessen):
|
|
164 Pruefdateien, 129 verschiedene Nummern -- NEUNZEHN waren doppelt
|
|
vergeben, 4186 und 4188 und 4196 und 4198 sogar dreifach. Niemand
|
|
hatte das gewollt; jede neue Pruefung wurde von einer vorhandenen
|
|
abgeschrieben, und die Nummer kam mit.
|
|
|
|
WAS DAS ANRICHTET: Der Waechter bricht ab, statt gegen einen fremden
|
|
Server zu messen -- so weit, so richtig. Aber zwei Pruefungen mit
|
|
derselben Nummer koennen nie gleichzeitig laufen, und ein
|
|
liegengebliebener Prozess der einen laesst die andere mit einer
|
|
Meldung abbrechen, die wie ein Befund aussieht. Genau das ist am
|
|
20.09. zweimal passiert, beide Male mit Fehlersuche an der falschen
|
|
Stelle.
|
|
|
|
WAS DIESE PRUEFUNG SICHERT: Dass die Nummer ABGELEITET bleibt. Eine
|
|
von Hand eingetragene Zahl ist der Anfang der naechsten Doppelung --
|
|
sie faellt nur niemandem auf, bis zwei Laeufe kollidieren.
|
|
===================================================================== */
|
|
|
|
import { readdirSync, readFileSync } from "node:fs";
|
|
import { eigenerPort, portNummer } from "./helfer-port.mjs";
|
|
|
|
let ok_ = 0, fehl = 0;
|
|
const ok = (b, text) => { if (b) { ok_++; console.log(" ok " + text); }
|
|
else { fehl++; console.log(" FEHL " + text); } };
|
|
|
|
const ORDNER = new URL(".", import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1");
|
|
const dateien = readdirSync(ORDNER)
|
|
.filter((f) => f.startsWith("pruef-") && f.endsWith(".mjs")).sort();
|
|
|
|
console.log("\n=== 1. Niemand traegt eine Nummer von Hand ein ===");
|
|
const vonHand = [];
|
|
const ohneWaechter = [];
|
|
for (const f of dateien) {
|
|
const s = readFileSync(ORDNER + "/" + f, "utf8");
|
|
/* Kommentare zaehlen nicht: In helfer-port.mjs und hier stehen alte
|
|
Nummern als Beleg, und ein Beleg ist kein Eintrag. */
|
|
const code = s.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
|
|
if (/portMussFreiSein\(\s*\d/.test(code)) vonHand.push(f);
|
|
const startetServer = /listen\(|process\.env\.PORT\s*=/.test(code);
|
|
if (startetServer && !/eigenerPort\(\s*import\.meta/.test(code)) ohneWaechter.push(f);
|
|
}
|
|
ok(dateien.length > 100, `${dateien.length} Pruefdateien angesehen`);
|
|
ok(vonHand.length === 0,
|
|
`keine traegt eine feste Nummer im Waechter (${vonHand.slice(0, 4).join(", ") || "—"})`);
|
|
ok(ohneWaechter.length === 0,
|
|
`jede, die einen Server startet, leitet ihre Nummer ab (${ohneWaechter.slice(0, 4).join(", ") || "—"})`);
|
|
|
|
console.log("\n=== 2. Die abgeleiteten Nummern sind eindeutig ===");
|
|
const belegt = new Map();
|
|
let doppelt = 0;
|
|
dateien.forEach((f, i) => {
|
|
for (let n = 0; n < 2; n++) {
|
|
const p = portNummer(i * 2 + n);
|
|
if (belegt.has(p)) { doppelt++; console.log(` ${p}: ${belegt.get(p)} und ${f}`); }
|
|
belegt.set(p, f);
|
|
}
|
|
});
|
|
ok(belegt.size === dateien.length * 2 && doppelt === 0,
|
|
`${belegt.size} Nummern fuer ${dateien.length} Dateien, ${doppelt} doppelt`);
|
|
|
|
console.log("\n=== 2b. Keine davon ist eine, die `fetch` verweigert ===");
|
|
/* DIE TEUERSTE HALBE STUNDE DIESES ABENDS. Nach der Umstellung bekam
|
|
pruef-chat-kanaele die 5060 und stuerzte ab -- obwohl der Server
|
|
nachweislich lief und die Anmeldung ueber `http.request` geklappt
|
|
hatte. 5060 ist SIP und steht auf der "bad ports"-Liste der
|
|
Fetch-Spezifikation, die Node uebernommen hat: `fetch` baut dorthin
|
|
keine Verbindung auf.
|
|
|
|
Die Meldung lautet "fetch failed" -- genau wie bei einem Server, der
|
|
nicht laeuft. Wer sie liest, sucht am Server. Deshalb steht sie
|
|
jetzt hier als eigene Pruefung: Die naechste Datei, die in so eine
|
|
Nummer rutscht, soll es HIER erfahren und nicht in einer
|
|
Fehlersuche. */
|
|
const BOESE = [1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53,
|
|
69, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119,
|
|
123, 135, 137, 138, 139, 143, 161, 179, 389, 427, 465, 512, 513, 514, 515,
|
|
526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636, 989, 990, 993,
|
|
995, 1719, 1720, 1723, 2049, 3659, 4045, 4190, 5060, 5061, 6000, 6566, 6665,
|
|
6666, 6667, 6668, 6669, 6679, 6697, 10080];
|
|
const getroffen = [...belegt.keys()].filter((x) => BOESE.includes(x));
|
|
ok(getroffen.length === 0,
|
|
`keine der ${belegt.size} Nummern steht auf der Sperrliste (${getroffen.join(", ") || "—"})`);
|
|
|
|
/* GEGENPROBE AM ECHTEN VERHALTEN, nicht an der Liste: Ein Zuhoerer auf
|
|
einer gesperrten Nummer muss fuer `fetch` unerreichbar sein, einer
|
|
daneben erreichbar. Ohne das waere die Liste oben nur eine
|
|
Behauptung ueber Node -- und Behauptungen altern. */
|
|
{
|
|
const { createServer } = await import("node:http");
|
|
const probe = async (nummer) => {
|
|
const dienst = createServer((q, a) => { a.writeHead(200); a.end("da"); });
|
|
await new Promise((r) => dienst.listen(nummer, "127.0.0.1", r));
|
|
let grund = "erreichbar";
|
|
try { await fetch(`http://127.0.0.1:${nummer}/`); }
|
|
catch (f) { grund = f?.cause?.message || f?.message || "?"; }
|
|
await new Promise((r) => dienst.close(r));
|
|
return grund;
|
|
};
|
|
const gesperrt = await probe(5060);
|
|
const erlaubt = await probe(portNummer(0));
|
|
ok(/bad port/i.test(gesperrt),
|
|
`eine gesperrte Nummer ist fuer fetch tot, obwohl jemand lauscht (5060: ${gesperrt})`);
|
|
ok(erlaubt === "erreichbar",
|
|
`eine abgeleitete dagegen nicht (${portNummer(0)}: ${erlaubt})`);
|
|
}
|
|
|
|
console.log("\n=== 3. Die Ableitung stimmt wirklich (gemessen, nicht gerechnet) ===");
|
|
/* NICHT die Formel gegen sich selbst pruefen -- das waere eine
|
|
Bestaetigung ohne Aussage. Gefragt wird die Funktion, die die
|
|
Pruefungen auch benutzen, und verglichen wird mit der Stelle im
|
|
Alphabet. */
|
|
const ersteDatei = dateien[0];
|
|
const meine = dateien.indexOf("pruef-portnummern.mjs");
|
|
const p0 = await eigenerPort(import.meta, "pruef-portnummern (Selbsttest)", 0);
|
|
ok(p0 === portNummer(meine * 2),
|
|
`meine eigene Nummer ist ${p0} — Stelle ${meine} im Alphabet, erste Datei ${ersteDatei}`);
|
|
const p1 = await eigenerPort(import.meta, "pruef-portnummern (Selbsttest)", 1);
|
|
ok(p1 === p0 + 1, `und die zweite ist ${p1}, also genau eine daneben`);
|
|
|
|
console.log("\n=== 4. Gegenprobe: kann sie auch NICHT IN ORDNUNG sagen? ===");
|
|
/* Eine Pruefung, die immer bestaetigt, bestaetigt nichts. */
|
|
let drittesAusgangGriff = false;
|
|
try {
|
|
/* Eine Datei, die es nicht gibt -- die Ableitung darf dann NICHT
|
|
raten, sondern muss abbrechen. */
|
|
await eigenerPort({ url: new URL("./gibt-es-nicht.mjs", import.meta.url).href }, "Gegenprobe");
|
|
} catch (f) {
|
|
drittesAusgangGriff = /steht nicht in der Liste/.test(f?.message || "");
|
|
}
|
|
ok(drittesAusgangGriff, "eine unbekannte Datei bekommt keine geratene Nummer, sondern einen Abbruch");
|
|
|
|
let zuHoheNummer = false;
|
|
try { await eigenerPort(import.meta, "Gegenprobe", 7); }
|
|
catch (f) { zuHoheNummer = /Nummern je Datei/.test(f?.message || ""); }
|
|
ok(zuHoheNummer, "und eine dritte Nummer je Datei gibt es nicht");
|
|
|
|
console.log(`\n${ok_} Pruefungen, ${fehl} Fehler`);
|
|
console.log(fehl ? "NICHT IN ORDNUNG\n" : "ALLES IN ORDNUNG\n");
|
|
process.exit(fehl ? 1 : 0);
|