11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst common = require('../common');
41cb0ef41Sopenharmony_ciconst { parseDNSPacket, writeDNSPacket } = require('../common/dns');
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ciconst assert = require('assert');
71cb0ef41Sopenharmony_ciconst dgram = require('dgram');
81cb0ef41Sopenharmony_ciconst { Resolver } = require('dns');
91cb0ef41Sopenharmony_ciconst { request, createServer } = require('http');
101cb0ef41Sopenharmony_ciconst { setDefaultAutoSelectFamilyAttemptTimeout } = require('net');
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ci// Test that happy eyeballs algorithm is properly implemented when using HTTP.
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ci// Some of the windows machines in the CI need more time to establish connection
151cb0ef41Sopenharmony_cisetDefaultAutoSelectFamilyAttemptTimeout(common.platformTimeout(common.isWindows ? 1500 : 250));
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_cifunction _lookup(resolver, hostname, options, cb) {
181cb0ef41Sopenharmony_ci  resolver.resolve(hostname, 'ANY', (err, replies) => {
191cb0ef41Sopenharmony_ci    assert.notStrictEqual(options.family, 4);
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci    if (err) {
221cb0ef41Sopenharmony_ci      return cb(err);
231cb0ef41Sopenharmony_ci    }
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci    const hosts = replies
261cb0ef41Sopenharmony_ci      .map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }))
271cb0ef41Sopenharmony_ci      .sort((a, b) => b.family - a.family);
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ci    if (options.all === true) {
301cb0ef41Sopenharmony_ci      return cb(null, hosts);
311cb0ef41Sopenharmony_ci    }
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci    return cb(null, hosts[0].address, hosts[0].family);
341cb0ef41Sopenharmony_ci  });
351cb0ef41Sopenharmony_ci}
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_cifunction createDnsServer(ipv6Addr, ipv4Addr, cb) {
381cb0ef41Sopenharmony_ci  // Create a DNS server which replies with a AAAA and a A record for the same host
391cb0ef41Sopenharmony_ci  const socket = dgram.createSocket('udp4');
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci  socket.on('message', common.mustCall((msg, { address, port }) => {
421cb0ef41Sopenharmony_ci    const parsed = parseDNSPacket(msg);
431cb0ef41Sopenharmony_ci    const domain = parsed.questions[0].domain;
441cb0ef41Sopenharmony_ci    assert.strictEqual(domain, 'example.org');
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci    socket.send(writeDNSPacket({
471cb0ef41Sopenharmony_ci      id: parsed.id,
481cb0ef41Sopenharmony_ci      questions: parsed.questions,
491cb0ef41Sopenharmony_ci      answers: [
501cb0ef41Sopenharmony_ci        { type: 'AAAA', address: ipv6Addr, ttl: 123, domain: 'example.org' },
511cb0ef41Sopenharmony_ci        { type: 'A', address: ipv4Addr, ttl: 123, domain: 'example.org' },
521cb0ef41Sopenharmony_ci      ]
531cb0ef41Sopenharmony_ci    }), port, address);
541cb0ef41Sopenharmony_ci  }));
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci  socket.bind(0, () => {
571cb0ef41Sopenharmony_ci    const resolver = new Resolver();
581cb0ef41Sopenharmony_ci    resolver.setServers([`127.0.0.1:${socket.address().port}`]);
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci    cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) });
611cb0ef41Sopenharmony_ci  });
621cb0ef41Sopenharmony_ci}
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci// Test that IPV4 is reached if IPV6 is not reachable
651cb0ef41Sopenharmony_ci{
661cb0ef41Sopenharmony_ci  createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
671cb0ef41Sopenharmony_ci    const ipv4Server = createServer(common.mustCall((_, res) => {
681cb0ef41Sopenharmony_ci      res.writeHead(200, { Connection: 'close' });
691cb0ef41Sopenharmony_ci      res.end('response-ipv4');
701cb0ef41Sopenharmony_ci    }));
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci    ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
731cb0ef41Sopenharmony_ci      request(
741cb0ef41Sopenharmony_ci        `http://example.org:${ipv4Server.address().port}/`,
751cb0ef41Sopenharmony_ci        {
761cb0ef41Sopenharmony_ci          lookup,
771cb0ef41Sopenharmony_ci          autoSelectFamily: true,
781cb0ef41Sopenharmony_ci        },
791cb0ef41Sopenharmony_ci        (res) => {
801cb0ef41Sopenharmony_ci          assert.strictEqual(res.statusCode, 200);
811cb0ef41Sopenharmony_ci          res.setEncoding('utf-8');
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci          let response = '';
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci          res.on('data', (chunk) => {
861cb0ef41Sopenharmony_ci            response += chunk;
871cb0ef41Sopenharmony_ci          });
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci          res.on('end', common.mustCall(() => {
901cb0ef41Sopenharmony_ci            assert.strictEqual(response, 'response-ipv4');
911cb0ef41Sopenharmony_ci            ipv4Server.close();
921cb0ef41Sopenharmony_ci            dnsServer.close();
931cb0ef41Sopenharmony_ci          }));
941cb0ef41Sopenharmony_ci        }
951cb0ef41Sopenharmony_ci      ).end();
961cb0ef41Sopenharmony_ci    }));
971cb0ef41Sopenharmony_ci  }));
981cb0ef41Sopenharmony_ci}
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci// Test that IPV4 is NOT reached if IPV6 is reachable
1011cb0ef41Sopenharmony_ciif (common.hasIPv6) {
1021cb0ef41Sopenharmony_ci  createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
1031cb0ef41Sopenharmony_ci    const ipv4Server = createServer(common.mustNotCall((_, res) => {
1041cb0ef41Sopenharmony_ci      res.writeHead(200, { Connection: 'close' });
1051cb0ef41Sopenharmony_ci      res.end('response-ipv4');
1061cb0ef41Sopenharmony_ci    }));
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci    const ipv6Server = createServer(common.mustCall((_, res) => {
1091cb0ef41Sopenharmony_ci      res.writeHead(200, { Connection: 'close' });
1101cb0ef41Sopenharmony_ci      res.end('response-ipv6');
1111cb0ef41Sopenharmony_ci    }));
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci    ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
1141cb0ef41Sopenharmony_ci      const port = ipv4Server.address().port;
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci      ipv6Server.listen(port, '::1', common.mustCall(() => {
1171cb0ef41Sopenharmony_ci        request(
1181cb0ef41Sopenharmony_ci          `http://example.org:${ipv4Server.address().port}/`,
1191cb0ef41Sopenharmony_ci          {
1201cb0ef41Sopenharmony_ci            lookup,
1211cb0ef41Sopenharmony_ci            autoSelectFamily: true,
1221cb0ef41Sopenharmony_ci          },
1231cb0ef41Sopenharmony_ci          (res) => {
1241cb0ef41Sopenharmony_ci            assert.strictEqual(res.statusCode, 200);
1251cb0ef41Sopenharmony_ci            res.setEncoding('utf-8');
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_ci            let response = '';
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci            res.on('data', (chunk) => {
1301cb0ef41Sopenharmony_ci              response += chunk;
1311cb0ef41Sopenharmony_ci            });
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci            res.on('end', common.mustCall(() => {
1341cb0ef41Sopenharmony_ci              assert.strictEqual(response, 'response-ipv6');
1351cb0ef41Sopenharmony_ci              ipv4Server.close();
1361cb0ef41Sopenharmony_ci              ipv6Server.close();
1371cb0ef41Sopenharmony_ci              dnsServer.close();
1381cb0ef41Sopenharmony_ci            }));
1391cb0ef41Sopenharmony_ci          }
1401cb0ef41Sopenharmony_ci        ).end();
1411cb0ef41Sopenharmony_ci      }));
1421cb0ef41Sopenharmony_ci    }));
1431cb0ef41Sopenharmony_ci  }));
1441cb0ef41Sopenharmony_ci}
145