-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
net: multiple listen() events fail silently #8419
Conversation
Related: #8294 … I read that as meaning that the current behaviour is the intended one? |
The issue #6190 for this PR still open. Also, it should be more appropriate err code instead of |
oh, yeah. /cc @trevnorris @jasnell |
lib/net.js
Outdated
var er = new Error('listen method has been called more than once.'); | ||
// maybe this is should be another code error | ||
er.code = 'EADDRINUSE'; | ||
self.emit('error', er); |
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.
After emitting this error, why does it continue?
f5b20c9
to
a066225
Compare
@thefourtheye Thank you for comments. |
Hi there, @addaleax @thefourtheye @trevnorris @jasnell just a quick reminder. |
Thanks for the reminder ping. I'll take another look at this a bit later on today. |
Hi there, @addaleax @thefourtheye @trevnorris @jasnell just a quick reminder. Second one :) |
@eduardbcom Can you please rebase, so that we can take another look at the latest code? |
It would be (have been?) nice if this could('ve) landed in Node 7. |
As a semver-major it can't get into v7 without CTC review and ok. We can try. Adding it to the ctc agenda. |
ping @eduardbcom … could you rebase this so that it can be ready for merging from your side? :) |
@addaleax sorry for the delay, I don't exactly understand what you expect to receive. rebase to master ? thx. |
@eduardbcom Yep, rebase against master to remove the merge conflicts github is complaining about. |
lib/net.js
Outdated
@@ -1327,6 +1329,15 @@ function listen(self, address, port, addressType, backlog, fd, exclusive) { | |||
Server.prototype.listen = function() { | |||
var self = this; | |||
|
|||
if (self._listenHasBeenCalled) { | |||
var er = new Error('listen method has been called more than once.'); |
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.
are we using let
in newly added code or are the existing var
's in here keeping us consistent?
From what I’ve gathered in the CTC meeting it seems like calling |
CTC meeting:
No negatives from the CTC meeting but it might need to come back next week or be handled async here. |
I'm +1 on landing it in v7 |
CI 5: https://ci.nodejs.org/job/node-test-commit/8384/ @nodejs/ctc this needs some review. |
lib/net.js
Outdated
@@ -1379,6 +1397,14 @@ Server.prototype.listen = function() { | |||
var options = normalized[0]; | |||
const cb = normalized[1]; | |||
|
|||
if (this[serverListenHasBeenCalled]) { | |||
var er = new Error('listen method has been called more than once.'); | |||
er.code = 'ELISTENCALLEDTWICE'; |
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 don't think we should create new error codes.
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 agree
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.
What error code should I use ?
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.
We don't necessarily need to attach a code. If we really want one, EISCONN
, EADDRNOTAVAIL
, or maybe EALREADY
are options.
let err1 = false; | ||
let err2 = false; | ||
|
||
function case1() { |
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.
You don't need to use functions. You can create separate block scopes.
function case1() { | ||
|
||
const dummyServer = net.Server(); | ||
const server1 = net.Server(); |
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.
You don't need server1
and server2
as different names. They are in different scopes. Just use server
for both.
const dummyServer = net.Server(); | ||
const server1 = net.Server(); | ||
|
||
// run some server |
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.
Can you capitalize and punctuate comments. This might fit on a single line too.
|
||
// run some server | ||
// in order to simulate EADDRINUSE error | ||
dummyServer.listen(0, 'localhost', () => { |
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.
Please add common.mustCall()
around this callback.
|
||
server2.on('error', common.mustCall(function(e) { | ||
assert.strictEqual(e.code, 'ELISTENCALLEDTWICE'); | ||
err2 = true; |
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.
err2
isn't needed. This listener should only be called once, and is guaranteed by common.mustCall()
.
server1.close(); | ||
}); | ||
} else if (e.code === 'ELISTENCALLEDTWICE') { | ||
err1 = true; |
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.
Instead of having err1
, couldn't this just be an assertion?
case1(); | ||
case2(); | ||
|
||
process.on('exit', function() { |
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 this whole block could go away.
doc/api/http.md
Outdated
*Note*: The `server.listen()` method may be called multiple times. Each | ||
subsequent call will *re-open* the server using the provided options. | ||
*Note*: | ||
The `server.listen()` method may be called one more time iff there was an error during the first *listen* call or you explicitly called `server.close()`. Otherwise, `ELISTENCALLEDTWICE` error would be emitted. |
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.
Can you break this line at 80 columns please?
doc/api/http.md
Outdated
*Note*: The `server.listen()` method may be called multiple times. Each | ||
subsequent call will *re-open* the server using the provided options. | ||
*Note*: | ||
The `server.listen()` method may be called one more time iff there was an error during the first *listen* call or you explicitly called `server.close()`. Otherwise, `ELISTENCALLEDTWICE` error would be emitted. |
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.
Can you break this line at 80 columns please?
doc/api/http.md
Outdated
*Note*: The `server.listen()` method may be called multiple times. Each | ||
subsequent call will *re-open* the server using the provided options. | ||
*Note*: | ||
The `server.listen()` method may be called one more time iff there was an error during the first *listen* call or you explicitly called `server.close()`. Otherwise, `ELISTENCALLEDTWICE` error would be emitted. |
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.
Can you break this line at 80 columns please?
lib/net.js
Outdated
@@ -1379,6 +1397,14 @@ Server.prototype.listen = function() { | |||
var options = normalized[0]; | |||
const cb = normalized[1]; | |||
|
|||
if (this[serverListenHasBeenCalled]) { | |||
var er = new Error('listen method has been called more than once.'); | |||
er.code = 'ELISTENCALLEDTWICE'; |
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 agree
316f7ee
to
cd5396f
Compare
@evanlucas @cjihrig @gibfahn @Trott thanks for comments. updated pr |
doc/api/http.md
Outdated
*Note*: | ||
The `server.listen()` method may be called one more time iff there was an error | ||
during the first *listen* call or you explicitly called `server.close()`. | ||
Otherwise, `EALREADY` error would be emitted. |
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.
"Otherwise, an EALREADY
error is emitted." - likewise below.
Also, I'd probably replace "may be called one more time " with "can be called again."
doc/api/net.md
Outdated
subsequent call will *re-open* the server using the provided options. | ||
* The `server.listen()` method may be called one more time iff there was an error | ||
during the first *listen* call or you explicitly called `server.close()`. | ||
Otherwise, `EALREADY` error would be emitted. |
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.
Can you indent here?
lib/net.js
Outdated
return this[serverHandle]; | ||
}, | ||
enumerable: true | ||
}); |
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 don't know how I feel about turning an internal property into an accessor... Why did you decide on this approach? Is it because of #8419 (comment)?
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.
The main reason I decided to do it in this way it's because we need to set serverListenHasBeenCalled
back to false when we close connection or some error has happened. And I have noticed that for both cases we call the same code to handle _handle
field. Concretely:
this._handle.close(); this._handle = null;
So in order to avoid repetitions and do something like this every time we close this._handle
this._handle.close(); this._handle = null; this.serverListenHasBeenCalled = false
I decided to make this stuff in one place.
A big plus of this, is that if someone in a future will add some logic to handle error, they will not forget to set this.serverListenHasBeenCalled = false
.
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.
@bnoordhuis what are you thoughts on this now? Still conflicted?
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'd stick with simple properties. Accessors have overhead. If that means duplicating an assignment in two places, that's something I can live with.
lib/net.js
Outdated
@@ -1379,6 +1397,14 @@ Server.prototype.listen = function() { | |||
var options = normalized[0]; | |||
const cb = normalized[1]; | |||
|
|||
if (this[serverListenHasBeenCalled]) { | |||
var er = new Error('listen method has been called more than once.'); | |||
er.code = 'EALREADY'; |
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.
This is in line with what libuv returns for client sockets but using EALREADY instead of EINVAL (the standard POSIX error for trying to rebind) is one of my little regrets there.
Thoughts, anyone? Consistency or conformity?
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.
If you regret it, then we should probably go with EINVAL
.
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.
Agreed.
})); | ||
|
||
server.listen(0, 'localhost', () => server.close()); | ||
server.listen(1, 'localhost'); |
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.
Why 1
here?
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.
Right, no reason for this.
Since this has received three CTC reviews since application of |
Ping @eduardbcom... some new comments have been added |
e2f6e5e
to
1488e56
Compare
@jasnell thanks for the reminder ping. @bnoordhuis please check the comment for your question about the updated pr. Thank you folks for your comments. |
assert.strictEqual(e.code, 'EINVAL'); | ||
})); | ||
|
||
server.listen(common.mustCall( |
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.
This can all be one line.
// after first time is fail (without calling close method). | ||
|
||
// The second one is about calling a listen method twice. | ||
// It should be EALREADY but the first server listen call is working. |
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.
Should EALREADY
be EINVAL
?
lib/net.js
Outdated
@@ -1373,6 +1391,14 @@ Server.prototype.listen = function() { | |||
var options = normalized[0]; | |||
const cb = normalized[1]; | |||
|
|||
if (this[serverListenHasBeenCalled]) { |
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.
Can you check === true
here.
lib/net.js
Outdated
this._handle = null; | ||
|
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.
Can you remove the unnecessary whitespace changes (here and in other places).
1488e56
to
1533c61
Compare
thanks @cjihrig, updated |
@jasnell @bnoordhuis @Trott @cjihrig @evanlucas quick reminder |
doc/api/http.md
Outdated
*Note*: The `server.listen()` method may be called multiple times. Each | ||
subsequent call will *re-open* the server using the provided options. | ||
*Note*: | ||
The `server.listen()` method can be called again iff there was an error |
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 forgot what our position was on using 'iff' in the documentation: yay or nay? git grep
suggests nay.
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.
My preference is to avoid it. Please use if and only if
instead.
lib/net.js
Outdated
return this[serverHandle]; | ||
}, | ||
enumerable: true | ||
}); |
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'd stick with simple properties. Accessors have overhead. If that means duplicating an assignment in two places, that's something I can live with.
f1bfdbd
to
df659f0
Compare
f1bfdbd
to
281de19
Compare
Checklist
make -j4 test
(UNIX), orvcbuild test nosign
(Windows) passesAffected core subsystem(s)
net
Description of change
Problem: It's possible to run listen() on a net.Server that's already listening to a port.
The result is silent failure, with the side effect of changing the _connectionKey and or _pipeName.
Solution: emit error if listen method called more than once.
close() method should be called between listen() method calls.