Skip to content

test: implement common.skipIfNoIpv6Localhost #16248

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

Closed
wants to merge 3 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
8 changes: 8 additions & 0 deletions test/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,14 @@ was disabled at compile time.
Skip the rest of the tests in the current file when the Node.js executable
was compiled with a pointer size smaller than 64 bits.

### skipIfNoIpv6Localhost(cb)

* `cb` [<Function>]
* `ipv6Host` [<String>] A local IPv6 host that can be resolved to `::1`.

Run the `cb` only if there is a local IPv6 host that can be resolved to `::1`,
otherwise call `common.skip()`.

### spawnPwd(options)
* `options` [<Object>]
* return [<Object>]
Expand Down
59 changes: 59 additions & 0 deletions test/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const path = require('path');
const fs = require('fs');
const assert = require('assert');
const os = require('os');
const dns = require('dns');
const { exec, execSync, spawn, spawnSync } = require('child_process');
const stream = require('stream');
const util = require('util');
Expand Down Expand Up @@ -765,6 +766,64 @@ exports.skipIf32Bits = function skipIf32Bits() {
}
};

exports.skipIfNoIpv6Localhost = function skipIfNoIpv6Localhost(cb) {
if (!exports.hasIPv6) {
exports.skip('no IPv6 support');
}

const hosts = exports.localIPv6Hosts;
let localhostTries = 10; // Try to resolve "localhost" 10 times

function tryResolve(hostIdx) {
const host = hosts[hostIdx];

dns.lookup(host, { family: 6, all: true }, (err, addresses) => {
// ENOTFOUND means we don't have the requested address. In this
// case we try the next one in the list and if we run out of
// candidates we assume IPv6 is not supported on the
// machine and skip the test.
// EAI_AGAIN means we tried to remotely resolve the address and
// timed out or hit some intermittent connectivity issue with the
// dns server. Although we are looking for local loopback addresses
// we may go remote since the list we search includes addresses that
// cover more than is available on any one distribution. The
// net is that if we get an EAI_AGAIN we were looking for an
// address which does not exist in this distribution so the error
// is not significant and we should just move on and try the
// next address in the list.
if (err) {
const isResolutionErr = (err.syscall === 'getaddrinfo') &&
((err.code === 'ENOTFOUND') || (err.code === 'EAI_AGAIN'));

if (!isResolutionErr) {
throw err;
}

if (host !== 'localhost') {
// Try again with the next available host
if (hostIdx + 1 < hosts.length) {
return tryResolve(hostIdx + 1);
}
} else if (localhostTries > 0) { // Try again with localhost
localhostTries--;
return tryResolve(hostIdx);
}

exports.skip('No available local host that resolves to ::1');
}

// Success, return the host that can be resolved to ::1
if (addresses.some((val) => val.address === '::1')) {
return cb(host);
}

exports.skip('No available local host that resolves to ::1');
});
}

tryResolve(0);
};

const arrayBufferViews = [
Int8Array,
Uint8Array,
Expand Down
31 changes: 8 additions & 23 deletions test/parallel/test-https-connect-address-family.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
'use strict';

// This test that the family option of https.get is honored.

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');

if (!common.hasIPv6)
common.skip('no IPv6 support');

const assert = require('assert');
const fixtures = require('../common/fixtures');
const https = require('https');
const dns = require('dns');
common.skipIfNoIpv6Localhost((ipv6Host) => {
const assert = require('assert');
const https = require('https');
const fixtures = require('../common/fixtures');

function runTest() {
https.createServer({
cert: fixtures.readKey('agent1-cert.pem'),
key: fixtures.readKey('agent1-key.pem'),
Expand All @@ -20,7 +19,7 @@ function runTest() {
res.end();
})).listen(0, '::1', common.mustCall(function() {
const options = {
host: 'localhost',
host: ipv6Host,
Copy link
Contributor

Choose a reason for hiding this comment

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

given that // This test that the family option of https.get is honored. would it be correct to replace this with '::1' since this what the server bind to?

Copy link
Member Author

@joyeecheung joyeecheung Nov 12, 2017

Choose a reason for hiding this comment

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

@refack I think replacing it with a ip would skip the family option handling altogether. The family option is used when performing a lookup, so if the connection doesn't even need to lookup then it won't be tested. In net.js:

  // If host is an IP, skip performing a lookup
  var addressType = cares.isIP(host);
  if (addressType) {
    nextTick(self[async_id_symbol], function() {
      if (self.connecting)
        internalConnect(self, host, port, addressType, localAddress, localPort);
    });
    return;
  }

....

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

port: this.address().port,
family: 6,
rejectUnauthorized: false,
Expand All @@ -31,18 +30,4 @@ function runTest() {
this.destroy();
}));
}));
}

dns.lookup('localhost', { family: 6, all: true }, (err, addresses) => {
if (err) {
if (err.code === 'ENOTFOUND' || err.code === 'EAI_AGAIN')
common.skip('localhost does not resolve to ::1');

throw err;
}

if (addresses.some((val) => val.address === '::1'))
runTest();
else
common.skip('localhost does not resolve to ::1');
});
95 changes: 34 additions & 61 deletions test/parallel/test-net-connect-options-ipv6.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,71 +19,44 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// This tests that the family option of net.connect is hornored.

'use strict';
const common = require('../common');
if (!common.hasIPv6)
common.skip('no IPv6 support');

const assert = require('assert');
const net = require('net');

const hosts = common.localIPv6Hosts;
let hostIdx = 0;
let host = hosts[hostIdx];
let localhostTries = 10;
common.skipIfNoIpv6Localhost((ipv6Host) => {
const assert = require('assert');
const net = require('net');

const server = net.createServer({ allowHalfOpen: true }, function(socket) {
socket.resume();
socket.on('end', common.mustCall());
socket.end();
});
const server = net.createServer({
allowHalfOpen: true
}, common.mustCall((socket) => {
assert.strictEqual('::1', socket.remoteAddress);
socket.resume();
socket.on('end', common.mustCall());
socket.end();
}));

server.listen(0, '::1', tryConnect);
server.listen(0, '::1', common.mustCall(tryConnect));

function tryConnect() {
const client = net.connect({
host: host,
port: server.address().port,
family: 6,
allowHalfOpen: true
}, function() {
console.error('client connect cb');
client.resume();
client.on('end', common.mustCall(function() {
setTimeout(function() {
assert(client.writable);
client.end();
}, 10);
}));
client.on('close', function() {
server.close();
});
}).on('error', function(err) {
// ENOTFOUND means we don't have the requested address. In this
// case we try the next one in the list and if we run out of
// candidates we assume IPv6 is not supported on the
// machine and skip the test.
// EAI_AGAIN means we tried to remotely resolve the address and
// timed out or hit some intermittent connectivity issue with the
// dns server. Although we are looking for local loopback addresses
// we may go remote since the list we search includes addresses that
// cover more than is available on any one distribution. The
// net is that if we get an EAI_AGAIN we were looking for an
// address which does not exist in this distribution so the error
// is not significant and we should just move on and try the
// next address in the list.
if ((err.syscall === 'getaddrinfo') && ((err.code === 'ENOTFOUND') ||
(err.code === 'EAI_AGAIN'))) {
if (host !== 'localhost' || --localhostTries === 0)
host = hosts[++hostIdx];
if (host)
tryConnect();
else {
function tryConnect() {
const client = net.connect({
host: ipv6Host,
port: server.address().port,
family: 6,
allowHalfOpen: true
}, common.mustCall(() => {
console.error('client connect cb');
client.resume();
client.on('end', common.mustCall(function() {
setTimeout(function() {
assert(client.writable);
client.end();
}, 10);
}));
client.on('close', function() {
server.close();
common.skip('no IPv6 localhost support');
}
return;
}
throw err;
});
}
});
}));
}
});