Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix memory leak bug when re-using abortSignal (#2160) #2161

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 9 additions & 3 deletions source/core/index.ts
Expand Up @@ -251,9 +251,8 @@ export default class Request extends Duplex implements RequestEvents<Request> {
this.destroy(new AbortError(this));
}

this.options.signal?.addEventListener('abort', () => {
this.destroy(new AbortError(this));
});
this.abortHandler = this.abortHandler.bind(this);
this.options.signal?.addEventListener('abort', this.abortHandler, {once: true});

// Important! If you replace `body` in a handler with another stream, make sure it's readable first.
// The below is run only once.
Expand Down Expand Up @@ -545,6 +544,10 @@ export default class Request extends Duplex implements RequestEvents<Request> {
return this;
}

private abortHandler(): void {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
private abortHandler(): void {
private _abortHandler(): void {

this.destroy(new AbortError(this));
}

private async _finalizeBody(): Promise<void> {
const {options} = this;
const {headers} = options;
Expand Down Expand Up @@ -664,6 +667,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
response.once('end', () => {
this._responseSize = this._downloadedSize;
this.emit('downloadProgress', this.downloadProgress);
this.removeListener('abort', this.abortHandler);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be called on the signal instead

});

response.once('error', (error: Error) => {
Expand All @@ -674,6 +678,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
response.destroy();

this._beforeError(new ReadError(error, this));
this.removeListener('abort', this.abortHandler);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response is not guaranteed, but request is. However listening on error request event is also incorrect because of retries. I'd suggest to move this to _destroy.

});

response.once('aborted', () => {
Expand All @@ -684,6 +689,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
message: 'The server aborted pending request',
code: 'ECONNRESET',
}, this));
this.removeListener('abort', this.abortHandler);
});

this.emit('downloadProgress', this.downloadProgress);
Expand Down