From fe78073b3f7425d7f02a1c525ddb033e4dfa655b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 17 Oct 2017 12:05:12 +0800 Subject: [PATCH 1/3] test: implement common.skipIfNoIpv6Localhost Which run the callback with a local host that can be resolved to ::1 if available, otherwise skip the test. --- test/common/README.md | 8 ++++++ test/common/index.js | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/test/common/README.md b/test/common/README.md index 32a14382ff5c51..06c2268ebcb6b2 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -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>] diff --git a/test/common/index.js b/test/common/index.js index 838c7b60da21aa..a6eabe7a87984c 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -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'); @@ -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, From c3c65402387ea512763f3f25f1475fc38d99fb6c Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 17 Oct 2017 12:06:10 +0800 Subject: [PATCH 2/3] test: make the https connect test more robust Use common.skipIfNoIpv6Localhost --- .../test-https-connect-address-family.js | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/test/parallel/test-https-connect-address-family.js b/test/parallel/test-https-connect-address-family.js index 28d47b3a967424..74d22ed3872d39 100644 --- a/test/parallel/test-https-connect-address-family.js +++ b/test/parallel/test-https-connect-address-family.js @@ -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'), @@ -20,7 +19,7 @@ function runTest() { res.end(); })).listen(0, '::1', common.mustCall(function() { const options = { - host: 'localhost', + host: ipv6Host, port: this.address().port, family: 6, rejectUnauthorized: false, @@ -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'); }); From 8097bd8f06f5b5ac4cecbef68132bf20ebfc33a6 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 17 Oct 2017 12:06:23 +0800 Subject: [PATCH 3/3] test: refactor net.connect ipv6 options test Use common.skipIfNoIpv6Localhost --- .../parallel/test-net-connect-options-ipv6.js | 95 +++++++------------ 1 file changed, 34 insertions(+), 61 deletions(-) diff --git a/test/parallel/test-net-connect-options-ipv6.js b/test/parallel/test-net-connect-options-ipv6.js index aba07d9fcc8094..9df69ca7a8ac73 100644 --- a/test/parallel/test-net-connect-options-ipv6.js +++ b/test/parallel/test-net-connect-options-ipv6.js @@ -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; - }); -} + }); + })); + } +});