Skip to content
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: cleanup connect logic and add ability to specify custom lookup function #1505

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/api/net.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ For TCP sockets, `options` argument should be an object which specifies:
- `localPort`: Local port to bind to for network connections.

- `family` : Version of IP stack. Defaults to `4`.

- `lookup` : Custom lookup function. Defaults to `dns.lookup`.

For local domain sockets, `options` argument should be an object which
specifies:
Expand Down
123 changes: 70 additions & 53 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -881,64 +881,81 @@ Socket.prototype.connect = function(options, cb) {
connect(self, options.path);

} else {
const dns = require('dns');
var host = options.host || 'localhost';
var port = 0;
var localAddress = options.localAddress;
var localPort = options.localPort;
var dnsopts = {
family: options.family,
hints: 0
};

if (localAddress && !exports.isIP(localAddress))
throw new TypeError('localAddress must be a valid IP: ' + localAddress);

if (localPort && typeof localPort !== 'number')
throw new TypeError('localPort should be a number: ' + localPort);

port = options.port;
if (typeof port !== 'undefined') {
if (typeof port !== 'number' && typeof port !== 'string')
throw new TypeError('port should be a number or string: ' + port);
if (!isLegalPort(port))
throw new RangeError('port should be >= 0 and < 65536: ' + port);
}
port |= 0;
lookupAndConnect(self, options);
}
return self;
};

if (dnsopts.family !== 4 && dnsopts.family !== 6)
dnsopts.hints = dns.ADDRCONFIG | dns.V4MAPPED;

debug('connect: find host ' + host);
debug('connect: dns options ' + dnsopts);
self._host = host;
dns.lookup(host, dnsopts, function(err, ip, addressType) {
self.emit('lookup', err, ip, addressType);
function lookupAndConnect(self, options) {
const dns = require('dns');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance this can be cached while you're at it? Unless you want to just move the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could cache it if that is what we want. I'm assuming it was not included until now for lazy loading though. Either way works for me

var host = options.host || 'localhost';
var port = options.port;
var localAddress = options.localAddress;
var localPort = options.localPort;

// It's possible we were destroyed while looking this up.
// XXX it would be great if we could cancel the promise returned by
// the look up.
if (!self._connecting) return;
if (localAddress && !exports.isIP(localAddress))
throw new TypeError('localAddress must be a valid IP: ' + localAddress);

if (err) {
// net.createConnection() creates a net.Socket object and
// immediately calls net.Socket.connect() on it (that's us).
// There are no event listeners registered yet so defer the
// error event to the next tick.
process.nextTick(connectErrorNT, self, err, options);
} else {
self._unrefTimer();
connect(self,
ip,
port,
addressType,
localAddress,
localPort);
}
});
if (localPort && typeof localPort !== 'number')
throw new TypeError('localPort should be a number: ' + localPort);

if (typeof port !== 'undefined') {
if (typeof port !== 'number' && typeof port !== 'string')
throw new TypeError('port should be a number or string: ' + port);
if (!isLegalPort(port))
throw new RangeError('port should be >= 0 and < 65536: ' + port);
}
return self;
};
port |= 0;

// If host is an IP, skip performing a lookup
// TODO(evanlucas) should we hot path this for localhost?
var addressType = exports.isIP(host);
if (addressType) {
connect(self, host, port, addressType, localAddress, localPort);
return;
}

if (options.lookup && typeof options.lookup !== 'function')
throw new TypeError('options.lookup should be a function.');

var dnsopts = {
family: options.family,
hints: 0
};

if (dnsopts.family !== 4 && dnsopts.family !== 6)
dnsopts.hints = dns.ADDRCONFIG | dns.V4MAPPED;

debug('connect: find host ' + host);
debug('connect: dns options ' + dnsopts);
self._host = host;
var lookup = options.lookup || dns.lookup;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add type check like

if (typeof lookup !== 'function') {
  throw new TypeError('lookup should be a function.');
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Added.

lookup(host, dnsopts, function(err, ip, addressType) {
self.emit('lookup', err, ip, addressType);

// It's possible we were destroyed while looking this up.
// XXX it would be great if we could cancel the promise returned by
// the look up.
if (!self._connecting) return;

if (err) {
// net.createConnection() creates a net.Socket object and
// immediately calls net.Socket.connect() on it (that's us).
// There are no event listeners registered yet so defer the
// error event to the next tick.
process.nextTick(connectErrorNT, self, err, options);
} else {
self._unrefTimer();
connect(self,
ip,
port,
addressType,
localAddress,
localPort);
}
});
}


function connectErrorNT(self, err, options) {
Expand Down
40 changes: 40 additions & 0 deletions test/parallel/test-net-dns-custom-lookup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
var common = require('../common');
var assert = require('assert');
var net = require('net');
var dns = require('dns');
var ok = false;

function check(addressType, cb) {
var server = net.createServer(function(client) {
client.end();
server.close();
cb && cb();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test, cb is always passed, so checking for it is sort of pointless.

});

var address = addressType === 4 ? '127.0.0.1' : '::1';
server.listen(common.PORT, address, function() {
net.connect({
port: common.PORT,
host: 'localhost',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this use common.localhostIPv4?

Edit: ah, thats unnecessary since we don't check the host. I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was actually something I am not sure of. Since we don't call lookup in the event that the host is an IP address, this test could fail of common.localhostIPv4 === '127.0.0.1'

lookup: lookup
}).on('lookup', function(err, ip, type) {
assert.equal(err, null);
assert.equal(ip, address);
assert.equal(type, addressType);
ok = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok is unused variable?? need to check the ok is true ??
If ok is unused, remove the variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, check was missing. Added

});
});

function lookup(host, dnsopts, cb) {
dnsopts.family = addressType;
dns.lookup(host, dnsopts, cb);
}
}

check(4, function() {
common.hasIPv6 && check(6);
});

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style fix: we don't put lines at the end of file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

process.on('exit', function() {
assert.ok(ok);
});
18 changes: 18 additions & 0 deletions test/parallel/test-net-dns-lookup-skip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var common = require('../common');
var assert = require('assert');
var net = require('net');

function check(addressType) {
var server = net.createServer(function(client) {
client.end();
server.close();
});

var address = addressType === 4 ? '127.0.0.1' : '::1';
server.listen(common.PORT, address, function() {
net.connect(common.PORT, address).on('lookup', assert.fail);
});
}

check(4);
common.hasIPv6 && check(6);
2 changes: 1 addition & 1 deletion test/parallel/test-net-dns-lookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var server = net.createServer(function(client) {
});

server.listen(common.PORT, '127.0.0.1', function() {
net.connect(common.PORT, '127.0.0.1').on('lookup', function(err, ip, type) {
net.connect(common.PORT, 'localhost').on('lookup', function(err, ip, type) {
assert.equal(err, null);
assert.equal(ip, '127.0.0.1');
assert.equal(type, '4');
Expand Down