@@ -20,7 +20,6 @@ import {
20
20
registerResolver ,
21
21
registerDefaultScheme ,
22
22
} from './resolver' ;
23
- import { AnyRecord } from 'dns' ;
24
23
import * as dns from 'dns/promises' ;
25
24
import { extractAndSelectServiceConfig , ServiceConfig } from './service-config' ;
26
25
import { Status } from './constants' ;
@@ -45,7 +44,8 @@ function trace(text: string): void {
45
44
*/
46
45
export const DEFAULT_PORT = 443 ;
47
46
48
- const DEFAULT_MIN_TIME_BETWEEN_RESOLUTIONS_MS = 30_000 ;
47
+ const DEFAULT_MIN_TIME_BETWEEN_RESOLUTIONS_MS = 30_000 ,
48
+ DNS_RESOLUTION_ENV = 'GRPC_DNS_RESOLVER_TODO' ;
49
49
50
50
/**
51
51
* Resolver implementation that handles DNS names and IP addresses.
@@ -60,7 +60,7 @@ class DnsResolver implements Resolver {
60
60
* Failures are handled by the backoff timer.
61
61
*/
62
62
private readonly minTimeBetweenResolutionsMs : number ;
63
- private pendingLookupPromise : Promise < AnyRecord [ ] > | null = null ;
63
+ private pendingLookupPromise : Promise < TcpSubchannelAddress [ ] > | null = null ;
64
64
private pendingTxtPromise : Promise < string [ ] [ ] > | null = null ;
65
65
private latestLookupResult : Endpoint [ ] | null = null ;
66
66
private latestServiceConfig : ServiceConfig | null = null ;
@@ -73,7 +73,7 @@ class DnsResolver implements Resolver {
73
73
private isNextResolutionTimerRunning = false ;
74
74
private isServiceConfigEnabled = true ;
75
75
private returnedIpResult = false ;
76
- private resolver = new dns . Resolver ( ) ;
76
+ private independentResolver = new dns . Resolver ( ) ;
77
77
78
78
constructor (
79
79
private target : GrpcUri ,
@@ -82,7 +82,7 @@ class DnsResolver implements Resolver {
82
82
) {
83
83
trace ( 'Resolver constructed for target ' + uriToString ( target ) ) ;
84
84
if ( target . authority ) {
85
- this . resolver . setServers ( [ target . authority ] ) ;
85
+ this . independentResolver . setServers ( [ target . authority ] ) ;
86
86
}
87
87
const hostPort = splitHostPort ( target . path ) ;
88
88
if ( hostPort === null ) {
@@ -187,11 +187,7 @@ class DnsResolver implements Resolver {
187
187
* revert to an effectively blank one. */
188
188
this . latestLookupResult = null ;
189
189
const hostname : string = this . dnsHostname ;
190
- /* We lookup both address families here and then split them up later
191
- * because when looking up a single family, dns.lookup outputs an error
192
- * if the name exists but there are no records for that family, and that
193
- * error is indistinguishable from other kinds of errors */
194
- this . pendingLookupPromise = this . resolver . resolveAny ( hostname ) ;
190
+ this . pendingLookupPromise = this . lookup ( hostname ) ;
195
191
this . pendingLookupPromise . then (
196
192
addressList => {
197
193
if ( this . pendingLookupPromise === null ) {
@@ -200,19 +196,12 @@ class DnsResolver implements Resolver {
200
196
this . pendingLookupPromise = null ;
201
197
this . backoff . reset ( ) ;
202
198
this . backoff . stop ( ) ;
203
- const subchannelAddresses : TcpSubchannelAddress [ ] = addressList
204
- . filter ( addr => {
205
- addr . type === 'A' || addr . type === 'AAAA' ;
206
- } )
207
- . map ( addr => ( { host : addr . address , port : + this . port ! } ) ) ;
208
- this . latestLookupResult = subchannelAddresses . map ( address => ( {
199
+ this . latestLookupResult = addressList . map ( address => ( {
209
200
addresses : [ address ] ,
210
201
} ) ) ;
211
202
const allAddressesString : string =
212
203
'[' +
213
- subchannelAddresses
214
- . map ( addr => addr . host + ':' + addr . port )
215
- . join ( ',' ) +
204
+ addressList . map ( addr => addr . host + ':' + addr . port ) . join ( ',' ) +
216
205
']' ;
217
206
trace (
218
207
'Resolved addresses for target ' +
@@ -257,7 +246,7 @@ class DnsResolver implements Resolver {
257
246
/* We handle the TXT query promise differently than the others because
258
247
* the name resolution attempt as a whole is a success even if the TXT
259
248
* lookup fails */
260
- this . pendingTxtPromise = this . resolver . resolveTxt ( hostname ) ;
249
+ this . pendingTxtPromise = this . resolveTxt ( hostname ) ;
261
250
this . pendingTxtPromise . then (
262
251
txtRecord => {
263
252
if ( this . pendingTxtPromise === null ) {
@@ -306,6 +295,35 @@ class DnsResolver implements Resolver {
306
295
}
307
296
}
308
297
298
+ private async lookup ( hostname : string ) : Promise < TcpSubchannelAddress [ ] > {
299
+ if ( process . env [ DNS_RESOLUTION_ENV ] === 'true' ) {
300
+ const records = await this . independentResolver . resolveAny ( hostname ) ;
301
+ const addressList = records . filter ( addr => {
302
+ addr . type === 'A' || addr . type === 'AAAA' ;
303
+ } ) as unknown as { address : string } [ ] ;
304
+
305
+ return addressList . map ( addr => ( {
306
+ host : addr . address ,
307
+ port : + this . port ! ,
308
+ } ) ) ;
309
+ }
310
+
311
+ /* We lookup both address families here and then split them up later
312
+ * because when looking up a single family, dns.lookup outputs an error
313
+ * if the name exists but there are no records for that family, and that
314
+ * error is indistinguishable from other kinds of errors */
315
+ const addressList = await dns . lookup ( hostname , { all : true } ) ;
316
+ return addressList . map ( addr => ( { host : addr . address , port : + this . port ! } ) ) ;
317
+ }
318
+
319
+ private async resolveTxt ( hostname : string ) : Promise < string [ ] [ ] > {
320
+ if ( process . env [ DNS_RESOLUTION_ENV ] === 'true' ) {
321
+ return this . independentResolver . resolveTxt ( hostname ) ;
322
+ }
323
+
324
+ return dns . resolveTxt ( hostname ) ;
325
+ }
326
+
309
327
private startNextResolutionTimer ( ) {
310
328
clearTimeout ( this . nextResolutionTimer ) ;
311
329
this . nextResolutionTimer = setTimeout ( ( ) => {
0 commit comments