fix upload interruption

AuthorKonata <konata@posteo.jp>
Date
Commit263bf8fd29de7b2b6ddc430dd10628edf71f7321
Parent31ca3d1
1 file changed, 19 insertions(+)
▾Msrc/index.ts
@@ -100,6 +100,23 @@ class UploadError extends Error {
}
}
// Read and discard the rest of a request body. When /upload rejects a request
// before it parses the body (cooldown, bad content type), the client is still
// streaming the file up; returning immediately cancels that in-flight stream and
// the client sees a reset (NET_INTERRUPTED / H3_REQUEST_CANCELLED over HTTP/2/3)
// instead of our response. Draining lets the request finish so the error gets
// delivered. Best-effort: a client that hangs up mid-drain just errors here.
async function drainBody(request: Request): Promise<void> {
if (!request.body) return;
const reader = request.body.getReader();
try {
while (!(await reader.read()).done) {}
} catch {
} finally {
reader.releaseLock();
}
}
async function pump(
stream: ReadableStream<Uint8Array>,
onChunk: (chunk: Uint8Array) => void | Promise<void>,
@@ -223,6 +240,7 @@ const app = new Elysia({
if (config.uploadCooldownSeconds > 0) {
const last = lastUpload.get(ip) ?? 0;
if (now - last < config.uploadCooldownSeconds * 1000) {
await drainBody(request);
set.status = 429; // Too Many Requests
return "Upload cooldown active, please wait before uploading again";
}
@@ -230,6 +248,7 @@ const app = new Elysia({
const contentType = request.headers.get("content-type") ?? "";
if (!contentType.includes("multipart/form-data") || !request.body) {
await drainBody(request);
set.status = 400;
return "Expected a multipart/form-data upload";
}