fix(uploads): drain a refused video body before answering (#606 merge-time fix)

The ?kind=video branch called req.file(), which pipes the request into
busboy, then answered 400 (unsupported container) or 403 (upload dir not a
regular directory) without reading part.file. The server then stopped
reading the body, and a browser fetch, which reads the response only after
its upload is sent, hung on "Uploading 1/1 file" for an Android 3gp or an
.mkv. Both early answers now consume the part first, as the mislabeled
container path already did. (Reviewer-verified in Chromium: hang before,
400 in about 0.2 s after; inject and a Node client do not reproduce the hang.)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-10-13 18:25:07 +02:00
1 parent 6c7d63d45e
commit eaa9044520
1 file changed
+3
+3
View File
@@ -11,6 +11,7 @@ import { homedir } from 'node:os';
import { existsSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
import { execFile } from 'node:child_process';
import fs from 'node:fs/promises';
import { finished } from 'node:stream/promises';
import { randomBytes, randomUUID } from 'node:crypto';
import { performance } from 'node:perf_hooks';
import {
@@ -5482,6 +5483,7 @@ export function registerSessionRoutes(
const videoMime = (part.mimetype || '').toLowerCase().match(/^video\/(mp4|quicktime|x-m4v|webm|ogg)$/);
if (videoMime) ext = VIDEO_MIME_EXT[videoMime[1]] ?? ext;
if (!ext) {
await finished(part.file.resume()).catch(() => {});
reply.code(400);
return createErrorResponse(
ApiErrorCode.INVALID_INPUT,
@@ -5490,6 +5492,7 @@ export function registerSessionRoutes(
}
const imageDir = await ensureUploadDir(session.workingDir);
if (!imageDir) {
await finished(part.file.resume()).catch(() => {});
reply.code(403);
return createErrorResponse(ApiErrorCode.INVALID_INPUT, `${UPLOADS_DIR} is not a regular directory`);
}