summaryrefslogtreecommitdiffstats
path: root/test/autoselectfamily.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/autoselectfamily.js')
-rw-r--r--test/autoselectfamily.js198
1 files changed, 198 insertions, 0 deletions
diff --git a/test/autoselectfamily.js b/test/autoselectfamily.js
new file mode 100644
index 0000000..0b44a3e
--- /dev/null
+++ b/test/autoselectfamily.js
@@ -0,0 +1,198 @@
+'use strict'
+
+const { test, skip } = require('tap')
+const dgram = require('dgram')
+const { Resolver } = require('dns')
+const dnsPacket = require('dns-packet')
+const { createServer } = require('http')
+const { Client, Agent, request } = require('..')
+const { nodeHasAutoSelectFamily } = require('../lib/core/util')
+
+/*
+ * IMPORTANT
+ *
+ * As only some version of Node have autoSelectFamily enabled by default (>= 20), make sure the option is always
+ * explicitly passed in tests in this file to avoid compatibility problems across release lines.
+ *
+ */
+
+if (!nodeHasAutoSelectFamily) {
+ skip('autoSelectFamily is not supportee')
+ process.exit()
+}
+
+function _lookup (resolver, hostname, options, cb) {
+ resolver.resolve(hostname, 'ANY', (err, replies) => {
+ if (err) {
+ return cb(err)
+ }
+
+ const hosts = replies
+ .map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }))
+ .sort((a, b) => b.family - a.family)
+
+ if (options.all === true) {
+ return cb(null, hosts)
+ }
+
+ return cb(null, hosts[0].address, hosts[0].family)
+ })
+}
+
+function createDnsServer (ipv6Addr, ipv4Addr, cb) {
+ // Create a DNS server which replies with a AAAA and a A record for the same host
+ const socket = dgram.createSocket('udp4')
+
+ socket.on('message', (msg, { address, port }) => {
+ const parsed = dnsPacket.decode(msg)
+
+ const response = dnsPacket.encode({
+ type: 'answer',
+ id: parsed.id,
+ questions: parsed.questions,
+ answers: [
+ { type: 'AAAA', class: 'IN', name: 'example.org', data: '::1', ttl: 123 },
+ { type: 'A', class: 'IN', name: 'example.org', data: '127.0.0.1', ttl: 123 }
+ ]
+ })
+
+ socket.send(response, port, address)
+ })
+
+ socket.bind(0, () => {
+ const resolver = new Resolver()
+ resolver.setServers([`127.0.0.1:${socket.address().port}`])
+
+ cb(null, { dnsServer: socket, lookup: _lookup.bind(null, resolver) })
+ })
+}
+
+test('with autoSelectFamily enable the request succeeds when using request', (t) => {
+ t.plan(3)
+
+ createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {
+ const server = createServer((req, res) => {
+ res.end('hello')
+ })
+
+ t.teardown(() => {
+ server.close()
+ dnsServer.close()
+ })
+
+ server.listen(0, '127.0.0.1', () => {
+ const agent = new Agent({ connect: { lookup }, autoSelectFamily: true })
+
+ request(
+ `http://example.org:${server.address().port}/`, {
+ method: 'GET',
+ dispatcher: agent
+ }, (err, { statusCode, body }) => {
+ t.error(err)
+
+ let response = Buffer.alloc(0)
+
+ body.on('data', chunk => {
+ response = Buffer.concat([response, chunk])
+ })
+
+ body.on('end', () => {
+ t.strictSame(statusCode, 200)
+ t.strictSame(response.toString('utf-8'), 'hello')
+ })
+ })
+ })
+ })
+})
+
+test('with autoSelectFamily enable the request succeeds when using a client', (t) => {
+ t.plan(3)
+
+ createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {
+ const server = createServer((req, res) => {
+ res.end('hello')
+ })
+
+ t.teardown(() => {
+ server.close()
+ dnsServer.close()
+ })
+
+ server.listen(0, '127.0.0.1', () => {
+ const client = new Client(`http://example.org:${server.address().port}`, { connect: { lookup }, autoSelectFamily: true })
+
+ t.teardown(client.destroy.bind(client))
+
+ client.request({
+ path: '/',
+ method: 'GET'
+ }, (err, { statusCode, body }) => {
+ t.error(err)
+
+ let response = Buffer.alloc(0)
+
+ body.on('data', chunk => {
+ response = Buffer.concat([response, chunk])
+ })
+
+ body.on('end', () => {
+ t.strictSame(statusCode, 200)
+ t.strictSame(response.toString('utf-8'), 'hello')
+ })
+ })
+ })
+ })
+})
+
+test('with autoSelectFamily disabled the request fails when using request', (t) => {
+ t.plan(1)
+
+ createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {
+ const server = createServer((req, res) => {
+ res.end('hello')
+ })
+
+ t.teardown(() => {
+ server.close()
+ dnsServer.close()
+ })
+
+ server.listen(0, '127.0.0.1', () => {
+ const agent = new Agent({ connect: { lookup, autoSelectFamily: false } })
+
+ request(`http://example.org:${server.address().port}`, {
+ method: 'GET',
+ dispatcher: agent
+ }, (err, { statusCode, body }) => {
+ t.ok(['ECONNREFUSED', 'EAFNOSUPPORT'].includes(err.code))
+ })
+ })
+ })
+})
+
+test('with autoSelectFamily disabled the request fails when using a client', (t) => {
+ t.plan(1)
+
+ createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {
+ const server = createServer((req, res) => {
+ res.end('hello')
+ })
+
+ t.teardown(() => {
+ server.close()
+ dnsServer.close()
+ })
+
+ server.listen(0, '127.0.0.1', () => {
+ const client = new Client(`http://example.org:${server.address().port}`, { connect: { lookup, autoSelectFamily: false } })
+ t.teardown(client.destroy.bind(client))
+
+ client.request({
+ path: '/',
+ method: 'GET'
+ }, (err, { statusCode, body }) => {
+ t.ok(['ECONNREFUSED', 'EAFNOSUPPORT'].includes(err.code))
+ })
+ })
+ })
+})