Skip to content

Instantly share code, notes, and snippets.

@indutny
Created April 14, 2014 07:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save indutny/10624955 to your computer and use it in GitHub Desktop.
Save indutny/10624955 to your computer and use it in GitHub Desktop.
diff --git a/lib/child_process.js b/lib/child_process.js
index c8e9e37..390f435 100644
--- a/lib/child_process.js
+++ b/lib/child_process.js
@@ -140,6 +140,11 @@ var handleConversion = {
handle.onread = function() {};
socket._handle = null;
+ // Kill happy eyeball
+ if (socket._eyeball)
+ socket._eyeball.destroy();
+ socket._eyeball = null;
+
return handle;
},
@@ -465,7 +470,9 @@ function setupChannel(target, channel) {
if (obj.simultaneousAccepts) {
net._setSimultaneousAccepts(handle);
}
- } else if (this._handleQueue) {
+ } else if (this._handleQueue &&
+ message &&
+ message.cmd !== 'NODE_HANDLE_ACK') {
// Queue request anyway to avoid out-of-order messages.
this._handleQueue.push({ message: message, handle: null });
return;
diff --git a/lib/dns.js b/lib/dns.js
index ab80deb..2a34177 100644
--- a/lib/dns.js
+++ b/lib/dns.js
@@ -90,7 +90,14 @@ function onlookup(err, addresses) {
if (err) {
return this.callback(errnoException(err, 'getaddrinfo', this.hostname));
}
- if (this.family) {
+ if (this.raw) {
+ this.callback(null, addresses.map(function(host) {
+ return {
+ host: host,
+ family: host.indexOf(':') >= 0 ? 6 : 4
+ };
+ }));
+ } else if (this.family) {
this.callback(null, addresses[0], this.family);
} else {
this.callback(null, addresses[0], addresses[0].indexOf(':') >= 0 ? 6 : 4);
@@ -100,11 +107,19 @@ function onlookup(err, addresses) {
// Easy DNS A/AAAA look up
// lookup(hostname, [family,] callback)
+// lookup(options, callback)
exports.lookup = function(hostname, family, callback) {
+ var raw = false;
+
// parse arguments
if (arguments.length === 2) {
callback = family;
family = 0;
+ if (hostname !== null && typeof hostname === 'object') {
+ raw = hostname.raw;
+ family = hostname.family || 0;
+ hostname = hostname.host;
+ }
} else if (!family) {
family = 0;
} else {
@@ -116,13 +131,19 @@ exports.lookup = function(hostname, family, callback) {
callback = makeAsync(callback);
if (!hostname) {
- callback(null, null, family === 6 ? 6 : 4);
+ if (raw)
+ callback(null, []);
+ else
+ callback(null, null, family === 6 ? 6 : 4);
return {};
}
var matchedFamily = net.isIP(hostname);
if (matchedFamily) {
- callback(null, hostname, matchedFamily);
+ if (raw)
+ callback(null, [{ host: hostname, family: matchedFamily }]);
+ else
+ callback(null, hostname, matchedFamily);
return {};
}
@@ -130,7 +151,8 @@ exports.lookup = function(hostname, family, callback) {
callback: callback,
family: family,
hostname: hostname,
- oncomplete: onlookup
+ oncomplete: onlookup,
+ raw: raw
};
var err = cares.getaddrinfo(req, hostname, family);
if (err) throw errnoException(err, 'getaddrinfo', hostname);
diff --git a/lib/net.js b/lib/net.js
index 84c6833..4874610 100644
--- a/lib/net.js
+++ b/lib/net.js
@@ -79,7 +79,7 @@ exports.createServer = function() {
// connect(port, [host], [cb])
// connect(path, [cb]);
//
-exports.connect = exports.createConnection = function() {
+var publicConnect = exports.connect = exports.createConnection = function() {
var args = normalizeConnectArgs(arguments);
debug('createConnection', args);
var s = new Socket(args[0]);
@@ -132,6 +132,7 @@ function Socket(options) {
if (!(this instanceof Socket)) return new Socket(options);
this._connecting = false;
+ this._eyeball = null;
this._hadError = false;
this._handle = null;
this._host = null;
@@ -786,6 +787,7 @@ function afterWrite(status, handle, req, err) {
function connect(self, address, port, addressType, localAddress, localPort) {
+ debug('connect', address, port, addressType, localAddress, localPort);
// TODO return promise from Socket.prototype.connect which
// wraps _connectReq.
@@ -837,18 +839,17 @@ function connect(self, address, port, addressType, localAddress, localPort) {
}
var req = { oncomplete: afterConnect };
+ port = port | 0;
+
if (addressType === 6 || addressType === 4) {
- port = port | 0;
if (port <= 0 || port > 65535)
throw new RangeError('Port should be > 0 and < 65536');
+ }
- if (addressType === 6) {
- err = self._handle.connect6(req, address, port);
- } else if (addressType === 4) {
- err = self._handle.connect(req, address, port);
- }
+ if (addressType === 6) {
+ err = self._handle.connect6(req, address, port);
} else {
- err = self._handle.connect(req, address, afterConnect);
+ err = self._handle.connect(req, address, port);
}
if (err) {
@@ -857,6 +858,75 @@ function connect(self, address, port, addressType, localAddress, localPort) {
}
+function tryEyeball(socket, options, addr) {
+ debug('trying eyeball connect to ' + addr.host);
+
+ var opts = util._extend({}, options);
+ opts.host = addr.host;
+
+ var eyeball = publicConnect(opts, function() {
+ // Looks like primary IP was slower or failed
+ if (socket._connecting && !socket.destroyed) {
+ switchEyeball(socket);
+ } else if (socket._eyeball) {
+ assert(socket._eyeball === eyeball);
+
+ // Cleanup
+ socket._eyeball = null;
+ eyeball.destroy();
+ }
+ });
+
+ // Cleanup on error
+ function cleanup() {
+ if (socket._eyeball) {
+ assert(socket._eyeball === eyeball);
+ socket._eyeball = null;
+ eyeball.destroy();
+ }
+ }
+ eyeball.on('error', cleanup);
+
+ socket._eyeball = eyeball;
+}
+
+
+function switchEyeball(socket) {
+ var eyeball = socket._eyeball;
+ if (!eyeball)
+ return false;
+ socket._eyeball = null;
+
+ debug('switching eyeball');
+
+ // Socket was transferred via cluster module or just closed
+ if (!socket._handle)
+ return;
+
+ var old = socket._handle;
+
+ // Ignore errors on it
+ old.owner = eyeball;
+
+ // Replace with a new one
+ eyeball._handle.owner = socket;
+ eyeball.removeAllListeners('error');
+ eyeball.removeAllListeners('connect');
+ socket._connecting = eyeball._connecting;
+ socket._handle = eyeball._handle;
+ socket._host = eyeball._host;
+
+ // Close old handle
+ try {
+ eyeball._handle = old;
+ old.close();
+ } catch(e) {
+ }
+
+ return true;
+}
+
+
Socket.prototype.connect = function(options, cb) {
if (this.write !== Socket.prototype.write)
this.write = Socket.prototype.write;
@@ -901,18 +971,42 @@ Socket.prototype.connect = function(options, cb) {
if (pipe) {
connect(self, options.path);
- } else if (!options.host) {
- debug('connect: missing host');
- self._host = '127.0.0.1';
- connect(self, self._host, options.port, 4);
-
} else {
- var host = options.host;
- var family = options.family || 4;
+ var host = options.host || 'localhost';
+ var family = options.family;
debug('connect: find host ' + host);
self._host = host;
- require('dns').lookup(host, family, function(err, ip, addressType) {
+ require('dns').lookup({
+ host: host,
+ family: family,
+ raw: true
+ }, function(err, results) {
+ if (!err) {
+ var ip = results[0].host;
+ var addressType = results[0].family;
+
+ var alternate = null;
+ for (var i = 1; i < results.length; i++) {
+ if (results[i].family === addressType)
+ continue;
+ alternate = results[i];
+ break;
+ }
+ }
+
+ // Opt-out of eyeballs, if env variable is set
+ if (process.env.NODE_NET_HAPPY_EYEBALLS == '0' && alternate) {
+ // Default to ipv4 as in v0.10
+ if (addressType === 6) {
+ ip = alternate.host;
+ addressType = 4;
+ }
+ alternate = null;
+ }
+
self.emit('lookup', err, ip, addressType);
+ if (alternate)
+ self.emit('lookup', err, alternate.host, alternate.family);
// It's possible we were destroyed while looking this up.
// XXX it would be great if we could cancel the promise returned by
@@ -931,8 +1025,6 @@ Socket.prototype.connect = function(options, cb) {
} else {
timers._unrefActive(self);
- addressType = addressType || 4;
-
// node_net.cc handles null host names graciously but user land
// expects remoteAddress to have a meaningful value
ip = ip || (addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1');
@@ -943,6 +1035,10 @@ Socket.prototype.connect = function(options, cb) {
addressType,
options.localAddress,
options.localPort);
+
+ // Try happy eyeballs
+ if (alternate)
+ tryEyeball(self, options, alternate);
}
});
}
@@ -966,15 +1062,21 @@ Socket.prototype.unref = function() {
function afterConnect(status, handle, req, readable, writable) {
var self = handle.owner;
+ // Kill eyeball on success anyway
+ if (status == 0) {
+ if (self._eyeball)
+ self._eyeball.destroy();
+ self._eyeball = null;
+ }
+
// callback may come after call to destroy
if (self.destroyed) {
return;
}
+ debug('afterConnect', status);
assert(handle === self._handle, 'handle != self._handle');
- debug('afterConnect');
-
assert.ok(self._connecting);
self._connecting = false;
@@ -991,8 +1093,8 @@ function afterConnect(status, handle, req, readable, writable) {
self.read(0);
} else {
- self._connecting = false;
- self._destroy(errnoException(status, 'connect'));
+ if (!switchEyeball(self))
+ self._destroy(errnoException(status, 'connect'));
}
}
diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc
index a7bfeda..f774c17 100644
--- a/src/cares_wrap.cc
+++ b/src/cares_wrap.cc
@@ -898,19 +898,7 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
Local<String> s = OneByteString(env->isolate(), ip);
results->Set(n, s);
n++;
- }
-
- // Increment
- address = address->ai_next;
- }
-
- // Iterate over the IPv6 responses putting them in the array.
- address = res;
- while (address) {
- assert(address->ai_socktype == SOCK_STREAM);
-
- // Ignore random ai_family types.
- if (address->ai_family == AF_INET6) {
+ } else if (address->ai_family == AF_INET6) {
// Juggle pointers
addr = reinterpret_cast<char*>(&(reinterpret_cast<struct sockaddr_in6*>(
address->ai_addr)->sin6_addr));
@@ -931,7 +919,6 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
address = address->ai_next;
}
-
argv[1] = results;
}
diff --git a/test/simple/test-asynclistener-error-net.js b/test/simple/test-asynclistener-error-net.js
index 26a337a..60f5619 100644
--- a/test/simple/test-asynclistener-error-net.js
+++ b/test/simple/test-asynclistener-error-net.js
@@ -91,7 +91,7 @@ var server = net.createServer(function(c) {
});
expectCaught += iter;
-server.listen(common.PORT, function() {
+server.listen(common.PORT, '127.0.0.1', function() {
for (var i = 0; i < iter; i++)
clientConnect();
});
diff --git a/test/simple/test-http-parser-free.js b/test/simple/test-http-parser-free.js
index 7b35781..1a71ea7 100644
--- a/test/simple/test-http-parser-free.js
+++ b/test/simple/test-http-parser-free.js
@@ -38,7 +38,7 @@ server.listen(common.PORT, function() {
if (!parser) {
parser = req.parser;
} else {
- assert.strictEqual(req.parser, parser);
+ assert(req.parser === parser);
}
if (++responses === N) {
diff --git a/test/simple/test-https-invalid-key.js b/test/simple/test-https-invalid-key.js
index 82e05a2..5b57dd4 100644
--- a/test/simple/test-https-invalid-key.js
+++ b/test/simple/test-https-invalid-key.js
@@ -46,7 +46,7 @@ server.on('clientError', function(err) {
server.close();
});
-server.listen(common.PORT, function() {
+server.listen(common.PORT, '127.0.0.1', function() {
var req = https.get({port: common.PORT}, function(res) {
assert(false);
});
diff --git a/test/simple/test-net-pause-resume-connecting.js b/test/simple/test-net-pause-resume-connecting.js
index 264dc96..63ad485 100644
--- a/test/simple/test-net-pause-resume-connecting.js
+++ b/test/simple/test-net-pause-resume-connecting.js
@@ -37,7 +37,7 @@ var server = net.createServer(function(conn) {
server.close();
});
-server.listen(common.PORT);
+server.listen(common.PORT, '127.0.0.1');
// Client 1
diff --git a/test/simple/test-net-pingpong.js b/test/simple/test-net-pingpong.js
index 1e7b668..0ee269a 100644
--- a/test/simple/test-net-pingpong.js
+++ b/test/simple/test-net-pingpong.js
@@ -26,6 +26,8 @@ var net = require('net');
var tests_run = 0;
+process.env.NODE_NET_HAPPY_EYEBALLS = '0';
+
function pingPongTest(port, host) {
var N = 1000;
var count = 0;
diff --git a/test/simple/test-net-remote-address-port.js b/test/simple/test-net-remote-address-port.js
index 5d1ae3c..3c2956f 100644
--- a/test/simple/test-net-remote-address-port.js
+++ b/test/simple/test-net-remote-address-port.js
@@ -28,7 +28,8 @@ var conns = 0, conns_closed = 0;
var server = net.createServer(function(socket) {
conns++;
- assert.equal('127.0.0.1', socket.remoteAddress);
+ assert.ok(/127\.0\.0\.1/.test(socket.remoteAddress) ||
+ '::1' === socket.remoteAddress);
assert.ok(socket.remotePort);
assert.notEqual(socket.remotePort, common.PORT);
socket.on('end', function() {
diff --git a/test/simple/test-net-server-max-connections.js b/test/simple/test-net-server-max-connections.js
index 43bc2fe..2bea08a 100644
--- a/test/simple/test-net-server-max-connections.js
+++ b/test/simple/test-net-server-max-connections.js
@@ -41,7 +41,7 @@ var server = net.createServer(function(connection) {
waits.push(function() { connection.end(); });
});
-server.listen(common.PORT, function() {
+server.listen(common.PORT, '127.0.0.1', function() {
makeConnection(0);
});
diff --git a/test/simple/test-net-stream.js b/test/simple/test-net-stream.js
index 9f2db51..77b8da5 100644
--- a/test/simple/test-net-stream.js
+++ b/test/simple/test-net-stream.js
@@ -57,7 +57,7 @@ var server = net.createServer(function(socket) {
}
socket.end();
-}).listen(common.PORT, function() {
+}).listen(common.PORT, '127.0.0.1', function() {
var conn = net.connect(common.PORT);
conn.on('data', function(buf) {
conn.pause();
diff --git a/test/simple/test-net-write-after-close.js b/test/simple/test-net-write-after-close.js
index 3b98bbc..fa63c6d 100644
--- a/test/simple/test-net-write-after-close.js
+++ b/test/simple/test-net-write-after-close.js
@@ -48,7 +48,7 @@ var server = net.createServer(function(socket) {
}, 250);
});
-server.listen(common.PORT, function() {
+server.listen(common.PORT, '127.0.0.1', function() {
var client = net.connect(common.PORT, function() {
client.end();
});
diff --git a/test/simple/test-pump-file2tcp-noexist.js b/test/simple/test-pump-file2tcp-noexist.js
index 8017db7..01e39d3 100644
--- a/test/simple/test-pump-file2tcp-noexist.js
+++ b/test/simple/test-pump-file2tcp-noexist.js
@@ -45,7 +45,7 @@ var server = net.createServer(function(stream) {
});
});
-server.listen(common.PORT, function() {
+server.listen(common.PORT, '127.0.0.1', function() {
var conn = net.createConnection(common.PORT);
conn.setEncoding('utf8');
conn.on('data', function(chunk) {
diff --git a/test/simple/test-pump-file2tcp.js b/test/simple/test-pump-file2tcp.js
index 60c0fdd..3d28e60 100644
--- a/test/simple/test-pump-file2tcp.js
+++ b/test/simple/test-pump-file2tcp.js
@@ -38,7 +38,7 @@ var server = net.createServer(function(stream) {
});
});
-server.listen(common.PORT, function() {
+server.listen(common.PORT, '127.0.0.1', function() {
var conn = net.createConnection(common.PORT);
conn.setEncoding('utf8');
conn.on('data', function(chunk) {
diff --git a/test/simple/test-socket-write-after-fin.js b/test/simple/test-socket-write-after-fin.js
index 88d780b..c7289d2 100644
--- a/test/simple/test-socket-write-after-fin.js
+++ b/test/simple/test-socket-write-after-fin.js
@@ -38,7 +38,7 @@ var server = net.createServer({ allowHalfOpen: true }, function(sock) {
server.close();
});
});
-server.listen(common.PORT);
+server.listen(common.PORT, '127.0.0.1');
var sock = net.connect(common.PORT);
sock.setEncoding('utf8');
diff --git a/test/simple/test-tls-over-http-tunnel.js b/test/simple/test-tls-over-http-tunnel.js
index 9fa82ae..480a157 100644
--- a/test/simple/test-tls-over-http-tunnel.js
+++ b/test/simple/test-tls-over-http-tunnel.js
@@ -91,7 +91,8 @@ var proxy = net.createServer(function(clientSocket) {
});
clientSocket.on('end', function() {
- serverSocket.destroy();
+ if (serverSocket)
+ serverSocket.destroy();
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment