Repository navigation
http: client keep-alive for UNIX domain sockets #13214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Makes `Connection: keep-alive` behave correctly when making client connections to UNIX domain sockets. Prior to this, connections would never be re-used, but the keep-alive would cause the connections to stick around until they time out. This would lead to an eventual EMFILE error due to all the connections staying open. This was due to http.Agent not properly supporting UNIX domain sockets.
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -240,23 +240,7 @@ function ClientRequest(options, cb) { | |||
| this._deferToConnect(null, null, () => this._flush()); | ||||
| }; | ||||
|
|
||||
| var newSocket; | ||||
| if (this.socketPath) { | ||||
| this._last = true; | ||||
| this.shouldKeepAlive = false; | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bnoordhuis I tried to do a deep blame, seems like it's your code Line 1025 in bc7cfd7
Is there a specific reason why POSIX sockets shouldKeepAlive = false?
|
||||
| var optionsPath = { | ||||
| path: this.socketPath, | ||||
| timeout: this.timeout, | ||||
| rejectUnauthorized: !!options.rejectUnauthorized | ||||
| }; | ||||
| newSocket = this.agent.createConnection(optionsPath, oncreate); | ||||
| if (newSocket && !called) { | ||||
| called = true; | ||||
| this.onSocket(newSocket); | ||||
| } else { | ||||
| return; | ||||
| } | ||||
| } else if (this.agent) { | ||||
| if (this.agent) { | ||||
| // If there is an agent we should default to Connection:keep-alive, | ||||
| // but only if the Agent will actually reuse the connection! | ||||
| // If it's not a keepAlive agent, and the maxSockets==Infinity, then | ||||
|
|
@@ -274,7 +258,7 @@ function ClientRequest(options, cb) { | |||
| this._last = true; | ||||
| this.shouldKeepAlive = false; | ||||
| if (typeof options.createConnection === 'function') { | ||||
| newSocket = options.createConnection(options, oncreate); | ||||
| var newSocket = options.createConnection(options, oncreate); | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's initialized, never re-assigned, scope limited, and quacks like a duck; it's
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call! I left it as |
||||
| if (newSocket && !called) { | ||||
| called = true; | ||||
| this.onSocket(newSocket); | ||||
|
|
||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| 'use strict'; | ||
| const common = require('../common'); | ||
| const assert = require('assert'); | ||
| const http = require('http'); | ||
|
|
||
| const server = http.createServer((req, res) => res.end()); | ||
|
|
||
| common.refreshTmpDir(); | ||
|
|
||
| server.listen(common.PIPE, () => | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Nah, it's a matter of judgment. There are certainly callbacks that should not be wrapped (such as callbacks in cluster workers, which will throw but be ignored by the cluster master, so having them wrapped gives a false sense of security that the callback is being executed).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I'll put them in anyway to be super clear that these are all expected to run. |
||
| asyncLoop(makeKeepAliveRequest, 10, () => | ||
| server.getConnections(common.mustCall((err, conns) => { | ||
| assert.ifError(err); | ||
| assert.strictEqual(1, conns); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: can you please swap the arguments? |
||
| server.close(); | ||
| })) | ||
| ) | ||
| ); | ||
|
|
||
| function asyncLoop(fn, times, cb) { | ||
| fn(function handler() { | ||
| if (--times) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| fn(handler); | ||
| } else { | ||
| cb(); | ||
| } | ||
| }); | ||
| } | ||
| function makeKeepAliveRequest(cb) { | ||
| http.get({ | ||
| socketPath: common.PIPE, | ||
| headers: {connection: 'keep-alive'} | ||
| }, (res) => res.on('data', () => {}).on('error', assert.fail).on('end', cb)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Instead of |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to extend
test/parallel/test-http-agent-getname.jsand cover this.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the socketPath should not be set when a family, a port or a localAddress is set. Therefore it would be nicer branch wise to write
I think it is similar with
servernamedown below.