timeout direct upload control requests

This commit is contained in:
134 2026-07-08 21:08:02 +08:00
parent 2b875847dd
commit 81a0b29633

View File

@ -7,6 +7,8 @@ const SERVER_UPLOAD_MAX_BYTES = 50 * 1024 * 1024;
const DIRECT_UPLOAD_PART_CONCURRENCY = 4;
const DIRECT_UPLOAD_SUPPRESS_MS = 2 * 60 * 1000;
const CLIENT_UPLOAD_ID_FIELD = "clientUploadId";
const DIRECT_UPLOAD_ACTION_TIMEOUT_MS = 15 * 1000;
const DIRECT_UPLOAD_ABORT_TIMEOUT_MS = 8 * 1000;
const XHR_UPLOAD_TOTAL_TIMEOUT_MS = 6 * 60 * 1000;
const XHR_UPLOAD_STALL_TIMEOUT_MS = 30 * 1000;
@ -90,6 +92,12 @@ function createAbortError() {
return error;
}
function createTimeoutError(message: string) {
const error = new Error(message);
error.name = "TimeoutError";
return error;
}
function sleep(ms: number, signal?: AbortSignal) {
if (signal?.aborted) {
return Promise.reject(createAbortError());
@ -314,18 +322,75 @@ async function readErrorPayload(response: Response) {
}
}
async function fetchWithControlTimeout(
input: RequestInfo | URL,
init: RequestInit,
timeoutMs: number,
signal?: AbortSignal
) {
if (signal?.aborted) {
throw createAbortError();
}
const controller = new AbortController();
let timeout: ReturnType<typeof setTimeout> | null = null;
let timedOut = false;
const onAbort = () => {
controller.abort();
};
signal?.addEventListener("abort", onAbort, { once: true });
try {
timeout = setTimeout(() => {
timedOut = true;
controller.abort();
}, timeoutMs);
return await fetch(input, {
...init,
signal: controller.signal,
});
} catch (error) {
if (signal?.aborted) {
throw createAbortError();
}
if (timedOut) {
throw createTimeoutError(
`Direct upload control request timed out after ${Math.round(
timeoutMs / 1000
)}s`
);
}
throw error;
} finally {
if (timeout) {
clearTimeout(timeout);
}
signal?.removeEventListener("abort", onAbort);
}
}
async function postDirectUploadAction<T>(
payload: Record<string, unknown>,
signal?: AbortSignal
signal?: AbortSignal,
timeoutMs = DIRECT_UPLOAD_ACTION_TIMEOUT_MS
): Promise<T> {
const response = await fetch("/api/files/direct-upload", {
method: "POST",
headers: {
"Content-Type": "application/json",
const response = await fetchWithControlTimeout(
"/api/files/direct-upload",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
},
body: JSON.stringify(payload),
signal,
});
timeoutMs,
signal
);
if (!response.ok) {
throw new Error(String(await readErrorPayload(response)));
@ -745,7 +810,8 @@ async function fetchDirectUpload(
uploadId: multipartUpload.uploadId,
clientUploadId: file.clientUploadId,
},
signal
signal,
DIRECT_UPLOAD_ABORT_TIMEOUT_MS
).catch(() => {});
}