GeezerVison is showing github.com — http: add reusedSocket property on client request · nodejs/node@fc29cf9
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/http.md +56Lines changed: 56 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -673,6 +673,62 @@ Removes a header that's already defined into headers object. | 673 | 673 | request.removeHeader('Content-Type'); | 674 | 674 | ``` | 675 | 675 |
| | 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 | + | 676 | 732 | ### `request.setHeader(name, value)` | 677 | 733 | <!-- YAML | 678 | 734 | added: v1.6.0 | |
Collapse file Copy file name to clipboardExpand all lines: lib/_http_agent.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -340,6 +340,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { | 340 | 340 |
| 341 | 341 | Agent.prototype.reuseSocket = function reuseSocket(socket, req) { | 342 | 342 | debug('have free socket'); | | 343 | + req.reusedSocket = true; | 343 | 344 | socket.ref(); | 344 | 345 | }; | 345 | 346 |
| |
Collapse file Copy file name to clipboardExpand all lines: lib/_http_client.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -195,6 +195,7 @@ function ClientRequest(input, options, cb) { | 195 | 195 | this.upgradeOrConnect = false; | 196 | 196 | this.parser = null; | 197 | 197 | this.maxHeadersCount = null; | | 198 | + this.reusedSocket = false; | 198 | 199 |
| 199 | 200 | let called = false; | 200 | 201 |
| |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-http-agent-keepalive.js +6-3Lines changed: 6 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -63,7 +63,8 @@ function checkDataAndSockets(body) { | 63 | 63 |
| 64 | 64 | function second() { | 65 | 65 | // 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); | 67 | 68 | assert.strictEqual(res.statusCode, 200); | 68 | 69 | res.on('data', checkDataAndSockets); | 69 | 70 | res.on('end', common.mustCall(() => { | @@ -80,7 +81,8 @@ function second() { | 80 | 81 |
| 81 | 82 | function remoteClose() { | 82 | 83 | // 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); | 84 | 86 | assert.deepStrictEqual(res.statusCode, 200); | 85 | 87 | res.on('data', checkDataAndSockets); | 86 | 88 | res.on('end', common.mustCall(() => { | @@ -120,7 +122,8 @@ function remoteError() { | 120 | 122 | server.listen(0, common.mustCall(() => { | 121 | 123 | name = `localhost:${server.address().port}:`; | 122 | 124 | // 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); | 124 | 127 | assert.strictEqual(res.statusCode, 200); | 125 | 128 | res.on('data', checkDataAndSockets); | 126 | 129 | res.on('end', common.mustCall(() => { | |
|