From a2dc03b22a9eb1e80b63d5ad351b2442c03cbde6 Mon Sep 17 00:00:00 2001 From: ayip8 <16192761+ayip8@users.noreply.github.com> Date: Thu, 22 May 2025 09:27:13 -0400 Subject: [PATCH] Guard against potential race conditions with abort controllers --- src/loader.ts | 15 ++++++++++++--- src/telemetryUploader.ts | 8 ++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/loader.ts b/src/loader.ts index 6dfadf1..7eb5381 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -1,4 +1,4 @@ -import { headers, DEFAULT_TIMEOUT } from "./apiHelpers"; +import { DEFAULT_TIMEOUT, headers } from "./apiHelpers"; import { EvaluationPayload } from "./config"; import Context from "./context"; @@ -44,6 +44,8 @@ export default class Loader { abortController: AbortController | undefined; + isAborted: boolean = false; + constructor({ apiKey, context, @@ -74,6 +76,7 @@ export default class Loader { ) { this.abortController = new AbortController() as AbortController; const { signal } = this.abortController; + this.isAborted = false; const endpoint = this.endpoints[index]; const url = this.url(endpoint); @@ -109,12 +112,18 @@ export default class Loader { index < this.endpoints.length - 1 ? Math.min(this.timeout, EARLY_TIMEOUT) : this.timeout; this.abortTimeoutId = setTimeout(() => { - this.abortController?.abort(); + if (!this.isAborted) { + this.isAborted = true; + this.abortController?.abort(); + } }, timeout); } load() { - this.abortController?.abort(); + if (!this.isAborted) { + this.isAborted = true; + this.abortController?.abort(); + } const options = { headers: headers(this.apiKey, this.clientVersion), diff --git a/src/telemetryUploader.ts b/src/telemetryUploader.ts index 4779fa6..0cdf3fd 100644 --- a/src/telemetryUploader.ts +++ b/src/telemetryUploader.ts @@ -1,4 +1,4 @@ -import { headers, DEFAULT_TIMEOUT } from "./apiHelpers"; +import { DEFAULT_TIMEOUT, headers } from "./apiHelpers"; export type TelemetryUploaderParams = { apiKey: string; @@ -41,6 +41,7 @@ export default class TelemetryUploader { postToEndpoint(options: object, resolve: (value: any) => void, reject: (value: any) => void) { const controller = new AbortController() as AbortController; const signal = controller?.signal; + let isAborted = false; const url = TelemetryUploader.postUrl(this.apiEndpoint); @@ -81,7 +82,10 @@ export default class TelemetryUploader { }); this.abortTimeoutId = setTimeout(() => { - controller.abort(); + if (!isAborted) { + isAborted = true; + controller.abort(); + } }, this.timeout); }