Skip to content

Commit

Permalink
dns: add a cancel() method to the promise Resolver
Browse files Browse the repository at this point in the history
PR-URL: #33099
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
szmarczak authored and codebytere committed Nov 22, 2020
1 parent 9405cdd commit 6349b1d
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 13 deletions.
8 changes: 8 additions & 0 deletions doc/api/dns.md
Expand Up @@ -730,6 +730,14 @@ The following methods from the `dnsPromises` API are available:
* [`resolver.reverse()`][`dnsPromises.reverse()`]
* [`resolver.setServers()`][`dnsPromises.setServers()`]

### `resolver.cancel()`
<!-- YAML
added: REPLACEME
-->

Cancel all outstanding DNS queries made by this resolver. The corresponding
promises will be rejected with an error with code `ECANCELLED`.

### `dnsPromises.getServers()`
<!-- YAML
added: v10.6.0
Expand Down
1 change: 1 addition & 0 deletions lib/internal/dns/promises.js
Expand Up @@ -217,6 +217,7 @@ class Resolver {

Resolver.prototype.getServers = CallbackResolver.prototype.getServers;
Resolver.prototype.setServers = CallbackResolver.prototype.setServers;
Resolver.prototype.cancel = CallbackResolver.prototype.cancel;
Resolver.prototype.setLocalAddress = CallbackResolver.prototype.setLocalAddress;
Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny');
Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA');
Expand Down
65 changes: 65 additions & 0 deletions test/parallel/test-dns-channel-cancel-promise.js
@@ -0,0 +1,65 @@
'use strict';
const common = require('../common');
const { promises: dnsPromises } = require('dns');
const assert = require('assert');
const dgram = require('dgram');

const server = dgram.createSocket('udp4');
const resolver = new dnsPromises.Resolver();

const addMessageListener = () => {
server.removeAllListeners('message');

server.once('message', () => {
server.once('message', common.mustNotCall);

resolver.cancel();
});
};

server.bind(0, common.mustCall(async () => {
resolver.setServers([`127.0.0.1:${server.address().port}`]);

addMessageListener();

// Single promise
{
const hostname = 'example0.org';

await assert.rejects(
resolver.resolve4(hostname),
{
code: 'ECANCELLED',
syscall: 'queryA',
hostname
}
);
}

addMessageListener();

// Multiple promises
{
const assertions = [];
const assertionCount = 10;

for (let i = 1; i <= assertionCount; i++) {
const hostname = `example${i}.org`;

assertions.push(
assert.rejects(
resolver.resolve4(hostname),
{
code: 'ECANCELLED',
syscall: 'queryA',
hostname: hostname
}
)
);
}

await Promise.all(assertions);
}

server.close();
}));
49 changes: 36 additions & 13 deletions test/parallel/test-dns-channel-cancel.js
@@ -1,28 +1,51 @@
'use strict';
const common = require('../common');
const dnstools = require('../common/dns');
const { Resolver } = require('dns');
const assert = require('assert');
const dgram = require('dgram');

const server = dgram.createSocket('udp4');
const resolver = new Resolver();

server.bind(0, common.mustCall(() => {
const desiredQueries = 11;
let finishedQueries = 0;

const addMessageListener = () => {
server.removeAllListeners('message');

server.once('message', () => {
server.once('message', common.mustNotCall);

resolver.cancel();
});
};

server.bind(0, common.mustCall(async () => {
resolver.setServers([`127.0.0.1:${server.address().port}`]);
resolver.resolve4('example.org', common.mustCall((err, res) => {

const callback = common.mustCall((err, res) => {
assert.strictEqual(err.code, 'ECANCELLED');
assert.strictEqual(err.syscall, 'queryA');
assert.strictEqual(err.hostname, 'example.org');
server.close();
}));
}));
assert.strictEqual(err.hostname, `example${finishedQueries}.org`);

finishedQueries++;
if (finishedQueries === desiredQueries) {
server.close();
}
}, desiredQueries);

const next = (...args) => {
callback(...args);

addMessageListener();

server.on('message', common.mustCall((msg, { address, port }) => {
const parsed = dnstools.parseDNSPacket(msg);
const domain = parsed.questions[0].domain;
assert.strictEqual(domain, 'example.org');
// Multiple queries
for (let i = 1; i < desiredQueries; i++) {
resolver.resolve4(`example${i}.org`, callback);
}
};

// Do not send a reply.
resolver.cancel();
// Single query
addMessageListener();
resolver.resolve4('example0.org', next);
}));

0 comments on commit 6349b1d

Please sign in to comment.