1
+ import { once } from 'events' ;
1
2
import { promises as fs } from 'fs' ;
2
3
import type { TcpNetConnectOpts } from 'net' ;
3
4
import type { ConnectionOptions as TLSConnectionOptions , TLSSocketOptions } from 'tls' ;
@@ -20,7 +21,7 @@ import { parseOptions, resolveSRVRecord } from './connection_string';
20
21
import { MONGO_CLIENT_EVENTS } from './constants' ;
21
22
import { Db , type DbOptions } from './db' ;
22
23
import type { Encrypter } from './encrypter' ;
23
- import { MongoInvalidArgumentError } from './error' ;
24
+ import { MongoInvalidArgumentError , MongoNetworkTimeoutError } from './error' ;
24
25
import { MongoClientAuthProviders } from './mongo_client_auth_providers' ;
25
26
import {
26
27
type LogComponentSeveritiesClientOptions ,
@@ -34,12 +35,15 @@ import { executeOperation } from './operations/execute_operation';
34
35
import { RunAdminCommandOperation } from './operations/run_command' ;
35
36
import type { ReadConcern , ReadConcernLevel , ReadConcernLike } from './read_concern' ;
36
37
import { ReadPreference , type ReadPreferenceMode } from './read_preference' ;
38
+ import { STATE_CONNECTED , STATE_CONNECTING } from './sdam/common' ;
37
39
import type { ServerMonitoringMode } from './sdam/monitor' ;
40
+ import { Server } from './sdam/server' ;
38
41
import type { TagSet } from './sdam/server_description' ;
39
42
import { readPreferenceServerSelector } from './sdam/server_selection' ;
40
43
import type { SrvPoller } from './sdam/srv_polling' ;
41
44
import { Topology , type TopologyEvents } from './sdam/topology' ;
42
45
import { ClientSession , type ClientSessionOptions , ServerSessionPool } from './sessions' ;
46
+ import { Timeout } from './timeout' ;
43
47
import {
44
48
COSMOS_DB_CHECK ,
45
49
COSMOS_DB_MSG ,
@@ -469,12 +473,20 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
469
473
* @see docs.mongodb.org/manual/reference/connection-string/
470
474
*/
471
475
async connect ( ) : Promise < this> {
476
+ return await this . _connectWithLock ( ) ;
477
+ }
478
+
479
+ /** @internal */
480
+ async _connectWithLock ( options ?: {
481
+ skipPing : boolean ;
482
+ readPreference ?: ReadPreference ;
483
+ } ) : Promise < this> {
472
484
if ( this . connectionLock ) {
473
485
return await this . connectionLock ;
474
486
}
475
487
476
488
try {
477
- this . connectionLock = this . _connect ( ) ;
489
+ this . connectionLock = this . _connect ( options ) ;
478
490
await this . connectionLock ;
479
491
} finally {
480
492
// release
@@ -490,44 +502,78 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
490
502
*
491
503
* @internal
492
504
*/
493
- private async _connect ( ) : Promise < this> {
505
+ private async _connect ( options ?: {
506
+ skipPing : boolean ;
507
+ readPreference ?: ReadPreference ;
508
+ } ) : Promise < this> {
509
+ const topology = await this . initTopology ( ) ;
510
+ const readPreference = options ?. readPreference ?? this . readPreference ;
511
+ const { encrypter } = this [ kOptions ] ;
512
+ const topologyConnect = async ( ) => {
513
+ try {
514
+ const skipPingOnConnect = this . s . options [ Symbol . for ( '@@mdb.skipPingOnConnect' ) ] === true ;
515
+ if ( ! skipPingOnConnect && ! options ?. skipPing && topology . s . credentials != null ) {
516
+ await this . db ( ) . admin ( ) . ping ( { readPreference } ) ; // performs server selection and sends ping
517
+ topology . stateTransition ( STATE_CONNECTED ) ;
518
+ topology . emit ( Topology . OPEN , topology ) ;
519
+ topology . emit ( Topology . CONNECT , topology ) ;
520
+ }
521
+ } catch ( error ) {
522
+ topology . close ( ) ;
523
+ throw error ;
524
+ }
525
+ } ;
526
+
527
+ if ( this . autoEncrypter ) {
528
+ await this . autoEncrypter ?. init ( ) ;
529
+ await topologyConnect ( ) ;
530
+ await encrypter . connectInternalClient ( ) ;
531
+ } else {
532
+ await topologyConnect ( ) ;
533
+ }
534
+
535
+ return this ;
536
+ }
537
+
538
+ /** @internal */
539
+ async initTopology ( ) {
494
540
if ( this . topology && this . topology . isConnected ( ) ) {
495
- return this ;
541
+ return this . topology ;
496
542
}
497
543
498
- const options = this [ kOptions ] ;
544
+ const topologyOptions = this [ kOptions ] ;
499
545
500
- if ( options . tls ) {
501
- if ( typeof options . tlsCAFile === 'string' ) {
502
- options . ca ??= await fs . readFile ( options . tlsCAFile ) ;
546
+ if ( topologyOptions . tls ) {
547
+ if ( typeof topologyOptions . tlsCAFile === 'string' ) {
548
+ topologyOptions . ca ??= await fs . readFile ( topologyOptions . tlsCAFile ) ;
503
549
}
504
- if ( typeof options . tlsCRLFile === 'string' ) {
505
- options . crl ??= await fs . readFile ( options . tlsCRLFile ) ;
550
+ if ( typeof topologyOptions . tlsCRLFile === 'string' ) {
551
+ topologyOptions . crl ??= await fs . readFile ( topologyOptions . tlsCRLFile ) ;
506
552
}
507
- if ( typeof options . tlsCertificateKeyFile === 'string' ) {
508
- if ( ! options . key || ! options . cert ) {
509
- const contents = await fs . readFile ( options . tlsCertificateKeyFile ) ;
510
- options . key ??= contents ;
511
- options . cert ??= contents ;
553
+ if ( typeof topologyOptions . tlsCertificateKeyFile === 'string' ) {
554
+ if ( ! topologyOptions . key || ! topologyOptions . cert ) {
555
+ const contents = await fs . readFile ( topologyOptions . tlsCertificateKeyFile ) ;
556
+ topologyOptions . key ??= contents ;
557
+ topologyOptions . cert ??= contents ;
512
558
}
513
559
}
514
560
}
515
- if ( typeof options . srvHost === 'string' ) {
516
- const hosts = await resolveSRVRecord ( options ) ;
561
+ if ( typeof topologyOptions . srvHost === 'string' ) {
562
+ const hosts = await resolveSRVRecord ( topologyOptions ) ;
517
563
518
564
for ( const [ index , host ] of hosts . entries ( ) ) {
519
- options . hosts [ index ] = host ;
565
+ topologyOptions . hosts [ index ] = host ;
520
566
}
521
567
}
522
568
523
569
// It is important to perform validation of hosts AFTER SRV resolution, to check the real hostname,
524
570
// but BEFORE we even attempt connecting with a potentially not allowed hostname
525
- if ( options . credentials ?. mechanism === AuthMechanism . MONGODB_OIDC ) {
571
+ if ( topologyOptions . credentials ?. mechanism === AuthMechanism . MONGODB_OIDC ) {
526
572
const allowedHosts =
527
- options . credentials ?. mechanismProperties ?. ALLOWED_HOSTS || DEFAULT_ALLOWED_HOSTS ;
528
- const isServiceAuth = ! ! options . credentials ?. mechanismProperties ?. ENVIRONMENT ;
573
+ topologyOptions . credentials ?. mechanismProperties ?. ALLOWED_HOSTS || DEFAULT_ALLOWED_HOSTS ;
574
+ const isServiceAuth = ! ! topologyOptions . credentials ?. mechanismProperties ?. ENVIRONMENT ;
529
575
if ( ! isServiceAuth ) {
530
- for ( const host of options . hosts ) {
576
+ for ( const host of topologyOptions . hosts ) {
531
577
if ( ! hostMatchesWildcards ( host . toHostPort ( ) . host , allowedHosts ) ) {
532
578
throw new MongoInvalidArgumentError (
533
579
`Host '${ host } ' is not valid for OIDC authentication with ALLOWED_HOSTS of '${ allowedHosts . join (
@@ -539,7 +585,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
539
585
}
540
586
}
541
587
542
- this . topology = new Topology ( this , options . hosts , options ) ;
588
+ this . topology = new Topology ( this , topologyOptions . hosts , topologyOptions ) ;
543
589
// Events can be emitted before initialization is complete so we have to
544
590
// save the reference to the topology on the client ASAP if the event handlers need to access it
545
591
@@ -549,24 +595,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
549
595
this . topology . on ( event , ( ...args : any [ ] ) => this . emit ( event , ...( args as any ) ) ) ;
550
596
}
551
597
552
- const topologyConnect = async ( ) => {
553
- try {
554
- await this . topology ?. connect ( options ) ;
555
- } catch ( error ) {
556
- this . topology ?. close ( ) ;
557
- throw error ;
558
- }
559
- } ;
560
-
561
- if ( this . autoEncrypter ) {
562
- await this . autoEncrypter ?. init ( ) ;
563
- await topologyConnect ( ) ;
564
- await options . encrypter . connectInternalClient ( ) ;
565
- } else {
566
- await topologyConnect ( ) ;
567
- }
568
-
569
- return this ;
598
+ return this . topology ;
570
599
}
571
600
572
601
/**
0 commit comments