GeezerVison is showing github.com — http: overridable keep-alive behavior of `Agent` · nodejs/node@1390c28
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 +36Lines changed: 36 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -159,6 +159,42 @@ socket/stream from this function, or by passing the socket/stream to `callback`. | 159 | 159 |
| 160 | 160 | `callback` has a signature of `(err, stream)`. | 161 | 161 |
| | 162 | +### agent.keepSocketAlive(socket) | | 163 | +<!-- YAML | | 164 | +added: REPLACEME | | 165 | +--> | | 166 | + | | 167 | +* `socket` {net.Socket} | | 168 | + | | 169 | +Called when `socket` is detached from a request and could be persisted by the | | 170 | +Agent. Default behavior is to: | | 171 | + | | 172 | +```js | | 173 | +socket.unref(); | | 174 | +socket.setKeepAlive(agent.keepAliveMsecs); | | 175 | +``` | | 176 | + | | 177 | +This method can be overridden by a particular `Agent` subclass. If this | | 178 | +method returns a falsy value, the socket will be destroyed instead of persisting | | 179 | +it for use with the next request. | | 180 | + | | 181 | +### agent.reuseSocket(socket, request) | | 182 | +<!-- YAML | | 183 | +added: REPLACEME | | 184 | +--> | | 185 | + | | 186 | +* `socket` {net.Socket} | | 187 | +* `request` {http.ClientRequest} | | 188 | + | | 189 | +Called when `socket` is attached to `request` after being persisted because of | | 190 | +the keep-alive options. Default behavior is to: | | 191 | + | | 192 | +```js | | 193 | +socket.ref(); | | 194 | +``` | | 195 | + | | 196 | +This method can be overridden by a particular `Agent` subclass. | | 197 | + | 162 | 198 | ### agent.destroy() | 163 | 199 | <!-- YAML | 164 | 200 | added: v0.11.4 | |
Collapse file Copy file name to clipboardExpand all lines: lib/_http_agent.js +18-6Lines changed: 18 additions & 6 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -67,14 +67,15 @@ function Agent(options) { | 67 | 67 |
| 68 | 68 | if (count > self.maxSockets || freeLen >= self.maxFreeSockets) { | 69 | 69 | socket.destroy(); | 70 | | - } else { | | 70 | + } else if (self.keepSocketAlive(socket)) { | 71 | 71 | freeSockets = freeSockets || []; | 72 | 72 | self.freeSockets[name] = freeSockets; | 73 | | - socket.setKeepAlive(true, self.keepAliveMsecs); | 74 | | - socket.unref(); | 75 | 73 | socket._httpMessage = null; | 76 | 74 | self.removeSocket(socket, options); | 77 | 75 | freeSockets.push(socket); | | 76 | + } else { | | 77 | + // Implementation doesn't want to keep socket alive | | 78 | + socket.destroy(); | 78 | 79 | } | 79 | 80 | } else { | 80 | 81 | socket.destroy(); | @@ -142,13 +143,12 @@ Agent.prototype.addRequest = function(req, options) { | 142 | 143 | if (freeLen) { | 143 | 144 | // we have a free socket, so use that. | 144 | 145 | var socket = this.freeSockets[name].shift(); | 145 | | - debug('have free socket'); | 146 | 146 |
| 147 | 147 | // don't leak | 148 | 148 | if (!this.freeSockets[name].length) | 149 | 149 | delete this.freeSockets[name]; | 150 | 150 |
| 151 | | - socket.ref(); | | 151 | + this.reuseSocket(socket, req); | 152 | 152 | req.onSocket(socket); | 153 | 153 | this.sockets[name].push(socket); | 154 | 154 | } else if (sockLen < this.maxSockets) { | @@ -279,7 +279,19 @@ Agent.prototype.removeSocket = function removeSocket(s, options) { | 279 | 279 | } | 280 | 280 | }; | 281 | 281 |
| 282 | | -Agent.prototype.destroy = function() { | | 282 | +Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { | | 283 | + socket.setKeepAlive(true, this.keepAliveMsecs); | | 284 | + socket.unref(); | | 285 | + | | 286 | + return true; | | 287 | +}; | | 288 | + | | 289 | +Agent.prototype.reuseSocket = function reuseSocket(socket, req) { | | 290 | + debug('have free socket'); | | 291 | + socket.ref(); | | 292 | +}; | | 293 | + | | 294 | +Agent.prototype.destroy = function destroy() { | 283 | 295 | var sets = [this.freeSockets, this.sockets]; | 284 | 296 | for (var s = 0; s < sets.length; s++) { | 285 | 297 | var set = sets[s]; | |
Collapse file Copy file name to clipboard +67Lines changed: 67 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +const common = require('../common'); | | 3 | +const assert = require('assert'); | | 4 | + | | 5 | +const http = require('http'); | | 6 | + | | 7 | +const server = http.createServer((req, res) => { | | 8 | + res.end('ok'); | | 9 | +}).listen(0, common.mustCall(() => { | | 10 | + const agent = http.Agent({ | | 11 | + keepAlive: true, | | 12 | + maxSockets: 5, | | 13 | + maxFreeSockets: 2 | | 14 | + }); | | 15 | + | | 16 | + const keepSocketAlive = agent.keepSocketAlive; | | 17 | + const reuseSocket = agent.reuseSocket; | | 18 | + | | 19 | + let called = 0; | | 20 | + let expectedSocket; | | 21 | + agent.keepSocketAlive = common.mustCall((socket) => { | | 22 | + assert(socket); | | 23 | + | | 24 | + called++; | | 25 | + if (called === 1) { | | 26 | + return false; | | 27 | + } else if (called === 2) { | | 28 | + expectedSocket = socket; | | 29 | + return keepSocketAlive.call(agent, socket); | | 30 | + } | | 31 | + | | 32 | + assert.strictEqual(socket, expectedSocket); | | 33 | + return false; | | 34 | + }, 3); | | 35 | + | | 36 | + agent.reuseSocket = common.mustCall((socket, req) => { | | 37 | + assert.strictEqual(socket, expectedSocket); | | 38 | + assert(req); | | 39 | + | | 40 | + return reuseSocket.call(agent, socket, req); | | 41 | + }, 1); | | 42 | + | | 43 | + function req(callback) { | | 44 | + http.request({ | | 45 | + method: 'GET', | | 46 | + path: '/', | | 47 | + agent, | | 48 | + port: server.address().port | | 49 | + }, common.mustCall((res) => { | | 50 | + res.resume(); | | 51 | + res.once('end', common.mustCall(() => { | | 52 | + setImmediate(callback); | | 53 | + })); | | 54 | + })).end(); | | 55 | + } | | 56 | + | | 57 | + // Should destroy socket instead of keeping it alive | | 58 | + req(common.mustCall(() => { | | 59 | + // Should keep socket alive | | 60 | + req(common.mustCall(() => { | | 61 | + // Should reuse the socket | | 62 | + req(common.mustCall(() => { | | 63 | + server.close(); | | 64 | + })); | | 65 | + })); | | 66 | + })); | | 67 | +})); |
|