fix: avoid storage probe redirect timeouts
This commit is contained in:
@@ -18,14 +18,33 @@ export function getMigrationStorageUrlConcurrency(env = process.env) {
|
||||
export async function checkStorageUrl(baseUrl, storageUrl, options = {}) {
|
||||
const timeoutMs = Number(options.timeoutMs || 10_000);
|
||||
const fetchImpl = options.fetchImpl || fetch;
|
||||
const targetUrl = `${baseUrl}${storageUrl}`;
|
||||
|
||||
try {
|
||||
const response = await fetchImpl(`${baseUrl}${storageUrl}`, {
|
||||
const response = await fetchImpl(targetUrl, {
|
||||
method: 'HEAD',
|
||||
redirect: 'manual',
|
||||
signal: AbortSignal.timeout(timeoutMs),
|
||||
});
|
||||
await response.body?.cancel?.();
|
||||
if (!response.ok) {
|
||||
|
||||
if (isReachableStorageResponse(response)) {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
if (response.status !== 405) {
|
||||
return { ok: false, error: `HTTP ${response.status}` };
|
||||
}
|
||||
|
||||
const fallback = await fetchImpl(targetUrl, {
|
||||
redirect: 'manual',
|
||||
signal: AbortSignal.timeout(timeoutMs),
|
||||
});
|
||||
await fallback.body?.cancel?.();
|
||||
if (!isReachableStorageResponse(fallback)) {
|
||||
return { ok: false, error: `HTTP ${fallback.status}` };
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
} catch (error) {
|
||||
return {
|
||||
@@ -34,3 +53,10 @@ export async function checkStorageUrl(baseUrl, storageUrl, options = {}) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function isReachableStorageResponse(response) {
|
||||
if (response.ok) return true;
|
||||
return response.status >= 300
|
||||
&& response.status < 400
|
||||
&& Boolean(response.headers?.get?.('location'));
|
||||
}
|
||||
|
||||
@@ -71,6 +71,24 @@ await runTest('migration storage URL check records fetch failures instead of thr
|
||||
});
|
||||
});
|
||||
|
||||
await runTest('migration storage URL check treats local-storage redirects as reachable', async () => {
|
||||
const calls = [];
|
||||
const result = await checkStorageUrl('http://127.0.0.1:8000', '/api/local-storage/gallery/images/work.png', {
|
||||
timeoutMs: 10,
|
||||
fetchImpl: async (_url, init) => {
|
||||
calls.push(init);
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: { Location: 'https://object-storage.example/work.png' },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(result, { ok: true });
|
||||
assert.equal(calls[0].method, 'HEAD');
|
||||
assert.equal(calls[0].redirect, 'manual');
|
||||
});
|
||||
|
||||
await runTest('migration integrity script uses resilient storage URL helpers', () => {
|
||||
const source = read('scripts/migration-integrity-check.mjs');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user