summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/node_compat/config.jsonc2
-rw-r--r--tests/node_compat/runner/TODO.md1
-rw-r--r--tests/node_compat/test/common/index.js1
-rw-r--r--tests/node_compat/test/parallel/test-net-autoselectfamily.js312
-rw-r--r--tests/unit_node/http2_test.ts5
5 files changed, 320 insertions, 1 deletions
diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc
index 16951d9ed..664adaedf 100644
--- a/tests/node_compat/config.jsonc
+++ b/tests/node_compat/config.jsonc
@@ -77,6 +77,7 @@
"test-fs-rmdir-recursive.js",
"test-fs-write-file.js",
"test-http-url.parse-https.request.js",
+ "test-net-autoselectfamily.js",
"test-net-better-error-messages-path.js",
"test-net-connect-buffer.js",
"test-net-connect-buffer2.js",
@@ -404,6 +405,7 @@
"test-http-url.parse-only-support-http-https-protocol.js",
"test-icu-transcode.js",
"test-net-access-byteswritten.js",
+ "test-net-autoselectfamily.js",
"test-net-better-error-messages-listen-path.js",
"test-net-better-error-messages-path.js",
"test-net-better-error-messages-port-hostname.js",
diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md
index 231a4f62c..27c2ef3e7 100644
--- a/tests/node_compat/runner/TODO.md
+++ b/tests/node_compat/runner/TODO.md
@@ -1767,7 +1767,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
- [parallel/test-net-autoselectfamily-commandline-option.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-autoselectfamily-commandline-option.js)
- [parallel/test-net-autoselectfamily-default.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-autoselectfamily-default.js)
- [parallel/test-net-autoselectfamily-ipv4first.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-autoselectfamily-ipv4first.js)
-- [parallel/test-net-autoselectfamily.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-autoselectfamily.js)
- [parallel/test-net-better-error-messages-listen.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-better-error-messages-listen.js)
- [parallel/test-net-binary.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-binary.js)
- [parallel/test-net-bind-twice.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-bind-twice.js)
diff --git a/tests/node_compat/test/common/index.js b/tests/node_compat/test/common/index.js
index d2165aecd..d358ffce5 100644
--- a/tests/node_compat/test/common/index.js
+++ b/tests/node_compat/test/common/index.js
@@ -473,6 +473,7 @@ const pwdCommand = isWindows ?
module.exports = {
allowGlobals,
+ defaultAutoSelectFamilyAttemptTimeout: 2500,
expectsError,
expectWarning,
getArrayBufferViews,
diff --git a/tests/node_compat/test/parallel/test-net-autoselectfamily.js b/tests/node_compat/test/parallel/test-net-autoselectfamily.js
new file mode 100644
index 000000000..3b520e6c8
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-net-autoselectfamily.js
@@ -0,0 +1,312 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+const common = require('../common');
+const { parseDNSPacket, writeDNSPacket } = require('../common/dns');
+
+const assert = require('assert');
+const dgram = require('dgram');
+const { Resolver } = require('dns');
+const { createConnection, createServer } = require('net');
+
+// Test that happy eyeballs algorithm is properly implemented.
+
+// Purposely not using setDefaultAutoSelectFamilyAttemptTimeout here to test the
+// parameter is correctly used in options.
+//
+// Some of the machines in the CI need more time to establish connection
+const autoSelectFamilyAttemptTimeout = common.defaultAutoSelectFamilyAttemptTimeout;
+
+function _lookup(resolver, hostname, options, cb) {
+ resolver.resolve(hostname, 'ANY', (err, replies) => {
+ assert.notStrictEqual(options.family, 4);
+
+ 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(ipv6Addrs, ipv4Addrs, cb) {
+ if (!Array.isArray(ipv6Addrs)) {
+ ipv6Addrs = [ipv6Addrs];
+ }
+
+ if (!Array.isArray(ipv4Addrs)) {
+ ipv4Addrs = [ipv4Addrs];
+ }
+
+ // Create a DNS server which replies with a AAAA and a A record for the same host
+ const socket = dgram.createSocket('udp4');
+
+ // TODO(kt3k): We use common.mustCallAtLeast instead of common.mustCall
+ // because Deno sends multiple requests to the DNS server.
+ // This can be addressed if Deno.resolveDns supports ANY record type.
+ // See https://github.com/denoland/deno/issues/14492
+ socket.on('message', common.mustCallAtLeast((msg, { address, port }) => {
+ const parsed = parseDNSPacket(msg);
+ const domain = parsed.questions[0].domain;
+ assert.strictEqual(domain, 'example.org');
+
+ socket.send(writeDNSPacket({
+ id: parsed.id,
+ questions: parsed.questions,
+ answers: [
+ ...ipv6Addrs.map((address) => ({ type: 'AAAA', address, ttl: 123, domain: 'example.org' })),
+ ...ipv4Addrs.map((address) => ({ type: 'A', address, ttl: 123, domain: 'example.org' })),
+ ]
+ }), port, address);
+ }));
+
+ socket.bind(0, () => {
+ const resolver = new Resolver();
+ resolver.setServers([`127.0.0.1:${socket.address().port}`]);
+
+ cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) });
+ });
+}
+
+// Test that IPV4 is reached if IPV6 is not reachable
+{
+ createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
+ const ipv4Server = createServer((socket) => {
+ socket.on('data', common.mustCall(() => {
+ socket.write('response-ipv4');
+ socket.end();
+ }));
+ });
+
+ ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
+ const port = ipv4Server.address().port;
+
+ const connection = createConnection({
+ host: 'example.org',
+ port: port,
+ lookup,
+ autoSelectFamily: true,
+ autoSelectFamilyAttemptTimeout,
+ });
+
+ let response = '';
+ connection.setEncoding('utf-8');
+
+ connection.on('ready', common.mustCall(() => {
+ assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`, `127.0.0.1:${port}`]);
+ }));
+
+ connection.on('data', (chunk) => {
+ response += chunk;
+ });
+
+ connection.on('end', common.mustCall(() => {
+ assert.strictEqual(response, 'response-ipv4');
+ ipv4Server.close();
+ dnsServer.close();
+ }));
+
+ connection.write('request');
+ }));
+ }));
+}
+
+// Test that only the last successful connection is established.
+{
+ createDnsServer(
+ ['2606:4700::6810:85e5', '2606:4700::6810:84e5', "::1"],
+ // TODO(kt3k): Comment out ipv4 addresses to make the test pass faster.
+ // Enable this when Deno.connect() call becomes cancellable.
+ // See https://github.com/denoland/deno/issues/26819
+ // ['104.20.22.46', '104.20.23.46', '127.0.0.1'],
+ ['127.0.0.1'],
+ common.mustCall(function({ dnsServer, lookup }) {
+ const ipv4Server = createServer((socket) => {
+ socket.on('data', common.mustCall(() => {
+ socket.write('response-ipv4');
+ socket.end();
+ }));
+ });
+
+ ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
+ const port = ipv4Server.address().port;
+
+ const connection = createConnection({
+ host: 'example.org',
+ port: port,
+ lookup,
+ autoSelectFamily: true,
+ autoSelectFamilyAttemptTimeout,
+ });
+
+ let response = '';
+ connection.setEncoding('utf-8');
+
+ connection.on('ready', common.mustCall(() => {
+ assert.deepStrictEqual(
+ connection.autoSelectFamilyAttemptedAddresses,
+ [
+ `2606:4700::6810:85e5:${port}`,
+ `104.20.22.46:${port}`,
+ `2606:4700::6810:84e5:${port}`,
+ `104.20.23.46:${port}`,
+ `::1:${port}`,
+ `127.0.0.1:${port}`,
+ ]
+ );
+ }));
+
+ connection.on('data', (chunk) => {
+ response += chunk;
+ });
+
+ connection.on('end', common.mustCall(() => {
+ assert.strictEqual(response, 'response-ipv4');
+ ipv4Server.close();
+ dnsServer.close();
+ }));
+
+ connection.write('request');
+ }));
+ })
+ );
+}
+
+// Test that IPV4 is NOT reached if IPV6 is reachable
+if (common.hasIPv6) {
+ createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
+ const ipv4Server = createServer((socket) => {
+ socket.on('data', common.mustNotCall(() => {
+ socket.write('response-ipv4');
+ socket.end();
+ }));
+ });
+
+ const ipv6Server = createServer((socket) => {
+ socket.on('data', common.mustCall(() => {
+ socket.write('response-ipv6');
+ socket.end();
+ }));
+ });
+
+ ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
+ const port = ipv4Server.address().port;
+
+ ipv6Server.listen(port, '::1', common.mustCall(() => {
+ const connection = createConnection({
+ host: 'example.org',
+ port,
+ lookup,
+ autoSelectFamily: true,
+ autoSelectFamilyAttemptTimeout,
+ });
+
+ let response = '';
+ connection.setEncoding('utf-8');
+
+ connection.on('ready', common.mustCall(() => {
+ assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`]);
+ }));
+
+ connection.on('data', (chunk) => {
+ response += chunk;
+ });
+
+ connection.on('end', common.mustCall(() => {
+ assert.strictEqual(response, 'response-ipv6');
+ ipv4Server.close();
+ ipv6Server.close();
+ dnsServer.close();
+ }));
+
+ connection.write('request');
+ }));
+ }));
+ }));
+}
+
+// Test that when all errors are returned when no connections succeeded
+{
+ createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
+ const connection = createConnection({
+ host: 'example.org',
+ port: 10,
+ lookup,
+ autoSelectFamily: true,
+ autoSelectFamilyAttemptTimeout,
+ });
+
+ connection.on('ready', common.mustNotCall());
+ connection.on('error', common.mustCall((error) => {
+ assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, ['::1:10', '127.0.0.1:10']);
+ assert.strictEqual(error.constructor.name, 'AggregateError');
+ assert.strictEqual(error.errors.length, 2);
+
+ const errors = error.errors.map((e) => e.message);
+ assert.ok(errors.includes('connect ECONNREFUSED 127.0.0.1:10'));
+
+ if (common.hasIPv6) {
+ assert.ok(errors.includes('connect ECONNREFUSED ::1:10'));
+ }
+
+ dnsServer.close();
+ }));
+ }));
+}
+
+// Test that the option can be disabled
+{
+ createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
+ const ipv4Server = createServer((socket) => {
+ socket.on('data', common.mustCall(() => {
+ socket.write('response-ipv4');
+ socket.end();
+ }));
+ });
+
+ ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
+ const port = ipv4Server.address().port;
+
+ const connection = createConnection({
+ host: 'example.org',
+ port,
+ lookup,
+ autoSelectFamily: false,
+ });
+
+ connection.on('ready', common.mustNotCall());
+ connection.on('error', common.mustCall((error) => {
+ assert.strictEqual(connection.autoSelectFamilyAttemptedAddresses, undefined);
+
+ if (common.hasIPv6) {
+ assert.strictEqual(error.code, 'ECONNREFUSED');
+ assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
+ } else if (error.code === 'EAFNOSUPPORT') {
+ assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
+ } else if (error.code === 'EUNATCH') {
+ assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
+ } else {
+ assert.strictEqual(error.code, 'EADDRNOTAVAIL');
+ assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
+ }
+
+ ipv4Server.close();
+ dnsServer.close();
+ }));
+ }));
+ }));
+}
diff --git a/tests/unit_node/http2_test.ts b/tests/unit_node/http2_test.ts
index 7473a487a..c540c90f7 100644
--- a/tests/unit_node/http2_test.ts
+++ b/tests/unit_node/http2_test.ts
@@ -10,6 +10,11 @@ import * as net from "node:net";
import { assert, assertEquals } from "@std/assert";
import { curlRequest } from "../unit/test_util.ts";
+// Increase the timeout for the auto select family to avoid flakiness
+net.setDefaultAutoSelectFamilyAttemptTimeout(
+ net.getDefaultAutoSelectFamilyAttemptTimeout() * 30,
+);
+
for (const url of ["http://localhost:4246", "https://localhost:4247"]) {
Deno.test(`[node/http2 client] ${url}`, {
ignore: Deno.build.os === "windows",