Skip to content

Commit 1330b03

Browse files
authored
fix: safe cancel upload stream (#126)
1 parent f8b7164 commit 1330b03

File tree

3 files changed

+21
-4
lines changed

3 files changed

+21
-4
lines changed

src/common/cancel.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { ReadableStream } from 'node:stream/web';
2+
3+
// A stream cannot be closed if there's an active reader.
4+
// Try and cancel the stream, and ignore the error that's thrown if it's locked.
5+
// This is exactly what the official node.js fetch implementation does.
6+
// https://github.com/nodejs/node/issues/42411#issuecomment-1073284684
7+
// https://github.com/nodejs/node/blob/15b39026197437493af224f0fe69aa878ec1abd3/deps/undici/src/lib/web/fetch/index.js#L331-L336
8+
export async function safeCancel(stream: ReadableStream): Promise<void> {
9+
return stream.cancel().catch((err) => {
10+
if (err.code === 'ERR_INVALID_STATE') {
11+
// Node bug?
12+
return;
13+
}
14+
throw err;
15+
});
16+
}

src/symbols/symbols-api-client/symbols-api-client.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ function createFakeStream(value: any, done: boolean, releaseLock?: jasmine.Spy)
141141
getReader: () => ({
142142
read: () => Promise.resolve({ value, done }),
143143
releaseLock: releaseLock ?? jasmine.createSpy('releaseLock'),
144-
cancel: jasmine.createSpy('reader-cancel')
144+
cancel: jasmine.createSpy('reader-cancel').and.resolveTo()
145145
}),
146-
cancel: jasmine.createSpy('stream-cancel')
146+
cancel: jasmine.createSpy('stream-cancel').and.resolveTo()
147147
};
148148
}
149149

src/symbols/symbols-api-client/symbols-api-client.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ApiClient, BugSplatResponse, GZippedSymbolFile, S3ApiClient } from '@common';
22
import { delay } from '../../common/delay';
3+
import { safeCancel } from 'src/common/cancel';
34

45
export class SymbolsApiClient {
56
private readonly uploadUrl = '/symsrv/uploadUrl';
@@ -38,7 +39,7 @@ export class SymbolsApiClient {
3839
// Release the lock, so we can cancel the stream.
3940
// See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/tee and https://github.com/whatwg/streams/issues/1033#issuecomment-601471668
4041
reader.releaseLock();
41-
checkStream.cancel();
42+
safeCancel(checkStream);
4243
}
4344

4445
let uploadResponse: globalThis.Response;
@@ -67,7 +68,7 @@ export class SymbolsApiClient {
6768
// Unfortunately, the original stream gets locked when we tee it, so we can't cancel it directly.
6869
// When both teed streams are cancelled, the original stream _should_ also be cancelled.
6970
// There's not a lot of documentation on this, so we might be mistaken.
70-
uploadStream.cancel();
71+
safeCancel(uploadStream);
7172
}
7273

7374
await this._timer(1000);

0 commit comments

Comments
 (0)