Skip to content

Commit 8d129c3

Browse files
eduardbmeaddaleax
authored andcommitted
net: multiple listen() events fail silently
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: throw an error if listen method called more than once. close() method should be called between listen() method calls. Refs: nodejs/node#8294 Fixes: nodejs/node#6190 Fixes: nodejs/node#11685 PR-URL: nodejs/node#13149 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent 75dd355 commit 8d129c3

File tree

5 files changed

+69
-9
lines changed

5 files changed

+69
-9
lines changed

doc/api/http.md

+9-6
Original file line numberDiff line numberDiff line change
@@ -809,8 +809,9 @@ This function is asynchronous. `callback` will be added as a listener for the
809809

810810
Returns `server`.
811811

812-
*Note*: The `server.listen()` method may be called multiple times. Each
813-
subsequent call will *re-open* the server using the provided options.
812+
*Note*: The `server.listen()` method can be called again if and only if there was an error
813+
during the first `server.listen()` call or `server.close()` has been called.
814+
Otherwise, an `ERR_SERVER_ALREADY_LISTEN` error will be thrown.
814815

815816
### server.listen(path[, callback])
816817
<!-- YAML
@@ -825,8 +826,9 @@ Start a UNIX socket server listening for connections on the given `path`.
825826
This function is asynchronous. `callback` will be added as a listener for the
826827
[`'listening'`][] event. See also [`net.Server.listen(path)`][].
827828

828-
*Note*: The `server.listen()` method may be called multiple times. Each
829-
subsequent call will *re-open* the server using the provided options.
829+
*Note*: The `server.listen()` method can be called again if and only if there was an error
830+
during the first `server.listen()` call or `server.close()` has been called.
831+
Otherwise, an `ERR_SERVER_ALREADY_LISTEN` error will be thrown.
830832

831833
### server.listen([port][, hostname][, backlog][, callback])
832834
<!-- YAML
@@ -861,8 +863,9 @@ parameter is 511 (not 512).
861863
This function is asynchronous. `callback` will be added as a listener for the
862864
[`'listening'`][] event. See also [`net.Server.listen(port)`][].
863865

864-
*Note*: The `server.listen()` method may be called multiple times. Each
865-
subsequent call will *re-open* the server using the provided options.
866+
*Note*: The `server.listen()` method can be called again if and only if there was an error
867+
during the first `server.listen()` call or `server.close()` has been called.
868+
Otherwise, an `ERR_SERVER_ALREADY_LISTEN` error will be thrown.
866869

867870
### server.listening
868871
<!-- YAML

doc/api/net.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ on Linux. The default value of this parameter is 511 (not 512).
199199

200200
* All [`net.Socket`][] are set to `SO_REUSEADDR` (See [socket(7)][] for
201201
details).
202-
203-
* The `server.listen()` method may be called multiple times. Each
204-
subsequent call will *re-open* the server using the provided options.
202+
* The `server.listen()` method can be called again if and only if there was an error
203+
during the first `server.listen()` call or `server.close()` has been called.
204+
Otherwise, an `ERR_SERVER_ALREADY_LISTEN` error will be thrown.
205205

206206
One of the most common errors raised when listening is `EADDRINUSE`.
207207
This happens when another server is already listening on the requested

lib/internal/errors.js

+2
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ E('ERR_NO_ICU', '%s is not supported on Node.js compiled without ICU');
237237
E('ERR_NO_LONGER_SUPPORTED', '%s is no longer supported');
238238
E('ERR_OUTOFMEMORY', 'Out of memory');
239239
E('ERR_PARSE_HISTORY_DATA', 'Could not parse history data in %s');
240+
E('ERR_SERVER_ALREADY_LISTEN',
241+
'Listen method has been called more than once without closing.');
240242
E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound');
241243
E('ERR_SOCKET_BAD_PORT', 'Port should be > 0 and < 65536');
242244
E('ERR_SOCKET_BAD_TYPE',

lib/net.js

+4
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,10 @@ Server.prototype.listen = function(...args) {
14231423
var options = normalized[0];
14241424
var cb = normalized[1];
14251425

1426+
if (this._handle) {
1427+
throw new errors.Error('ERR_SERVER_ALREADY_LISTEN');
1428+
}
1429+
14261430
var hasCallback = (cb !== null);
14271431
if (hasCallback) {
14281432
this.once('listening', cb);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const net = require('net');
6+
7+
// First test. Check that after error event you can listen right away.
8+
{
9+
const dummyServer = net.Server();
10+
const server = net.Server();
11+
12+
// Run some server in order to simulate EADDRINUSE error.
13+
dummyServer.listen(common.mustCall(() => {
14+
// Try to listen used port.
15+
server.listen(dummyServer.address().port);
16+
}));
17+
18+
server.on('error', common.mustCall((e) => {
19+
assert.doesNotThrow(
20+
() => server.listen(common.mustCall(() => {
21+
dummyServer.close();
22+
server.close();
23+
}))
24+
);
25+
}));
26+
}
27+
28+
// Second test. Check that second listen call throws an error.
29+
{
30+
const server = net.Server();
31+
32+
server.listen(common.mustCall(() => server.close()));
33+
34+
common.expectsError(() => server.listen(), {
35+
code: 'ERR_SERVER_ALREADY_LISTEN',
36+
type: Error
37+
});
38+
}
39+
40+
// Third test.
41+
// Check that after the close call you can run listen method just fine.
42+
{
43+
const server = net.Server();
44+
45+
server.listen(common.mustCall(() => {
46+
server.close();
47+
assert.doesNotThrow(
48+
() => server.listen(common.mustCall(() => server.close()))
49+
);
50+
}));
51+
}

0 commit comments

Comments
 (0)