Skip to content

Commit 63dc86e

Browse files
authored
sdk-core, browser, react-native: fix abort event not being removed from signal (#265)
* session-replay: add prepublish script * sdk-core: fix 'abort' event not being removed from signal * react-native: dispose signal in ReactNativeRequestHandler * browser: dispose signal in ReactNativeRequestHandler --------- Co-authored-by: Sebastian Alex <[email protected]>
1 parent 2b3f040 commit 63dc86e

File tree

6 files changed

+64
-33
lines changed

6 files changed

+64
-33
lines changed

packages/browser/src/BacktraceBrowserRequestHandler.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ export class BacktraceBrowserRequestHandler implements BacktraceRequestHandler {
4444
): Promise<BacktraceReportSubmissionResult<T>> {
4545
const controller = new AbortController();
4646
const id = setTimeout(() => controller.abort(), this._timeout);
47+
const signal = anySignal(abortSignal, controller.signal);
4748

4849
try {
4950
const response = await fetch(submissionUrl, {
5051
method: 'POST',
5152
body: payload,
5253
headers: typeof payload === 'string' ? this.JSON_HEADERS : this.MULTIPART_HEADERS,
53-
signal: anySignal(abortSignal, controller.signal),
54+
signal,
5455
});
5556

5657
clearInterval(id);
@@ -84,6 +85,11 @@ export class BacktraceBrowserRequestHandler implements BacktraceRequestHandler {
8485
}
8586

8687
return BacktraceReportSubmissionResult.OnUnknownError(err.message);
88+
} finally {
89+
// Check for backwards compatibility
90+
if ('dispose' in signal && typeof signal.dispose === 'function') {
91+
signal.dispose();
92+
}
8793
}
8894
}
8995

packages/react-native/src/ReactNativeRequestHandler.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,14 @@ export class ReactNativeRequestHandler implements BacktraceRequestHandler {
4545
const controller = new AbortController();
4646
const id = setTimeout(() => controller.abort(), this._timeout);
4747

48+
const signal = anySignal(abortSignal, controller.signal);
49+
4850
try {
4951
const response = await fetch(submissionUrl, {
5052
method: 'POST',
5153
body: payload,
5254
headers: typeof payload === 'string' ? this.JSON_HEADERS : this.MULTIPART_HEADERS,
53-
signal: anySignal(abortSignal, controller.signal),
55+
signal,
5456
});
5557

5658
clearInterval(id);
@@ -84,6 +86,11 @@ export class ReactNativeRequestHandler implements BacktraceRequestHandler {
8486
}
8587

8688
return BacktraceReportSubmissionResult.OnUnknownError(err.message);
89+
} finally {
90+
// Check for backwards compatibility
91+
if ('dispose' in signal && typeof signal.dispose === 'function') {
92+
signal.dispose();
93+
}
8794
}
8895
}
8996

packages/sdk-core/src/common/AbortController.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,14 @@ export function createAbortController(): OriginalAbortController {
202202
}
203203
}
204204

205-
export function anySignal(...signals: (OriginalAbortSignal | undefined)[]): OriginalAbortSignal {
206-
const controller = createAbortController();
205+
interface DisposableAbortSignal extends OriginalAbortSignal {
206+
dispose(): void;
207+
}
207208

208-
function onAbort() {
209-
controller.abort();
209+
export function anySignal(...signals: (OriginalAbortSignal | undefined)[]): DisposableAbortSignal {
210+
const controller = createAbortController();
210211

212+
function cleanup() {
211213
// Cleanup
212214
for (const signal of signals) {
213215
if (signal) {
@@ -216,6 +218,11 @@ export function anySignal(...signals: (OriginalAbortSignal | undefined)[]): Orig
216218
}
217219
}
218220

221+
function onAbort() {
222+
controller.abort();
223+
cleanup();
224+
}
225+
219226
for (const signal of signals) {
220227
if (!signal) {
221228
continue;
@@ -228,5 +235,6 @@ export function anySignal(...signals: (OriginalAbortSignal | undefined)[]): Orig
228235
signal.addEventListener('abort', onAbort);
229236
}
230237

231-
return controller.signal;
238+
(controller.signal as DisposableAbortSignal).dispose = cleanup;
239+
return controller.signal as DisposableAbortSignal;
232240
}

packages/sdk-core/src/modules/database/BacktraceDatabase.ts

+29-25
Original file line numberDiff line numberDiff line change
@@ -276,34 +276,38 @@ export class BacktraceDatabase implements BacktraceModule {
276276
const records = [...this._databaseRecordContext.getBucket(bucketIndex)];
277277
const signal = anySignal(abortSignal, this._abortController.signal);
278278

279-
for (const record of records) {
280-
if (!this.enabled) {
281-
return;
282-
}
283-
if (record.locked) {
284-
continue;
285-
}
286-
try {
287-
record.locked = true;
288-
289-
const result =
290-
record.type === 'report'
291-
? await this._requestHandler.send(record.data, record.attachments, signal)
292-
: await this._requestHandler.sendAttachment(record.rxid, record.attachment, signal);
293-
294-
if (
295-
result.status === 'Ok' ||
296-
result.status === 'Unsupported' ||
297-
result.status === 'Report skipped'
298-
) {
299-
this.remove(record);
279+
try {
280+
for (const record of records) {
281+
if (!this.enabled) {
282+
return;
283+
}
284+
if (record.locked) {
300285
continue;
301286
}
302-
this._databaseRecordContext.increaseBucket(bucketIndex);
303-
return;
304-
} finally {
305-
record.locked = false;
287+
try {
288+
record.locked = true;
289+
290+
const result =
291+
record.type === 'report'
292+
? await this._requestHandler.send(record.data, record.attachments, signal)
293+
: await this._requestHandler.sendAttachment(record.rxid, record.attachment, signal);
294+
295+
if (
296+
result.status === 'Ok' ||
297+
result.status === 'Unsupported' ||
298+
result.status === 'Report skipped'
299+
) {
300+
this.remove(record);
301+
continue;
302+
}
303+
this._databaseRecordContext.increaseBucket(bucketIndex);
304+
return;
305+
} finally {
306+
record.locked = false;
307+
}
306308
}
309+
} finally {
310+
signal.dispose();
307311
}
308312
}
309313
}

packages/sdk-core/src/modules/metrics/MetricsSubmissionQueue.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ export class MetricsSubmissionQueue<T extends MetricsEvent> implements MetricsQu
4242

4343
public async send(abortSignal?: AbortSignal) {
4444
const eventsToProcess = this._events.splice(0);
45-
return await this.submit(eventsToProcess, anySignal(abortSignal, this._abortController.signal));
45+
const signal = anySignal(abortSignal, this._abortController.signal);
46+
try {
47+
return await this.submit(eventsToProcess, signal);
48+
} finally {
49+
signal.dispose();
50+
}
4651
}
4752

4853
public dispose() {

packages/session-replay/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"format": "prettier --write '**/*.ts'",
1414
"lint": "eslint . --ext .ts",
1515
"watch": "npm run build -- --watch",
16+
"prepublishOnly": "cross-env NODE_ENV=production npm run clean && npm run build",
1617
"test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" NODE_NO_WARNINGS=1 NODE_ENV=test jest"
1718
},
1819
"repository": {

0 commit comments

Comments
 (0)