From 2313eb8befac8612ff105bdc1ec5dcb91fda23b4 Mon Sep 17 00:00:00 2001 From: Weijia Wang Date: Sat, 12 Oct 2019 16:19:28 +0800 Subject: [PATCH] Add `reusedSocket` property on client request --- README.md | 24 ++++++++++++++++++++++++ lib/agent.js | 2 ++ test/agent.test.js | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/README.md b/README.md index 553ea94..a8cb6aa 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,30 @@ setTimeout(() => { }, 2000); ``` +### Support `req.reusedSocket` + +This agent implements the `req.reusedSocket` to determine whether a request is send through a reused socket. + +When server closes connection at unfortunate time ([keep-alive race](https://code-examples.net/en/q/28a8069)), the http client will throw a `ECONNRESET` error. Under this circumstance, `req.reusedSocket` is useful when we want to retry the request automatically. + +```js +const http = require('http'); +const Agent = require('agentkeepalive'); +const agent = new Agent(); + +const req = http + .get('http://localhost:3000', { agent }, (res) => { + // ... + }) + .on('error', (err) => { + if (req.reusedSocket && err.code === 'ECONNRESET') { + // retry the request or anything else... + } + }) +``` + +This behavior is consistent with Node.js core. But through `agentkeepalive`, you can use this feature in older Node.js version. + ## [Benchmark](https://github.com/node-modules/agentkeepalive/tree/master/benchmark) run the benchmark: diff --git a/lib/agent.js b/lib/agent.js index 1db283e..0a313ce 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -140,6 +140,8 @@ class Agent extends OriginalAgent { // reuseSocket(socket, req) super.reuseSocket(...args); const socket = args[0]; + const req = args[1]; + req.reusedSocket = true; const agentTimeout = this.options.timeout; if (getSocketTimeout(socket) !== agentTimeout) { // reset timeout before use diff --git a/test/agent.test.js b/test/agent.test.js index 6a48cba..30416af 100644 --- a/test/agent.test.js +++ b/test/agent.test.js @@ -1495,6 +1495,43 @@ describe('test/agent.test.js', () => { assert(Object.keys(agentkeepalive.sockets).length === 1); }); + it('should set req.reusedSocket to true when reuse socket', done => { + const agent = new Agent({ + keepAlive: true, + }); + + // First request + const req1 = http.get({ + port, + path: '/', + agent, + }, res => { + assert(res.statusCode === 200); + res.on('data', () => {}); + res.on('end', () => { + setTimeout(() => { + // Second request + const req2 = http.get({ + port, + path: '/', + agent, + }, res => { + assert(res.statusCode === 200); + res.on('data', () => {}); + res.on('end', () => { + done(); + }); + }); + // Second request reuses the socket + assert(req2.reusedSocket); + }, 10); + }); + }); + + // First request doesn't reuse the socket + assert(!req1.reusedSocket); + }); + describe('request timeout > agent timeout', () => { it('should use request timeout', done => { const agent = new Agent({