From 5d3e3e5e8ca6e17b3d47a1242511c86295e38bc4 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sat, 30 May 2026 11:51:23 +0200 Subject: [PATCH] =?UTF-8?q?fix(diagnostic):=20ZIP-Download=20abgewuergt=20?= =?UTF-8?q?=E2=80=94=20req.on(close)=20zu=20aggressiv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug-Report Stefan: Datei-Manager in der Android-App kann nichts mehr herunterladen. Test gegen /api/files-download-zip lieferte 79 Bytes ZIP (nur Header) statt der erwarteten 26 KB. Ursache: req.on("close", () => zip.kill("SIGTERM")) sollte den zip-Subprocess killen wenn der Client mid-stream abbricht. ABER: req.on("close") feuert in Node.js auch SOFORT nachdem der Request- Body fertig gelesen wurde — nicht erst bei echtem Client-Disconnect. Folge: zip wird unmittelbar nach req.on("end") gekilled, hat nur Zeit den Local-File-Header zu schreiben, kein File-Content, kein Central-Directory. Fix: statt req.on("close") nun res.on("close") + res.writableEnded- Check. Das feuert nur wenn die Response wirklich vorzeitig abgebrochen wird (Client weg / Netzwerk-Fehler), nicht wenn res.end() durch pipe sauber durchgereicht wurde. Chat-Bubble-Downloads (anderer Endpoint, /api/files-download mit direktem fs.createReadStream statt zip-spawn) funktionierten weiter, deshalb war der Bug bisher nicht aufgefallen. Co-Authored-By: Claude Opus 4.7 (1M context) --- diagnostic/server.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/diagnostic/server.js b/diagnostic/server.js index 7719b75..b51d2cd 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -1506,7 +1506,12 @@ const server = http.createServer((req, res) => { log("error", "server", `zip exit ${code}: ${stderr.slice(0, 200)}`); } }); - req.on("close", () => { if (!zip.killed) zip.kill("SIGTERM"); }); + // SIGTERM an zip nur wenn der Client wirklich disconnected + // (res.close vor res.end). req.on("close") feuert auch wenn + // der Request-Body durch ist — das wuerde zip vorzeitig killen. + res.on("close", () => { + if (!res.writableEnded && !zip.killed) zip.kill("SIGTERM"); + }); }); return; } else if (req.url === "/api/files-delete-batch" && req.method === "POST") {