diff --git a/README.md b/README.md index 6cb326a..09389e6 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,8 @@ Create a new TCP server. Options include: ``` js { - allowHalfOpen: false // set to true to allow half open TCP connections + allowHalfOpen: false, // set to true to allow half open TCP connections + reusePort: true // Disable if you don't want SO_REUSEPORT to be set (SO_REUSEADDR on windows) } ``` diff --git a/lib/connection.js b/lib/connection.js index f443a1c..3b3d35b 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -34,7 +34,8 @@ class Connection extends events.EventEmitter { this._onwrite, this._onread, this._onfinish, - this._onclose + this._onclose, + 0 ) if (server) { diff --git a/lib/server.js b/lib/server.js index f3ac717..652fd22 100644 --- a/lib/server.js +++ b/lib/server.js @@ -2,6 +2,8 @@ const binding = require('./binding') const Connection = require('./connection') const lookup = require('./lookup') const events = require('events') +const semver = require('semver') +const os = require('os') class Server extends events.EventEmitter { constructor (opts) { @@ -10,6 +12,12 @@ class Server extends events.EventEmitter { this.connections = [] this.allowHalfOpen = !!opts.allowHalfOpen + this.reusePort = (opts.reusePort || opts.reusePort == null) ? 1 : 0 + + // SO_REUSEPORT is only supported on kernel 3.9+ + if (os.platform() === 'linux' && !semver.satisfies(semver.coerce(os.release()), '>=3.9')) { + this.reusePort = 0 + } this._closed = false this._address = null @@ -71,7 +79,8 @@ class Server extends events.EventEmitter { null, null, null, - this._onclose + this._onclose, + this.reusePort ) } diff --git a/package.json b/package.json index b8feb20..c186991 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "napi-macros": "^1.3.0", "node-gyp-build": "^3.3.0", + "semver": "^5.5.1", "unordered-set": "^2.0.0" }, "devDependencies": { diff --git a/src/turbo_net.c b/src/turbo_net.c index 2d38326..daed5fa 100644 --- a/src/turbo_net.c +++ b/src/turbo_net.c @@ -132,8 +132,9 @@ static void on_uv_connect (uv_connect_t* req, int status) { } NAPI_METHOD(turbo_net_tcp_init) { - NAPI_ARGV(8) + NAPI_ARGV(9) NAPI_ARGV_BUFFER_CAST(turbo_net_tcp_t *, self, 0) + NAPI_ARGV_UINT32(reusePort, 8) int err; uv_tcp_t *handle = &(self->handle); @@ -141,7 +142,28 @@ NAPI_METHOD(turbo_net_tcp_init) { handle->data = self; self->env = env; - NAPI_UV_THROWS(err, uv_tcp_init(uv_default_loop(), handle)); + // SO_REUSEADDR on windows + #ifdef _WIN32 + if (!reusePort) { + NAPI_UV_THROWS(err, uv_tcp_init(uv_default_loop(), handle)); + } else { + NAPI_UV_THROWS(err, uv_tcp_init_ex(uv_default_loop(), handle, AF_INET)); + uv_os_fd_t fd; + int on = 1; + NAPI_UV_THROWS(err, uv_fileno((const uv_handle_t *) handle, &fd)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + } + #else + if (!reusePort) { + NAPI_UV_THROWS(err, uv_tcp_init(uv_default_loop(), handle)); + } else { + NAPI_UV_THROWS(err, uv_tcp_init_ex(uv_default_loop(), handle, AF_INET)); + uv_os_fd_t fd; + int on = 1; + NAPI_UV_THROWS(err, uv_fileno((const uv_handle_t *) handle, &fd)); + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + } + #endif napi_create_reference(env, argv[1], 1, &(self->ctx)); napi_create_reference(env, argv[2], 1, &(self->on_alloc_connection)); diff --git a/test/server.js b/test/server.js index edc5053..f44d5bf 100644 --- a/test/server.js +++ b/test/server.js @@ -1,5 +1,7 @@ const tape = require('tape') const turbo = require('../') +const os = require('os') +const semver = require('semver') tape('listen', function (t) { const server = turbo.createServer() @@ -46,10 +48,14 @@ tape('address no listen', function (t) { }) tape('listen on used port', function (t) { - const server = turbo.createServer() + const server = turbo.createServer({ + reusePort: false + }) server.listen(function () { - const another = turbo.createServer() + const another = turbo.createServer({ + reusePort: false + }) another.on('error', function (err) { server.close() @@ -60,3 +66,24 @@ tape('listen on used port', function (t) { another.listen(server.address().port) }) }) + +tape(`listen on used port (SO_REUSEPORT) (${os.platform()}:${os.release()})`, function (t) { + if (os.platform() === 'linux' && !semver.satisfies(semver.coerce(os.release()), '>=3.9')) { + t.pass('SO_REUSEPORT only supported on kernel 3.9+') + t.end() + return + } + + const server = turbo.createServer() + + server.listen(function () { + const another = turbo.createServer() + + another.listen(server.address().port, function () { + server.close() + another.close() + t.pass('should not error') + t.end() + }) + }) +}) diff --git a/test/write.js b/test/write.js index 84a61ec..0a43e56 100644 --- a/test/write.js +++ b/test/write.js @@ -2,7 +2,7 @@ const tape = require('tape') const turbo = require('../') tape('writev', function (t) { - const server = turbo.createServer(echo) + const server = turbo.createServer(echo, {reusePort: false}) server.listen(function () { const client = turbo.connect(server.address().port) @@ -22,7 +22,7 @@ tape('writev', function (t) { }) tape('writev after connect', function (t) { - const server = turbo.createServer(echo) + const server = turbo.createServer(echo, {reusePort: false}) server.listen(function () { const client = turbo.connect(server.address().port) @@ -44,7 +44,7 @@ tape('writev after connect', function (t) { }) tape('writev before and after connect', function (t) { - const server = turbo.createServer(echo) + const server = turbo.createServer(echo, {reusePort: false}) server.listen(function () { const client = turbo.connect(server.address().port) @@ -73,7 +73,7 @@ tape('writev before and after connect', function (t) { }) tape('writev twice', function (t) { - const server = turbo.createServer(echo) + const server = turbo.createServer(echo, {reusePort: false}) server.listen(function () { const client = turbo.connect(server.address().port) @@ -99,7 +99,7 @@ tape('writev twice', function (t) { }) tape('write 256 buffers', function (t) { - const server = turbo.createServer(echo) + const server = turbo.createServer(echo, {reusePort: false}) server.listen(function () { const client = turbo.connect(server.address().port)