Skip to content

Commit 8915b15

Browse files
themezstarkwang
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 075c7eb commit 8915b15

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
@@ -676,6 +676,62 @@ Removes a header that's already defined into headers object.
676676
request.removeHeader('Content-Type');
677677
```
678678

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

lib/_http_agent.js

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

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

lib/_http_client.js

+1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ function ClientRequest(input, options, cb) {
195195
this.upgradeOrConnect = false;
196196
this.parser = null;
197197
this.maxHeadersCount = null;
198+
this.reusedSocket = false;
198199

199200
var called = false;
200201

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)