fetch verweigert Port 5060 -- und sagt dasselbe wie ein toter Server
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]>
This commit is contained in:
+58
-1
@@ -134,6 +134,63 @@ const PRO_DATEI = 2;
|
||||
* etwa weil jemand die Funktion aus einem anderen Ordner aufruft --,
|
||||
* wird NICHT geraten. Eine geratene Nummer waere genau die Doppelung,
|
||||
* die hier abgeschafft wird. */
|
||||
/* =====================================================================
|
||||
PORTS, DIE `fetch` NICHT ANFASST (20.09.2026, teuer gelernt)
|
||||
|
||||
Die Fetch-Spezifikation fuehrt eine Liste "bad ports" -- Nummern, auf
|
||||
die ein Browser KEINE Verbindung aufbaut, weil sich ueber sie fremde
|
||||
Dienste missbrauchen liessen (SMTP, NNTP, SIP, IRC und andere). Node
|
||||
hat diese Liste uebernommen: `fetch` wirft dort `TypeError: fetch
|
||||
failed` mit der Ursache `bad port`, noch bevor ein Paket fliegt.
|
||||
|
||||
WIE ES AUFGEFALLEN IST: Nach der Umstellung auf abgeleitete Nummern
|
||||
bekam pruef-chat-kanaele die 5060 -- und stuerzte ab, obwohl der
|
||||
Server nachweislich lief ("Server laeuft auf http://127.0.0.1:5060")
|
||||
und die Anmeldung ueber `http.request` sogar geklappt hatte. Nur die
|
||||
`fetch`-Aufrufe scheiterten. 5060 ist SIP und steht auf der Liste.
|
||||
|
||||
WARUM DAS SO SCHWER ZU FINDEN WAR: Die Meldung lautet "fetch failed"
|
||||
-- dieselbe Meldung wie bei einem Server, der nicht laeuft. Wer sie
|
||||
liest, sucht am Server. Der Unterschied steht erst in `.cause`:
|
||||
`bad port` statt `ECONNREFUSED`. Gegengeprueft mit einem echten
|
||||
Zuhoerer auf 5060: Port 5060 -> "bad port", Port 5062 -> sauberes
|
||||
"ECONNREFUSED". Die Sperre ist also real und trifft genau diese
|
||||
Nummern.
|
||||
|
||||
NEBENBEFUND: Auch im alten, von Hand vergebenen Bereich lag eine --
|
||||
die 4190 (ManageSieve). Sie war nie vergeben, rein zufaellig.
|
||||
===================================================================== */
|
||||
const GESPERRT = new Set([
|
||||
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,
|
||||
]);
|
||||
|
||||
/** Die n-te brauchbare Nummer ab BASIS -- gesperrte werden uebersprungen.
|
||||
*
|
||||
* Damit bleibt die Ableitung eindeutig (die Reihenfolge steht fest),
|
||||
* ohne dass eine gesperrte Nummer je herauskommt. Eine Liste von
|
||||
* Ausnahmen musste dafuer niemand pflegen: Uebersprungen wird nach
|
||||
* der Regel, nicht nach einer Tabelle von Sonderfaellen.
|
||||
*/
|
||||
export function portNummer(nte) {
|
||||
let p = BASIS;
|
||||
let uebrig = nte;
|
||||
while (true) {
|
||||
if (!GESPERRT.has(p)) {
|
||||
if (uebrig === 0) return p;
|
||||
uebrig -= 1;
|
||||
}
|
||||
p += 1;
|
||||
if (p > BASIS + 5000) {
|
||||
throw new Error("[Port] Kein freier Nummernbereich mehr oberhalb von " + BASIS + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function eigenerPort(meta, wer = null, nr = 0) {
|
||||
const pfad = fileURLToPath(meta.url);
|
||||
const mein = basename(pfad);
|
||||
@@ -151,5 +208,5 @@ export async function eigenerPort(meta, wer = null, nr = 0) {
|
||||
if (nr < 0 || nr >= PRO_DATEI) {
|
||||
throw new Error(`[Port] Es gibt ${PRO_DATEI} Nummern je Datei (0 und 1), nicht ${nr}.`);
|
||||
}
|
||||
return portMussFreiSein(BASIS + stelle * PRO_DATEI + nr, wer || mein.replace(/\.mjs$/, ""));
|
||||
return portMussFreiSein(portNummer(stelle * PRO_DATEI + nr), wer || mein.replace(/\.mjs$/, ""));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
===================================================================== */
|
||||
|
||||
import { readdirSync, readFileSync } from "node:fs";
|
||||
import { eigenerPort } from "./helfer-port.mjs";
|
||||
import { eigenerPort, portNummer } from "./helfer-port.mjs";
|
||||
|
||||
let ok_ = 0, fehl = 0;
|
||||
const ok = (b, text) => { if (b) { ok_++; console.log(" ok " + text); }
|
||||
@@ -54,7 +54,7 @@ const belegt = new Map();
|
||||
let doppelt = 0;
|
||||
dateien.forEach((f, i) => {
|
||||
for (let n = 0; n < 2; n++) {
|
||||
const p = 5000 + i * 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);
|
||||
}
|
||||
@@ -62,6 +62,52 @@ dateien.forEach((f, i) => {
|
||||
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
|
||||
@@ -70,7 +116,7 @@ console.log("\n=== 3. Die Ableitung stimmt wirklich (gemessen, nicht gerechnet)
|
||||
const ersteDatei = dateien[0];
|
||||
const meine = dateien.indexOf("pruef-portnummern.mjs");
|
||||
const p0 = await eigenerPort(import.meta, "pruef-portnummern (Selbsttest)", 0);
|
||||
ok(p0 === 5000 + meine * 2,
|
||||
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`);
|
||||
|
||||
Reference in New Issue
Block a user