From fef369519ced8c9af6f1e5f85f61574e0f00f76f Mon Sep 17 00:00:00 2001 From: tigerbot Date: Mon, 29 May 2017 12:41:12 -0600 Subject: [PATCH] fixed problems with simultaneous pair creation --- .gitignore | 1 + lib/socket-pair.js | 95 +++++++++++++++++++++++++++------------------- package.json | 5 ++- 3 files changed, 60 insertions(+), 41 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/lib/socket-pair.js b/lib/socket-pair.js index 1e3371e..c83c0cc 100644 --- a/lib/socket-pair.js +++ b/lib/socket-pair.js @@ -1,15 +1,15 @@ 'use strict'; -var server; -var listening = false; -var sock; +var PromiseA = require('bluebird'); +var promises = {}; -function createServer(cb, prefix) { +function createServer(prefix) { var os = require('os'); var net = require('net'); var path = require('path'); - var sockname = (prefix || 'node-socket-pair') + '.' + require('crypto').randomBytes(16).toString('hex') + '.sock'; + var sockname = prefix + '.' + require('crypto').randomBytes(16).toString('hex') + '.sock'; + var sock; if (/^win/.test(os.platform())) { sock = path.join('\\\\?\\pipe', process.cwd(), sockname); } @@ -17,52 +17,67 @@ function createServer(cb, prefix) { sock = path.join(os.tmpdir(), sockname); } - server = net.createServer(); - function onServerError(err) { - cb(err); - } - server.once('error', onServerError); - server.once('listening', function () { - listening = true; - server.removeListener('error', onServerError); + var server = net.createServer({allowHalfOpen: true}); + promises[prefix] = new PromiseA(function (resolve, reject) { + server.once('error', reject); + + server.listen(sock, function () { + server.removeListener('error', reject); + resolve({sock: sock, server: server}); + }); }); + + server.on('close', function () { + delete promises[prefix]; + }); + + return promises[prefix]; } exports.create = function create(cb, prefix) { + prefix = prefix || 'node-socket-pair'; var net = require('net'); - var client = new net.Socket(); + var client = new net.Socket({allowHalfOpen: true}); - function createConnection() { - function onClientError(err) { - cb(err); - } - client.connect(sock, function () { - client.removeListener('error', onClientError); + if (!promises[prefix]) { + createServer(prefix); + } + + // We chain the promises to make sure that we never have multiple pending connections at + // the same time to make sure the pairs are always matched correctly. Otherwise two different + // `onConn` listeners might end up with the same connection. + promises[prefix] = promises[prefix].then(function (result) { + return new PromiseA(function (resolve) { + function onConn(conn) { + cb(null, conn); + resolve(result); + } + result.server.once('connection', onConn); + + function onErr(err) { + result.server.removeListener('connection', onConn); + cb(err); + resolve(result); + } + client.once('error', onErr); + client.connect(result.sock, function () { + client.removeListener('error', onErr); + }); }); - client.once('error', onClientError); - } - - // This server listens on a Unix socket or Windows pipe at 'sock' - if (!server) { - createServer(cb, prefix); - } - - server.once('connection', function onEach(connection) { - cb(null, connection); + }, function (err) { + cb(err); + return PromiseA.reject(err); }); - if (!listening) { - server.listen(sock, createConnection); - } - else { - createConnection(); - } - return client; }; exports.closeAll = function () { - if (server) { - server.close(); - } + Object.keys(promises).forEach(function (key) { + promises[key].then(function (result) { + result.server.close(); + }, function () { + delete promises[key]; + }); + }); }; diff --git a/package.json b/package.json index 6287067..aedf578 100644 --- a/package.json +++ b/package.json @@ -23,5 +23,8 @@ "writer" ], "author": "AJ ONeal (https://coolaj86.com)", - "license": "MIT OR Apache-2.0" + "license": "MIT OR Apache-2.0", + "dependencies": { + "bluebird": "^3.5.0" + } }