@@ -2,6 +2,7 @@ import * as dns from 'dns';
2
2
import * as fs from 'fs' ;
3
3
import ConnectionString from 'mongodb-connection-string-url' ;
4
4
import { URLSearchParams } from 'url' ;
5
+ import { promisify } from 'util' ;
5
6
6
7
import type { Document } from './bson' ;
7
8
import { MongoCredentials } from './cmap/auth/mongo_credentials' ;
@@ -30,7 +31,6 @@ import { ReadPreference, ReadPreferenceMode } from './read_preference';
30
31
import type { TagSet } from './sdam/server_description' ;
31
32
import {
32
33
AnyOptions ,
33
- Callback ,
34
34
DEFAULT_PK_FACTORY ,
35
35
emitWarning ,
36
36
emitWarningOnce ,
@@ -70,97 +70,91 @@ function matchesParentDomain(srvAddress: string, parentDomain: string): boolean
70
70
* @param uri - The connection string to parse
71
71
* @param options - Optional user provided connection string options
72
72
*/
73
- export function resolveSRVRecord ( options : MongoOptions , callback : Callback < HostAddress [ ] > ) : void {
73
+ export async function resolveSRVRecord ( options : MongoOptions ) : Promise < HostAddress [ ] > {
74
+ // We need to do this here to make sinon tests work :(
75
+ const dnsResolveSrv = promisify ( dns . resolveSrv ) ;
76
+ const dnsResolveTxt = promisify ( dns . resolveTxt ) ;
77
+
74
78
if ( typeof options . srvHost !== 'string' ) {
75
- return callback ( new MongoAPIError ( 'Option "srvHost" must not be empty' ) ) ;
79
+ throw new MongoAPIError ( 'Option "srvHost" must not be empty' ) ;
76
80
}
77
81
78
82
if ( options . srvHost . split ( '.' ) . length < 3 ) {
79
83
// TODO(NODE-3484): Replace with MongoConnectionStringError
80
- return callback ( new MongoAPIError ( 'URI must include hostname, domain name, and tld' ) ) ;
84
+ throw new MongoAPIError ( 'URI must include hostname, domain name, and tld' ) ;
81
85
}
82
86
83
87
// Resolve the SRV record and use the result as the list of hosts to connect to.
84
88
const lookupAddress = options . srvHost ;
85
- dns . resolveSrv ( `_${ options . srvServiceName } ._tcp.${ lookupAddress } ` , ( err , addresses ) => {
86
- if ( err ) return callback ( err ) ;
89
+ const addresses = await dnsResolveSrv ( `_${ options . srvServiceName } ._tcp.${ lookupAddress } ` ) ;
87
90
88
- if ( addresses . length === 0 ) {
89
- return callback ( new MongoAPIError ( 'No addresses found at host' ) ) ;
90
- }
91
+ if ( addresses . length === 0 ) {
92
+ throw new MongoAPIError ( 'No addresses found at host' ) ;
93
+ }
91
94
92
- for ( const { name } of addresses ) {
93
- if ( ! matchesParentDomain ( name , lookupAddress ) ) {
94
- return callback ( new MongoAPIError ( 'Server record does not share hostname with parent URI' ) ) ;
95
- }
95
+ for ( const { name } of addresses ) {
96
+ if ( ! matchesParentDomain ( name , lookupAddress ) ) {
97
+ throw new MongoAPIError ( 'Server record does not share hostname with parent URI' ) ;
96
98
}
99
+ }
97
100
98
- const hostAddresses = addresses . map ( r =>
99
- HostAddress . fromString ( `${ r . name } :${ r . port ?? 27017 } ` )
100
- ) ;
101
+ const hostAddresses = addresses . map ( r => HostAddress . fromString ( `${ r . name } :${ r . port ?? 27017 } ` ) ) ;
101
102
102
- const lbError = validateLoadBalancedOptions ( hostAddresses , options , true ) ;
103
- if ( lbError ) {
104
- return callback ( lbError ) ;
103
+ validateLoadBalancedOptions ( hostAddresses , options , true ) ;
104
+
105
+ // Resolve TXT record and add options from there if they exist.
106
+ let record ;
107
+ try {
108
+ record = await dnsResolveTxt ( lookupAddress ) ;
109
+ } catch ( error ) {
110
+ if ( error . code !== 'ENODATA' && error . code !== 'ENOTFOUND' ) {
111
+ throw error ;
105
112
}
113
+ return hostAddresses ;
114
+ }
106
115
107
- // Resolve TXT record and add options from there if they exist.
108
- dns . resolveTxt ( lookupAddress , ( err , record ) => {
109
- if ( err ) {
110
- if ( err . code !== 'ENODATA' && err . code !== 'ENOTFOUND' ) {
111
- return callback ( err ) ;
112
- }
113
- } else {
114
- if ( record . length > 1 ) {
115
- return callback ( new MongoParseError ( 'Multiple text records not allowed' ) ) ;
116
- }
116
+ if ( record . length > 1 ) {
117
+ throw new MongoParseError ( 'Multiple text records not allowed' ) ;
118
+ }
117
119
118
- const txtRecordOptions = new URLSearchParams ( record [ 0 ] . join ( '' ) ) ;
119
- const txtRecordOptionKeys = [ ...txtRecordOptions . keys ( ) ] ;
120
- if ( txtRecordOptionKeys . some ( key => ! VALID_TXT_RECORDS . includes ( key ) ) ) {
121
- return callback (
122
- new MongoParseError ( `Text record may only set any of: ${ VALID_TXT_RECORDS . join ( ', ' ) } ` )
123
- ) ;
124
- }
120
+ const txtRecordOptions = new URLSearchParams ( record [ 0 ] . join ( '' ) ) ;
121
+ const txtRecordOptionKeys = [ ...txtRecordOptions . keys ( ) ] ;
122
+ if ( txtRecordOptionKeys . some ( key => ! VALID_TXT_RECORDS . includes ( key ) ) ) {
123
+ throw new MongoParseError ( `Text record may only set any of: ${ VALID_TXT_RECORDS . join ( ', ' ) } ` ) ;
124
+ }
125
125
126
- if ( VALID_TXT_RECORDS . some ( option => txtRecordOptions . get ( option ) === '' ) ) {
127
- return callback ( new MongoParseError ( 'Cannot have empty URI params in DNS TXT Record' ) ) ;
128
- }
126
+ if ( VALID_TXT_RECORDS . some ( option => txtRecordOptions . get ( option ) === '' ) ) {
127
+ throw new MongoParseError ( 'Cannot have empty URI params in DNS TXT Record' ) ;
128
+ }
129
129
130
- const source = txtRecordOptions . get ( 'authSource' ) ?? undefined ;
131
- const replicaSet = txtRecordOptions . get ( 'replicaSet' ) ?? undefined ;
132
- const loadBalanced = txtRecordOptions . get ( 'loadBalanced' ) ?? undefined ;
133
-
134
- if (
135
- ! options . userSpecifiedAuthSource &&
136
- source &&
137
- options . credentials &&
138
- ! AUTH_MECHS_AUTH_SRC_EXTERNAL . has ( options . credentials . mechanism )
139
- ) {
140
- options . credentials = MongoCredentials . merge ( options . credentials , { source } ) ;
141
- }
130
+ const source = txtRecordOptions . get ( 'authSource' ) ?? undefined ;
131
+ const replicaSet = txtRecordOptions . get ( 'replicaSet' ) ?? undefined ;
132
+ const loadBalanced = txtRecordOptions . get ( 'loadBalanced' ) ?? undefined ;
142
133
143
- if ( ! options . userSpecifiedReplicaSet && replicaSet ) {
144
- options . replicaSet = replicaSet ;
145
- }
134
+ if (
135
+ ! options . userSpecifiedAuthSource &&
136
+ source &&
137
+ options . credentials &&
138
+ ! AUTH_MECHS_AUTH_SRC_EXTERNAL . has ( options . credentials . mechanism )
139
+ ) {
140
+ options . credentials = MongoCredentials . merge ( options . credentials , { source } ) ;
141
+ }
146
142
147
- if ( loadBalanced === 'true' ) {
148
- options . loadBalanced = true ;
149
- }
143
+ if ( ! options . userSpecifiedReplicaSet && replicaSet ) {
144
+ options . replicaSet = replicaSet ;
145
+ }
150
146
151
- if ( options . replicaSet && options . srvMaxHosts > 0 ) {
152
- return callback ( new MongoParseError ( 'Cannot combine replicaSet option with srvMaxHosts' ) ) ;
153
- }
147
+ if ( loadBalanced === 'true' ) {
148
+ options . loadBalanced = true ;
149
+ }
154
150
155
- const lbError = validateLoadBalancedOptions ( hostAddresses , options , true ) ;
156
- if ( lbError ) {
157
- return callback ( lbError ) ;
158
- }
159
- }
151
+ if ( options . replicaSet && options . srvMaxHosts > 0 ) {
152
+ throw new MongoParseError ( 'Cannot combine replicaSet option with srvMaxHosts' ) ;
153
+ }
154
+
155
+ validateLoadBalancedOptions ( hostAddresses , options , true ) ;
160
156
161
- callback ( undefined , hostAddresses ) ;
162
- } ) ;
163
- } ) ;
157
+ return hostAddresses ;
164
158
}
165
159
166
160
/**
@@ -442,10 +436,8 @@ export function parseOptions(
442
436
PromiseProvider . set ( options . promiseLibrary ) ;
443
437
}
444
438
445
- const lbError = validateLoadBalancedOptions ( hosts , mongoOptions , isSRV ) ;
446
- if ( lbError ) {
447
- throw lbError ;
448
- }
439
+ validateLoadBalancedOptions ( hosts , mongoOptions , isSRV ) ;
440
+
449
441
if ( mongoClient && mongoOptions . autoEncryption ) {
450
442
Encrypter . checkForMongoCrypt ( ) ;
451
443
mongoOptions . encrypter = new Encrypter ( mongoClient , uri , options ) ;
@@ -526,20 +518,20 @@ function validateLoadBalancedOptions(
526
518
hosts : HostAddress [ ] | string [ ] ,
527
519
mongoOptions : MongoOptions ,
528
520
isSrv : boolean
529
- ) : MongoParseError | undefined {
521
+ ) : void {
530
522
if ( mongoOptions . loadBalanced ) {
531
523
if ( hosts . length > 1 ) {
532
- return new MongoParseError ( LB_SINGLE_HOST_ERROR ) ;
524
+ throw new MongoParseError ( LB_SINGLE_HOST_ERROR ) ;
533
525
}
534
526
if ( mongoOptions . replicaSet ) {
535
- return new MongoParseError ( LB_REPLICA_SET_ERROR ) ;
527
+ throw new MongoParseError ( LB_REPLICA_SET_ERROR ) ;
536
528
}
537
529
if ( mongoOptions . directConnection ) {
538
- return new MongoParseError ( LB_DIRECT_CONNECTION_ERROR ) ;
530
+ throw new MongoParseError ( LB_DIRECT_CONNECTION_ERROR ) ;
539
531
}
540
532
541
533
if ( isSrv && mongoOptions . srvMaxHosts > 0 ) {
542
- return new MongoParseError ( 'Cannot limit srv hosts with loadBalanced enabled' ) ;
534
+ throw new MongoParseError ( 'Cannot limit srv hosts with loadBalanced enabled' ) ;
543
535
}
544
536
}
545
537
return ;
0 commit comments