From bd8c81e4f32d12f28a35d265f88b1716703687c6 Mon Sep 17 00:00:00 2001 From: DigitalBrainJS Date: Tue, 19 Sep 2023 16:43:41 +0300 Subject: [PATCH] Fix resource leak on destroy. --- index.js | 20 ++++++++++++++++---- test/test.js | 16 ++++++++-------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 3e199c1..057c6b1 100644 --- a/index.js +++ b/index.js @@ -38,6 +38,9 @@ var WriteAfterEndError = createErrorType( "write after end" ); +// istanbul ignore next +var destroy = Writable.prototype.destroy || noop; + // An HTTP(S) request that can be redirected function RedirectableRequest(options, responseCallback) { // Initialize the request @@ -68,10 +71,17 @@ function RedirectableRequest(options, responseCallback) { RedirectableRequest.prototype = Object.create(Writable.prototype); RedirectableRequest.prototype.abort = function () { - abortRequest(this._currentRequest); + destroyRequest(this._currentRequest); + this._currentRequest.abort(); this.emit("abort"); }; +RedirectableRequest.prototype.destroy = function (error) { + destroyRequest(this._currentRequest, error); + destroy.call(this, error); + return this; +}; + // Writes buffered data to the current native request RedirectableRequest.prototype.write = function (data, encoding, callback) { // Writing is not allowed if end has been called @@ -184,6 +194,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) { self.removeListener("abort", clearTimer); self.removeListener("error", clearTimer); self.removeListener("response", clearTimer); + self.removeListener("close", clearTimer); if (callback) { self.removeListener("timeout", callback); } @@ -210,6 +221,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) { this.on("abort", clearTimer); this.on("error", clearTimer); this.on("response", clearTimer); + this.on("close", clearTimer); return this; }; @@ -361,7 +373,7 @@ RedirectableRequest.prototype._processResponse = function (response) { } // The response is a redirect, so abort the current request - abortRequest(this._currentRequest); + destroyRequest(this._currentRequest); // Discard the remainder of the response to avoid waiting for data response.destroy(); @@ -590,12 +602,12 @@ function createErrorType(code, message, baseClass) { return CustomError; } -function abortRequest(request) { +function destroyRequest(request, error) { for (var event of events) { request.removeListener(event, eventHandlers[event]); } request.on("error", noop); - request.abort(); + request.destroy(error); } function isSubdomain(subdomain, domain) { diff --git a/test/test.js b/test/test.js index fd839ac..078926d 100644 --- a/test/test.js +++ b/test/test.js @@ -269,7 +269,7 @@ describe("follow-redirects", function () { req._currentRequest.emit("connect", "r", "s", "h"); })) .then(function (args) { - req.abort(); + req.destroy(); assert.equal(args.response, "r"); assert.equal(args.socket, "s"); assert.equal(args.head, "h"); @@ -429,7 +429,7 @@ describe("follow-redirects", function () { req.on("socket", function () { assert(req.socket instanceof net.Socket); req.setTimeout(3000, function () { - req.abort(); + req.destroy(); resolve(); }); }); @@ -466,7 +466,7 @@ describe("follow-redirects", function () { }); req.on("error", reject); req.setTimeout(1000, function () { - req.abort(); + req.destroy(); resolve(); }); })); @@ -485,7 +485,7 @@ describe("follow-redirects", function () { }); req.on("error", reject); req.setTimeout(2000, function () { - req.abort(); + req.destroy(); resolve(); }); })); @@ -505,7 +505,7 @@ describe("follow-redirects", function () { var callbacks = 0; function timeoutCallback() { if (++callbacks === 3) { - req.abort(); + req.destroy(); resolve(callbacks); } } @@ -633,7 +633,7 @@ describe("follow-redirects", function () { var req = http.request("http://localhost:3600/a"); req.setHeader("my-header", "my value"); assert.equal(req.getHeader("my-header"), "my value"); - req.abort(); + req.destroy(); }); it("should provide removeHeader", function () { @@ -976,7 +976,7 @@ describe("follow-redirects", function () { return; } finally { - req.abort(); + req.destroy(); } throw new Error("no error"); }); @@ -1218,7 +1218,7 @@ describe("follow-redirects", function () { catch (e) { error = e; } - req.abort(); + req.destroy(); assert(error instanceof Error); assert(error instanceof TypeError); assert.equal(error.message, "data should be a string, Buffer or Uint8Array");