Skip to content

Commit fc29cf9

Browse files
themeztargos
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 e6397aa commit fc29cf9

4 files changed

Lines changed: 64 additions & 3 deletions

File tree

‎doc/api/http.md‎

Lines changed: 56 additions & 0 deletions
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+
consthttp=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+
consthttp=require('http');
714+
constagent=newhttp.Agent({ keepAlive:true });
715+
716+
functionretriableRequest() {
717+
constreq= 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‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
340340

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

‎lib/_http_client.js‎

Lines changed: 1 addition & 0 deletions
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
letcalled=false;
200201

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ function checkDataAndSockets(body) {
6363

6464
functionsecond(){
6565
// Request second, use the same socket
66-
get('/second',common.mustCall((res)=>{
66+
constreq=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
functionremoteClose(){
8283
// Mock remote server close the socket
83-
get('/remote_close',common.mustCall((res)=>{
84+
constreq=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+
constreq=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)