@@ -75,7 +75,7 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
7575
7676 // Resolve the SRV record and use the result as the list of hosts to connect to.
7777 const lookupAddress = options . srvHost ;
78- dns . resolveSrv ( `_mongodb ._tcp.${ lookupAddress } ` , ( err , addresses ) => {
78+ dns . resolveSrv ( `_ ${ options . srvServiceName } ._tcp.${ lookupAddress } ` , ( err , addresses ) => {
7979 if ( err ) return callback ( err ) ;
8080
8181 if ( addresses . length === 0 ) {
@@ -92,7 +92,7 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
9292 HostAddress . fromString ( `${ r . name } :${ r . port ?? 27017 } ` )
9393 ) ;
9494
95- const lbError = validateLoadBalancedOptions ( hostAddresses , options ) ;
95+ const lbError = validateLoadBalancedOptions ( hostAddresses , options , true ) ;
9696 if ( lbError ) {
9797 return callback ( lbError ) ;
9898 }
@@ -116,14 +116,14 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
116116 ) ;
117117 }
118118
119+ if ( VALID_TXT_RECORDS . some ( option => txtRecordOptions . get ( option ) === '' ) ) {
120+ return callback ( new MongoParseError ( 'Cannot have empty URI params in DNS TXT Record' ) ) ;
121+ }
122+
119123 const source = txtRecordOptions . get ( 'authSource' ) ?? undefined ;
120124 const replicaSet = txtRecordOptions . get ( 'replicaSet' ) ?? undefined ;
121125 const loadBalanced = txtRecordOptions . get ( 'loadBalanced' ) ?? undefined ;
122126
123- if ( source === '' || replicaSet === '' ) {
124- return callback ( new MongoParseError ( 'Cannot have empty URI params in DNS TXT Record' ) ) ;
125- }
126-
127127 if ( ! options . userSpecifiedAuthSource && source ) {
128128 options . credentials = MongoCredentials . merge ( options . credentials , { source } ) ;
129129 }
@@ -136,7 +136,11 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
136136 options . loadBalanced = true ;
137137 }
138138
139- const lbError = validateLoadBalancedOptions ( hostAddresses , options ) ;
139+ if ( options . replicaSet && options . srvMaxHosts > 0 ) {
140+ return callback ( new MongoParseError ( 'Cannot combine replicaSet option with srvMaxHosts' ) ) ;
141+ }
142+
143+ const lbError = validateLoadBalancedOptions ( hostAddresses , options , true ) ;
140144 if ( lbError ) {
141145 return callback ( lbError ) ;
142146 }
@@ -251,13 +255,6 @@ export function parseOptions(
251255
252256 const mongoOptions = Object . create ( null ) ;
253257 mongoOptions . hosts = isSRV ? [ ] : hosts . map ( HostAddress . fromString ) ;
254- if ( isSRV ) {
255- // SRV Record is resolved upon connecting
256- mongoOptions . srvHost = hosts [ 0 ] ;
257- if ( ! url . searchParams . has ( 'tls' ) && ! url . searchParams . has ( 'ssl' ) ) {
258- options . tls = true ;
259- }
260- }
261258
262259 const urlOptions = new CaseInsensitiveMap ( ) ;
263260
@@ -289,30 +286,34 @@ export function parseOptions(
289286 throw new MongoAPIError ( 'URI cannot contain options with no value' ) ;
290287 }
291288
292- if ( key . toLowerCase ( ) === 'serverapi' ) {
293- throw new MongoParseError (
294- 'URI cannot contain `serverApi`, it can only be passed to the client'
295- ) ;
296- }
297-
298- if ( key . toLowerCase ( ) === 'authsource' && urlOptions . has ( 'authSource' ) ) {
299- // If authSource is an explicit key in the urlOptions we need to remove the implicit dbName
300- urlOptions . delete ( 'authSource' ) ;
301- }
302-
303289 if ( ! urlOptions . has ( key ) ) {
304290 urlOptions . set ( key , values ) ;
305291 }
306292 }
307293
294+ if ( urlOptions . has ( 'authSource' ) ) {
295+ // If authSource is an explicit key in the urlOptions we need to remove the dbName
296+ urlOptions . delete ( 'dbName' ) ;
297+ }
298+
308299 const objectOptions = new CaseInsensitiveMap (
309300 Object . entries ( options ) . filter ( ( [ , v ] ) => v != null )
310301 ) ;
311302
303+ // Validate options that can only be provided by one of uri or object
304+
305+ if ( urlOptions . has ( 'serverApi' ) ) {
306+ throw new MongoParseError (
307+ 'URI cannot contain `serverApi`, it can only be passed to the client'
308+ ) ;
309+ }
310+
312311 if ( objectOptions . has ( 'loadBalanced' ) ) {
313312 throw new MongoParseError ( 'loadBalanced is only a valid option in the URI' ) ;
314313 }
315314
315+ // All option collection
316+
316317 const allOptions = new CaseInsensitiveMap ( ) ;
317318
318319 const allKeys = new Set < string > ( [
@@ -360,6 +361,8 @@ export function parseOptions(
360361 ) ;
361362 }
362363
364+ // Option parsing and setting
365+
363366 for ( const [ key , descriptor ] of Object . entries ( OPTIONS ) ) {
364367 const values = allOptions . get ( key ) ;
365368 if ( ! values || values . length === 0 ) continue ;
@@ -401,33 +404,62 @@ export function parseOptions(
401404
402405 if ( options . promiseLibrary ) PromiseProvider . set ( options . promiseLibrary ) ;
403406
404- if ( mongoOptions . directConnection && typeof mongoOptions . srvHost === 'string' ) {
405- throw new MongoAPIError ( 'SRV URI does not support directConnection' ) ;
406- }
407-
408- const lbError = validateLoadBalancedOptions ( hosts , mongoOptions ) ;
407+ const lbError = validateLoadBalancedOptions ( hosts , mongoOptions , isSRV ) ;
409408 if ( lbError ) {
410409 throw lbError ;
411410 }
411+ if ( mongoClient && mongoOptions . autoEncryption ) {
412+ Encrypter . checkForMongoCrypt ( ) ;
413+ mongoOptions . encrypter = new Encrypter ( mongoClient , uri , options ) ;
414+ mongoOptions . autoEncrypter = mongoOptions . encrypter . autoEncrypter ;
415+ }
416+
417+ // Potential SRV Overrides and SRV connection string validations
412418
413- // Potential SRV Overrides
414419 mongoOptions . userSpecifiedAuthSource =
415420 objectOptions . has ( 'authSource' ) || urlOptions . has ( 'authSource' ) ;
416421 mongoOptions . userSpecifiedReplicaSet =
417422 objectOptions . has ( 'replicaSet' ) || urlOptions . has ( 'replicaSet' ) ;
418423
419- if ( mongoClient && mongoOptions . autoEncryption ) {
420- Encrypter . checkForMongoCrypt ( ) ;
421- mongoOptions . encrypter = new Encrypter ( mongoClient , uri , options ) ;
422- mongoOptions . autoEncrypter = mongoOptions . encrypter . autoEncrypter ;
424+ if ( isSRV ) {
425+ // SRV Record is resolved upon connecting
426+ mongoOptions . srvHost = hosts [ 0 ] ;
427+
428+ if ( mongoOptions . directConnection ) {
429+ throw new MongoAPIError ( 'SRV URI does not support directConnection' ) ;
430+ }
431+
432+ if ( mongoOptions . srvMaxHosts > 0 && typeof mongoOptions . replicaSet === 'string' ) {
433+ throw new MongoParseError ( 'Cannot use srvMaxHosts option with replicaSet' ) ;
434+ }
435+
436+ // SRV turns on TLS by default, but users can override and turn it off
437+ const noUserSpecifiedTLS = ! objectOptions . has ( 'tls' ) && ! urlOptions . has ( 'tls' ) ;
438+ const noUserSpecifiedSSL = ! objectOptions . has ( 'ssl' ) && ! urlOptions . has ( 'ssl' ) ;
439+ if ( noUserSpecifiedTLS && noUserSpecifiedSSL ) {
440+ mongoOptions . tls = true ;
441+ }
442+ } else {
443+ const userSpecifiedSrvOptions =
444+ urlOptions . has ( 'srvMaxHosts' ) ||
445+ objectOptions . has ( 'srvMaxHosts' ) ||
446+ urlOptions . has ( 'srvServiceName' ) ||
447+ objectOptions . has ( 'srvServiceName' ) ;
448+
449+ if ( userSpecifiedSrvOptions ) {
450+ throw new MongoParseError (
451+ 'Cannot use srvMaxHosts or srvServiceName with a non-srv connection string'
452+ ) ;
453+ }
423454 }
424455
425456 return mongoOptions ;
426457}
427458
428459function validateLoadBalancedOptions (
429460 hosts : HostAddress [ ] | string [ ] ,
430- mongoOptions : MongoOptions
461+ mongoOptions : MongoOptions ,
462+ isSrv : boolean
431463) : MongoParseError | undefined {
432464 if ( mongoOptions . loadBalanced ) {
433465 if ( hosts . length > 1 ) {
@@ -439,6 +471,10 @@ function validateLoadBalancedOptions(
439471 if ( mongoOptions . directConnection ) {
440472 return new MongoParseError ( LB_DIRECT_CONNECTION_ERROR ) ;
441473 }
474+
475+ if ( isSrv && mongoOptions . srvMaxHosts > 0 ) {
476+ return new MongoParseError ( 'Cannot limit srv hosts with loadBalanced enabled' ) ;
477+ }
442478 }
443479}
444480
@@ -924,6 +960,14 @@ export const OPTIONS = {
924960 default : 0 ,
925961 type : 'uint'
926962 } ,
963+ srvMaxHosts : {
964+ type : 'uint' ,
965+ default : 0
966+ } ,
967+ srvServiceName : {
968+ type : 'string' ,
969+ default : 'mongodb'
970+ } ,
927971 ssl : {
928972 target : 'tls' ,
929973 type : 'boolean'
0 commit comments