From 21ef016b0ec20a94b4d0e22f679b17b4ffe370b9 Mon Sep 17 00:00:00 2001 From: themez Date: Thu, 26 Sep 2019 11:56:59 +0800 Subject: [PATCH 1/4] http: add reusedSocket property on client request Set ClientRequest.reusedSocket property when reusing socket for request, so user can handle retry base on wether the request is reusing a socket. Refs: https://github.com/request/request/issues/3131 --- lib/_http_agent.js | 1 + lib/_http_client.js | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/_http_agent.js b/lib/_http_agent.js index 25ff16fea186da..dcb5ed376de835 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -341,6 +341,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { Agent.prototype.reuseSocket = function reuseSocket(socket, req) { debug('have free socket'); + req.reusedSocket = true; socket.ref(); }; diff --git a/lib/_http_client.js b/lib/_http_client.js index c7c27f9ad598a5..d40480b7743d2a 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -195,6 +195,7 @@ function ClientRequest(input, options, cb) { this.upgradeOrConnect = false; this.parser = null; this.maxHeadersCount = null; + this.reusedSocket = false; var called = false; From f198794cd2fa331c56028799d816101780e6e549 Mon Sep 17 00:00:00 2001 From: themez Date: Tue, 1 Oct 2019 17:14:43 +0800 Subject: [PATCH 2/4] add tests and docs --- doc/api/http.md | 56 ++++++++++++++++++++++ test/parallel/test-http-agent-keepalive.js | 9 ++-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/doc/api/http.md b/doc/api/http.md index 5179252cb2329c..0b8abea3ef19ac 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -676,6 +676,62 @@ Removes a header that's already defined into headers object. request.removeHeader('Content-Type'); ``` +### request.reusedSocket + + + +* {boolean} Whether the request is send through a reused socket. + +When sending request through a keep-alive enabled agent, the underlying socket +might be reused. But if server closes connection at unfortunate time, client +may run into a 'ECONNRESET' error. + +```js +const http = require('http') + +// Server has a 5 seconds keep-alive timeout by default +http + .createServer((req, res) => { + res.write('hello\n'); + res.end(); + }) + .listen(3000); + +setInterval(() => { + // Adapting a keep-alive agent + http.get('http://localhost:3000', { agent }, (res) => { + res.on('data', data => { + // Do nothing + }); + }) +}, 5000); // Sending request on 5s interval so it's easy to hit idle timeout +``` + +By marking a request whether it reused socket or not, we can do +automatic error retry base on it. + +```js +const http = require('http'); +const agent = new http.Agent({ keepAlive: true }); + +function retriableRequest() { + const req = http + .get('http://localhost:3000', { agent }, (res) => { + // ... + }) + .on('error', (err) => { + // Check if retry is needed + if (req.reusedSocket && err.code === 'ECONNRESET') { + retriableRequest(); + } + }); +} + +retriableRequest(); +``` + ### request.setHeader(name, value) * {boolean} Whether the request is send through a reused socket. -When sending request through a keep-alive enabled agent, the underlying socket +When sending request through a keep-alive enabled agent, the underlying socket might be reused. But if server closes connection at unfortunate time, client may run into a 'ECONNRESET' error. @@ -709,7 +709,7 @@ setInterval(() => { }, 5000); // Sending request on 5s interval so it's easy to hit idle timeout ``` -By marking a request whether it reused socket or not, we can do +By marking a request whether it reused socket or not, we can do automatic error retry base on it. ```js