fix v2 release CI failures
This commit is contained in:
1 parent
4ac9d21bc1
commit
72caa31b57
5 files changed
+278
-218
No files matched your search
@@ -0,0 +1,59 @@
|
||||
import type { createPinnedAgent } from "./network-policy";
|
||||
|
||||
const DISPATCHER_CLOSE_GRACE_MS = 1_000;
|
||||
|
||||
type CustomWidgetDispatcher = ReturnType<typeof createPinnedAgent>;
|
||||
|
||||
export async function closeDispatcher(
|
||||
dispatcher: CustomWidgetDispatcher,
|
||||
deadlineSignal: AbortSignal,
|
||||
): Promise<boolean> {
|
||||
const cleanupController = new AbortController();
|
||||
const timeout = setTimeout(() => cleanupController.abort(), DISPATCHER_CLOSE_GRACE_MS);
|
||||
try {
|
||||
await abortable(dispatcher.close(), AbortSignal.any([deadlineSignal, cleanupController.signal]));
|
||||
return true;
|
||||
} catch (error) {
|
||||
detachDispatcherDestroy(dispatcher);
|
||||
if (cleanupController.signal.aborted && !deadlineSignal.aborted) return false;
|
||||
throw error;
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
function detachDispatcherDestroy(dispatcher: CustomWidgetDispatcher): void {
|
||||
try {
|
||||
void dispatcher.destroy().catch(() => undefined);
|
||||
} catch {
|
||||
// Cleanup is best effort after its bounded grace period.
|
||||
}
|
||||
}
|
||||
|
||||
function abortable<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {
|
||||
if (signal.aborted) return Promise.reject(signal.reason ?? createAbortError());
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const onAbort = () => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
reject(signal.reason ?? createAbortError());
|
||||
};
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
operation.then(
|
||||
(value) => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
resolve(value);
|
||||
},
|
||||
(error: unknown) => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
reject(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function createAbortError(): Error {
|
||||
const error = new Error("Operation aborted");
|
||||
error.name = "AbortError";
|
||||
return error;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
resolveSameOriginTarget,
|
||||
validateCustomWidgetUrl,
|
||||
} from "./network-policy";
|
||||
import { closeDispatcher } from "./request-dispatcher-lifecycle";
|
||||
import { parseResponseBody } from "./response";
|
||||
|
||||
export {
|
||||
@@ -30,7 +31,6 @@ export {
|
||||
|
||||
export const MAX_REQUEST_BODY_BYTES = 10 * 1024;
|
||||
const REQUEST_TIMEOUT_MS = 10_000;
|
||||
const DISPATCHER_CLOSE_GRACE_MS = 1_000;
|
||||
const MAX_QUERY_REDIRECTS = 3;
|
||||
export const MAX_REQUEST_DURATION_MS = 45_000;
|
||||
const TIMEOUT_ERROR_CODES = new Set(["UND_ERR_CONNECT_TIMEOUT", "UND_ERR_HEADERS_TIMEOUT", "UND_ERR_BODY_TIMEOUT"]);
|
||||
@@ -213,60 +213,6 @@ async function performRequestWithinDeadline(
|
||||
}
|
||||
}
|
||||
|
||||
async function closeDispatcher(
|
||||
dispatcher: ReturnType<typeof createPinnedAgent>,
|
||||
deadlineSignal: AbortSignal,
|
||||
): Promise<boolean> {
|
||||
const cleanupController = new AbortController();
|
||||
const timeout = setTimeout(() => cleanupController.abort(), DISPATCHER_CLOSE_GRACE_MS);
|
||||
try {
|
||||
await abortable(dispatcher.close(), AbortSignal.any([deadlineSignal, cleanupController.signal]));
|
||||
return true;
|
||||
} catch (error) {
|
||||
detachDispatcherDestroy(dispatcher);
|
||||
if (cleanupController.signal.aborted && !deadlineSignal.aborted) return false;
|
||||
throw error;
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
function detachDispatcherDestroy(dispatcher: ReturnType<typeof createPinnedAgent>): void {
|
||||
try {
|
||||
void dispatcher.destroy().catch(() => undefined);
|
||||
} catch {
|
||||
// Cleanup is best effort after its bounded grace period.
|
||||
}
|
||||
}
|
||||
|
||||
function abortable<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {
|
||||
if (signal.aborted) return Promise.reject(signal.reason ?? createAbortError());
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const onAbort = () => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
reject(signal.reason ?? createAbortError());
|
||||
};
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
operation.then(
|
||||
(value) => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
resolve(value);
|
||||
},
|
||||
(error: unknown) => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
reject(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function createAbortError(): Error {
|
||||
const error = new Error("Operation aborted");
|
||||
error.name = "AbortError";
|
||||
return error;
|
||||
}
|
||||
|
||||
export function isCustomWidgetRequestTimeoutError(error: unknown, ...signals: readonly AbortSignal[]): boolean {
|
||||
if (signals.some((signal) => signal.aborted)) return true;
|
||||
if (!(error instanceof Error)) return false;
|
||||
|
||||
Reference in new issue
Block a user