Skip to content

Commit b9ffca1

Browse files
themezBethGriggs
authored andcommitted
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: request/request#3131 PR-URL: #29715 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Weijia Wang <starkwang@126.com>
1 parent 7fc6984 commit b9ffca1

File tree

4 files changed

+64
-3
lines changed

4 files changed

+64
-3
lines changed

doc/api/http.md

+56
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,62 @@ Removes a header that's already defined into headers object.
673673
request.removeHeader('Content-Type');
674674
```
675675

676+
### `request.reusedSocket`
677+
678+
<!-- YAML
679+
added: REPLACEME
680+
-->
681+
682+
* {boolean} Whether the request is send through a reused socket.
683+
684+
When sending request through a keep-alive enabled agent, the underlying socket
685+
might be reused. But if server closes connection at unfortunate time, client
686+
may run into a 'ECONNRESET' error.
687+
688+
```js
689+
const http = require('http');
690+
691+
// Server has a 5 seconds keep-alive timeout by default
692+
http
693+
.createServer((req, res) => {
694+
res.write('hello\n');
695+
res.end();
696+
})
697+
.listen(3000);
698+
699+
setInterval(() => {
700+
// Adapting a keep-alive agent
701+
http.get('http://localhost:3000', { agent }, (res) => {
702+
res.on('data', (data) => {
703+
// Do nothing
704+
});
705+
});
706+
}, 5000); // Sending request on 5s interval so it's easy to hit idle timeout
707+
```
708+
709+
By marking a request whether it reused socket or not, we can do
710+
automatic error retry base on it.
711+
712+
```js
713+
const http = require('http');
714+
const agent = new http.Agent({ keepAlive: true });
715+
716+
function retriableRequest() {
717+
const req = http
718+
.get('http://localhost:3000', { agent }, (res) => {
719+
// ...
720+
})
721+
.on('error', (err) => {
722+
// Check if retry is needed
723+
if (req.reusedSocket && err.code === 'ECONNRESET') {
724+
retriableRequest();
725+
}
726+
});
727+
}
728+
729+
retriableRequest();
730+
```
731+
676732
### `request.setHeader(name, value)`
677733
<!-- YAML
678734
added: v1.6.0

lib/_http_agent.js

+1
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
340340

341341
Agent.prototype.reuseSocket = function reuseSocket(socket, req) {
342342
debug('have free socket');
343+
req.reusedSocket = true;
343344
socket.ref();
344345
};
345346

lib/_http_client.js

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ function ClientRequest(input, options, cb) {
204204
this.upgradeOrConnect = false;
205205
this.parser = null;
206206
this.maxHeadersCount = null;
207+
this.reusedSocket = false;
207208

208209
let called = false;
209210

test/parallel/test-http-agent-keepalive.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ function checkDataAndSockets(body) {
6363

6464
function second() {
6565
// Request second, use the same socket
66-
get('/second', common.mustCall((res) => {
66+
const req = get('/second', common.mustCall((res) => {
67+
assert.strictEqual(req.reusedSocket, true);
6768
assert.strictEqual(res.statusCode, 200);
6869
res.on('data', checkDataAndSockets);
6970
res.on('end', common.mustCall(() => {
@@ -80,7 +81,8 @@ function second() {
8081

8182
function remoteClose() {
8283
// Mock remote server close the socket
83-
get('/remote_close', common.mustCall((res) => {
84+
const req = get('/remote_close', common.mustCall((res) => {
85+
assert.deepStrictEqual(req.reusedSocket, true);
8486
assert.deepStrictEqual(res.statusCode, 200);
8587
res.on('data', checkDataAndSockets);
8688
res.on('end', common.mustCall(() => {
@@ -120,7 +122,8 @@ function remoteError() {
120122
server.listen(0, common.mustCall(() => {
121123
name = `localhost:${server.address().port}:`;
122124
// Request first, and keep alive
123-
get('/first', common.mustCall((res) => {
125+
const req = get('/first', common.mustCall((res) => {
126+
assert.strictEqual(req.reusedSocket, false);
124127
assert.strictEqual(res.statusCode, 200);
125128
res.on('data', checkDataAndSockets);
126129
res.on('end', common.mustCall(() => {

0 commit comments

Comments
 (0)