Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/api/dns.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ changes:
**Default:** `true` (addresses are not reordered). Default value is
configurable using [`dns.setDefaultResultOrder()`][] or
[`--dns-result-order`][].
* `signal` {AbortSignal} An AbortSignal that may be used to cancel an
in-progress lookup. If the signal is aborted, the callback is called with
an `AbortError`.
* `callback` {Function}
* `err` {Error}
* `address` {string} A string representation of an IPv4 or IPv6 address.
Expand Down Expand Up @@ -1125,6 +1128,9 @@ changes:
expected to change in the not too distant future. Default value is
configurable using [`dns.setDefaultResultOrder()`][] or
[`--dns-result-order`][].
* `signal` {AbortSignal} An AbortSignal that may be used to cancel an
in-progress lookup. If the signal is aborted, the returned `Promise` is
rejected with an `AbortError`.

Resolves a host name (e.g. `'nodejs.org'`) into the first found A (IPv4) or
AAAA (IPv6) record. All `option` properties are optional. If `options` is an
Expand Down
33 changes: 33 additions & 0 deletions lib/dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
const { isIP } = require('internal/net');
const { customPromisifyArgs } = require('internal/util');
const {
AbortError,
DNSException,
codes: {
ERR_INVALID_ARG_TYPE,
Expand Down Expand Up @@ -78,6 +79,7 @@
CANCELLED,
} = dnsErrorCodes;
const {
validateAbortSignal,
validateBoolean,
validateFunction,
validateNumber,
Expand All @@ -86,6 +88,8 @@
validateString,
} = require('internal/validators');

let kResistStopPropagation;

const {
GetAddrInfoReqWrap,
GetNameInfoReqWrap,
Expand All @@ -106,6 +110,7 @@
let promises = null; // Lazy loaded

function onlookup(err, addresses) {
if (this.aborted) return;
if (err) {
return this.callback(new DNSException(err, 'getaddrinfo', this.hostname));
}
Expand All @@ -117,6 +122,7 @@


function onlookupall(err, addresses) {
if (this.aborted) return;
if (err) {
return this.callback(new DNSException(err, 'getaddrinfo', this.hostname));
}
Expand All @@ -143,6 +149,7 @@
let hints = 0;
let family = 0;
let all = false;
let signal;
let dnsOrder = getDefaultResultOrder();

// Parse arguments
Expand Down Expand Up @@ -195,6 +202,15 @@
validateOneOf(options.order, 'options.order', validDnsOrders);
dnsOrder = options.order;
}
if (options?.signal != null) {
validateAbortSignal(options.signal, 'options.signal');
}
signal = options?.signal;
}

if (signal?.aborted) {
process.nextTick(callback, new AbortError(undefined, { cause: signal.reason }));
return {};
}

if (!hostname) {
Expand Down Expand Up @@ -234,6 +250,23 @@
process.nextTick(callback, new DNSException(err, 'getaddrinfo', hostname));
return {};
}
if (signal) {
const onAbort = () => {
req.aborted = true;
req.callback(new AbortError(undefined, { cause: signal.reason }));
};
kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
signal.addEventListener('abort', onAbort, {
__proto__: null,
once: true,
[kResistStopPropagation]: true,
});
const originalOncomplete = req.oncomplete;
req.oncomplete = function(...args) {
signal.removeEventListener('abort', onAbort);
return originalOncomplete.apply(this, args);

Check failure on line 267 in lib/dns.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `ReflectApply` instead of %Function.prototype.apply%
};
}
if (hasObserver('dns')) {
const detail = {
hostname,
Expand Down
60 changes: 48 additions & 12 deletions lib/internal/dns/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
FunctionPrototypeCall,
ObjectDefineProperty,
Promise,
SafePromisePrototypeFinally,
Symbol,
} = primordials;

Expand Down Expand Up @@ -46,6 +47,7 @@
CANCELLED,
} = dnsErrorCodes;
const {
AbortError,
DNSException,
codes: {
ERR_INVALID_ARG_TYPE,
Expand All @@ -65,13 +67,16 @@
DNS_ORDER_IPV6_FIRST,
} = internalBinding('cares_wrap');
const {
validateAbortSignal,
validateBoolean,
validateNumber,
validateOneOf,
validatePort,
validateString,
} = require('internal/validators');

let kResistStopPropagation;

const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext');
const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext');
Expand All @@ -83,6 +88,7 @@
} = require('internal/perf/observe');

function onlookup(err, addresses) {
if (this.aborted) return;
if (err) {
this.reject(new DNSException(err, 'getaddrinfo', this.hostname));
return;
Expand All @@ -96,6 +102,7 @@
}

function onlookupall(err, addresses) {
if (this.aborted) return;
if (err) {
this.reject(new DNSException(err, 'getaddrinfo', this.hostname));
return;
Expand Down Expand Up @@ -131,8 +138,13 @@
* @property {string} address - The IP address.
* @property {0 | 4 | 6} family - The IP address type. 4 for IPv4 or 6 for IPv6, or 0 (for both).
*/
function createLookupPromise(family, hostname, all, hints, dnsOrder) {
return new Promise((resolve, reject) => {
function createLookupPromise(family, hostname, all, hints, dnsOrder, signal) {
if (signal?.aborted) {
return Promise.reject(new AbortError(undefined, { cause: signal.reason }));

Check failure on line 143 in lib/internal/dns/promises.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { PromiseReject } = primordials;` instead of the global
}

let onabort;
const promise = new Promise((resolve, reject) => {
if (!hostname) {
reject(new ERR_INVALID_ARG_VALUE('hostname', hostname,
'must be a non-empty string'));
Expand Down Expand Up @@ -167,17 +179,36 @@

if (err) {
reject(new DNSException(err, 'getaddrinfo', hostname));
} else if (hasObserver('dns')) {
const detail = {
hostname,
family,
hints,
verbatim: order === DNS_ORDER_VERBATIM,
order: dnsOrder,
};
startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
} else {
if (signal) {
onabort = () => {
req.aborted = true;
reject(new AbortError(undefined, { cause: signal.reason }));
};
kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
signal.addEventListener('abort', onabort, {
__proto__: null,
once: true,
[kResistStopPropagation]: true,
});
}
if (hasObserver('dns')) {
const detail = {
hostname,
family,
hints,
verbatim: order === DNS_ORDER_VERBATIM,
order: dnsOrder,
};
startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
}
}
});

return onabort !== undefined ?
SafePromisePrototypeFinally(
promise,
() => signal.removeEventListener('abort', onabort)) : promise;
}

/**
Expand All @@ -196,6 +227,7 @@
let hints = 0;
let family = 0;
let all = false;
let signal;
let dnsOrder = getDefaultResultOrder();

// Parse arguments
Expand Down Expand Up @@ -230,9 +262,13 @@
validateOneOf(options.order, 'options.order', validDnsOrders);
dnsOrder = options.order;
}
if (options?.signal != null) {
validateAbortSignal(options.signal, 'options.signal');
}
signal = options?.signal;
}

return createLookupPromise(family, hostname, all, hints, dnsOrder);
return createLookupPromise(family, hostname, all, hints, dnsOrder, signal);
}


Expand Down
Loading
Loading