Skip to content

Commit 0f232e9

Browse files
joyeecheungRafaelGSS
authored andcommitted
dns: refactor default resolver
This patch refactors the DNS default resolver code to make it easier to be included in a snapshot: - The code specific for the callback-based DNS resolver are not in a separate module to make the dependency clearer (it's not actually needed if the user only ever loads `dns/promises`) - The common part of the callback-based resolver and the promise- based resolver is now ResolverBase. The other two user-facing resolvers are now subclasses of ResolverBase. The default Resolver is constructed with just ResolverBase. This would be fine as the default resolver is never actually exposed to the user-land and it has been working using duck-typing anyway. - Move the construction of Resolver subclasses into a common method `createResolverClass()` to reduce code duplication. The two subclasses now also share the same base constructor. This would make it possible for them to also share code for snapshot support later. - `--dns-result-order` is now queried and refreshed during pre-execution. To avoid loading the cares_wrap binding unnecessarily the loading of the binding is also made lazy. PR-URL: #44541 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Zeyu "Alex" Yang <[email protected]> Reviewed-By: Minwoo Jung <[email protected]>
1 parent 2d87b6f commit 0f232e9

File tree

7 files changed

+191
-141
lines changed

7 files changed

+191
-141
lines changed

lib/dns.js

+4-93
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,8 @@
2222
'use strict';
2323

2424
const {
25-
ArrayPrototypeMap,
26-
ObjectCreate,
2725
ObjectDefineProperties,
2826
ObjectDefineProperty,
29-
ReflectApply,
3027
Symbol,
3128
} = primordials;
3229

@@ -37,15 +34,16 @@ const { customPromisifyArgs } = require('internal/util');
3734
const errors = require('internal/errors');
3835
const {
3936
bindDefaultResolver,
40-
getDefaultResolver,
4137
setDefaultResolver,
42-
Resolver,
4338
validateHints,
4439
emitInvalidHostnameWarning,
4540
getDefaultVerbatim,
4641
setDefaultResultOrder,
4742
errorCodes: dnsErrorCodes,
4843
} = require('internal/dns/utils');
44+
const {
45+
Resolver
46+
} = require('internal/dns/callback_resolver');
4947
const {
5048
NODATA,
5149
FORMERR,
@@ -89,12 +87,10 @@ const {
8987
const {
9088
GetAddrInfoReqWrap,
9189
GetNameInfoReqWrap,
92-
QueryReqWrap,
9390
} = cares;
9491

9592
const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
9693
const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext');
97-
const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext');
9894

9995
const {
10096
hasObserver,
@@ -293,91 +289,6 @@ function lookupService(address, port, callback) {
293289
ObjectDefineProperty(lookupService, customPromisifyArgs,
294290
{ __proto__: null, value: ['hostname', 'service'], enumerable: false });
295291

296-
297-
function onresolve(err, result, ttls) {
298-
if (ttls && this.ttl)
299-
result = ArrayPrototypeMap(
300-
result, (address, index) => ({ address, ttl: ttls[index] }));
301-
302-
if (err)
303-
this.callback(dnsException(err, this.bindingName, this.hostname));
304-
else {
305-
this.callback(null, result);
306-
if (this[kPerfHooksDnsLookupResolveContext] && hasObserver('dns')) {
307-
stopPerf(this, kPerfHooksDnsLookupResolveContext, { detail: { result } });
308-
}
309-
}
310-
}
311-
312-
function resolver(bindingName) {
313-
function query(name, /* options, */ callback) {
314-
let options;
315-
if (arguments.length > 2) {
316-
options = callback;
317-
callback = arguments[2];
318-
}
319-
320-
validateString(name, 'name');
321-
validateFunction(callback, 'callback');
322-
323-
const req = new QueryReqWrap();
324-
req.bindingName = bindingName;
325-
req.callback = callback;
326-
req.hostname = name;
327-
req.oncomplete = onresolve;
328-
req.ttl = !!(options && options.ttl);
329-
const err = this._handle[bindingName](req, toASCII(name));
330-
if (err) throw dnsException(err, bindingName, name);
331-
if (hasObserver('dns')) {
332-
startPerf(req, kPerfHooksDnsLookupResolveContext, {
333-
type: 'dns',
334-
name: bindingName,
335-
detail: {
336-
host: name,
337-
ttl: req.ttl,
338-
},
339-
});
340-
}
341-
return req;
342-
}
343-
ObjectDefineProperty(query, 'name', { __proto__: null, value: bindingName });
344-
return query;
345-
}
346-
347-
const resolveMap = ObjectCreate(null);
348-
Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny');
349-
Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA');
350-
Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa');
351-
Resolver.prototype.resolveCaa = resolveMap.CAA = resolver('queryCaa');
352-
Resolver.prototype.resolveCname = resolveMap.CNAME = resolver('queryCname');
353-
Resolver.prototype.resolveMx = resolveMap.MX = resolver('queryMx');
354-
Resolver.prototype.resolveNs = resolveMap.NS = resolver('queryNs');
355-
Resolver.prototype.resolveTxt = resolveMap.TXT = resolver('queryTxt');
356-
Resolver.prototype.resolveSrv = resolveMap.SRV = resolver('querySrv');
357-
Resolver.prototype.resolvePtr = resolveMap.PTR = resolver('queryPtr');
358-
Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver('queryNaptr');
359-
Resolver.prototype.resolveSoa = resolveMap.SOA = resolver('querySoa');
360-
Resolver.prototype.reverse = resolver('getHostByAddr');
361-
362-
Resolver.prototype.resolve = resolve;
363-
364-
function resolve(hostname, rrtype, callback) {
365-
let resolver;
366-
if (typeof rrtype === 'string') {
367-
resolver = resolveMap[rrtype];
368-
} else if (typeof rrtype === 'function') {
369-
resolver = resolveMap.A;
370-
callback = rrtype;
371-
} else {
372-
throw new ERR_INVALID_ARG_TYPE('rrtype', 'string', rrtype);
373-
}
374-
375-
if (typeof resolver === 'function') {
376-
return ReflectApply(resolver, this, [hostname, callback]);
377-
}
378-
throw new ERR_INVALID_ARG_VALUE('rrtype', rrtype);
379-
}
380-
381292
function defaultResolverSetServers(servers) {
382293
const resolver = new Resolver();
383294

@@ -429,7 +340,7 @@ module.exports = {
429340
CANCELLED,
430341
};
431342

432-
bindDefaultResolver(module.exports, getDefaultResolver());
343+
bindDefaultResolver(module.exports, Resolver.prototype);
433344

434345
ObjectDefineProperties(module.exports, {
435346
promises: {

lib/internal/dns/callback_resolver.js

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
'use strict';
2+
3+
const {
4+
ObjectDefineProperty,
5+
ReflectApply,
6+
ArrayPrototypeMap,
7+
Symbol
8+
} = primordials;
9+
10+
const { toASCII } = require('internal/idna');
11+
12+
const {
13+
codes: {
14+
ERR_INVALID_ARG_TYPE,
15+
ERR_INVALID_ARG_VALUE,
16+
},
17+
dnsException
18+
} = require('internal/errors');
19+
20+
const {
21+
createResolverClass,
22+
} = require('internal/dns/utils');
23+
24+
const {
25+
validateFunction,
26+
validateString,
27+
} = require('internal/validators');
28+
29+
const {
30+
QueryReqWrap,
31+
} = internalBinding('cares_wrap');
32+
33+
const {
34+
hasObserver,
35+
startPerf,
36+
stopPerf,
37+
} = require('internal/perf/observe');
38+
39+
const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext');
40+
41+
function onresolve(err, result, ttls) {
42+
if (ttls && this.ttl)
43+
result = ArrayPrototypeMap(
44+
result, (address, index) => ({ address, ttl: ttls[index] }));
45+
46+
if (err)
47+
this.callback(dnsException(err, this.bindingName, this.hostname));
48+
else {
49+
this.callback(null, result);
50+
if (this[kPerfHooksDnsLookupResolveContext] && hasObserver('dns')) {
51+
stopPerf(this, kPerfHooksDnsLookupResolveContext, { detail: { result } });
52+
}
53+
}
54+
}
55+
56+
function resolver(bindingName) {
57+
function query(name, /* options, */ callback) {
58+
let options;
59+
if (arguments.length > 2) {
60+
options = callback;
61+
callback = arguments[2];
62+
}
63+
64+
validateString(name, 'name');
65+
validateFunction(callback, 'callback');
66+
67+
const req = new QueryReqWrap();
68+
req.bindingName = bindingName;
69+
req.callback = callback;
70+
req.hostname = name;
71+
req.oncomplete = onresolve;
72+
req.ttl = !!(options && options.ttl);
73+
const err = this._handle[bindingName](req, toASCII(name));
74+
if (err) throw dnsException(err, bindingName, name);
75+
if (hasObserver('dns')) {
76+
startPerf(req, kPerfHooksDnsLookupResolveContext, {
77+
type: 'dns',
78+
name: bindingName,
79+
detail: {
80+
host: name,
81+
ttl: req.ttl,
82+
},
83+
});
84+
}
85+
return req;
86+
}
87+
ObjectDefineProperty(query, 'name', { __proto__: null, value: bindingName });
88+
return query;
89+
}
90+
91+
// This is the callback-based resolver. There is another similar
92+
// resolver in dns/promises.js with resolve methods that are based
93+
// on promises instead.
94+
const { Resolver, resolveMap } = createResolverClass(resolver);
95+
Resolver.prototype.resolve = resolve;
96+
97+
function resolve(hostname, rrtype, callback) {
98+
let resolver;
99+
if (typeof rrtype === 'string') {
100+
resolver = resolveMap[rrtype];
101+
} else if (typeof rrtype === 'function') {
102+
resolver = resolveMap.A;
103+
callback = rrtype;
104+
} else {
105+
throw new ERR_INVALID_ARG_TYPE('rrtype', 'string', rrtype);
106+
}
107+
108+
if (typeof resolver === 'function') {
109+
return ReflectApply(resolver, this, [hostname, callback]);
110+
}
111+
throw new ERR_INVALID_ARG_VALUE('rrtype', rrtype);
112+
}
113+
114+
module.exports = {
115+
Resolver
116+
};

lib/internal/dns/promises.js

+8-36
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22
const {
33
ArrayPrototypeMap,
4-
ObjectCreate,
54
ObjectDefineProperty,
65
Promise,
76
ReflectApply,
@@ -10,16 +9,15 @@ const {
109

1110
const {
1211
bindDefaultResolver,
13-
Resolver: CallbackResolver,
12+
createResolverClass,
1413
validateHints,
15-
validateTimeout,
16-
validateTries,
1714
emitInvalidHostnameWarning,
1815
getDefaultVerbatim,
1916
errorCodes: dnsErrorCodes,
2017
setDefaultResultOrder,
2118
setDefaultResolver,
2219
} = require('internal/dns/utils');
20+
2321
const {
2422
NODATA,
2523
FORMERR,
@@ -52,7 +50,6 @@ const { isIP } = require('internal/net');
5250
const {
5351
getaddrinfo,
5452
getnameinfo,
55-
ChannelWrap,
5653
GetAddrInfoReqWrap,
5754
GetNameInfoReqWrap,
5855
QueryReqWrap
@@ -305,36 +302,7 @@ function resolver(bindingName) {
305302
return query;
306303
}
307304

308-
309-
const resolveMap = ObjectCreate(null);
310-
311-
// Resolver instances correspond 1:1 to c-ares channels.
312-
class Resolver {
313-
constructor(options = undefined) {
314-
const timeout = validateTimeout(options);
315-
const tries = validateTries(options);
316-
this._handle = new ChannelWrap(timeout, tries);
317-
}
318-
}
319-
320-
Resolver.prototype.getServers = CallbackResolver.prototype.getServers;
321-
Resolver.prototype.setServers = CallbackResolver.prototype.setServers;
322-
Resolver.prototype.cancel = CallbackResolver.prototype.cancel;
323-
Resolver.prototype.setLocalAddress = CallbackResolver.prototype.setLocalAddress;
324-
Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny');
325-
Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA');
326-
Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa');
327-
Resolver.prototype.resolveCaa = resolveMap.CAA = resolver('queryCaa');
328-
Resolver.prototype.resolveCname = resolveMap.CNAME = resolver('queryCname');
329-
Resolver.prototype.resolveMx = resolveMap.MX = resolver('queryMx');
330-
Resolver.prototype.resolveNs = resolveMap.NS = resolver('queryNs');
331-
Resolver.prototype.resolveTxt = resolveMap.TXT = resolver('queryTxt');
332-
Resolver.prototype.resolveSrv = resolveMap.SRV = resolver('querySrv');
333-
Resolver.prototype.resolvePtr = resolveMap.PTR = resolver('queryPtr');
334-
Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver('queryNaptr');
335-
Resolver.prototype.resolveSoa = resolveMap.SOA = resolver('querySoa');
336-
Resolver.prototype.reverse = resolver('getHostByAddr');
337-
Resolver.prototype.resolve = function resolve(hostname, rrtype) {
305+
function resolve(hostname, rrtype) {
338306
let resolver;
339307

340308
if (rrtype !== undefined) {
@@ -349,7 +317,11 @@ Resolver.prototype.resolve = function resolve(hostname, rrtype) {
349317
}
350318

351319
return ReflectApply(resolver, this, [hostname]);
352-
};
320+
}
321+
322+
// Promise-based resolver.
323+
const { Resolver, resolveMap } = createResolverClass(resolver);
324+
Resolver.prototype.resolve = resolve;
353325

354326
function defaultResolverSetServers(servers) {
355327
const resolver = new Resolver();

0 commit comments

Comments
 (0)