@@ -29,14 +29,14 @@ import { type CancellationToken, TypedEventEmitter } from '../mongo_types';
29
29
import type { ReadPreferenceLike } from '../read_preference' ;
30
30
import { applySession , type ClientSession , updateSessionFromResponse } from '../sessions' ;
31
31
import {
32
- abortable ,
33
32
BufferPool ,
34
33
calculateDurationInMs ,
35
34
type Callback ,
36
35
HostAddress ,
37
36
maxWireVersion ,
38
37
type MongoDBNamespace ,
39
38
now ,
39
+ promiseWithResolvers ,
40
40
uuidV4
41
41
} from '../utils' ;
42
42
import type { WriteConcern } from '../write_concern' ;
@@ -161,15 +161,14 @@ function streamIdentifier(stream: Stream, options: ConnectionOptions): string {
161
161
export class Connection extends TypedEventEmitter < ConnectionEvents > {
162
162
public id : number | '<monitor>' ;
163
163
public address : string ;
164
- public lastHelloMS ?: number ;
164
+ public lastHelloMS = - 1 ;
165
165
public serverApi ?: ServerApi ;
166
- public helloOk ?: boolean ;
166
+ public helloOk = false ;
167
167
public authContext ?: AuthContext ;
168
168
public delayedTimeoutId : NodeJS . Timeout | null = null ;
169
169
public generation : number ;
170
170
public readonly description : Readonly < StreamDescription > ;
171
171
/**
172
- * @public
173
172
* Represents if the connection has been established:
174
173
* - TCP handshake
175
174
* - TLS negotiated
@@ -180,15 +179,16 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
180
179
public established : boolean ;
181
180
182
181
private lastUseTime : number ;
183
- private socketTimeoutMS : number ;
184
- private monitorCommands : boolean ;
185
- private socket : Stream ;
186
- private controller : AbortController ;
187
- private messageStream : Readable ;
188
- private socketWrite : ( buffer : Uint8Array ) => Promise < void > ;
189
182
private clusterTime : Document | null = null ;
190
- /** @internal */
191
- override mongoLogger : MongoLogger | undefined ;
183
+
184
+ private readonly socketTimeoutMS : number ;
185
+ private readonly monitorCommands : boolean ;
186
+ private readonly socket : Stream ;
187
+ private readonly controller : AbortController ;
188
+ private readonly signal : AbortSignal ;
189
+ private readonly messageStream : Readable ;
190
+ private readonly socketWrite : ( buffer : Uint8Array ) => Promise < void > ;
191
+ private readonly aborted : Promise < never > ;
192
192
193
193
/** @event */
194
194
static readonly COMMAND_STARTED = COMMAND_STARTED ;
@@ -221,7 +221,21 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
221
221
this . lastUseTime = now ( ) ;
222
222
223
223
this . socket = stream ;
224
+
225
+ // TODO: Remove signal from connection layer
224
226
this . controller = new AbortController ( ) ;
227
+ const { signal } = this . controller ;
228
+ this . signal = signal ;
229
+ const { promise : aborted , reject } = promiseWithResolvers < never > ( ) ;
230
+ aborted . then ( undefined , ( ) => null ) ; // Prevent unhandled rejection
231
+ this . signal . addEventListener (
232
+ 'abort' ,
233
+ function onAbort ( ) {
234
+ reject ( signal . reason ) ;
235
+ } ,
236
+ { once : true }
237
+ ) ;
238
+ this . aborted = aborted ;
225
239
226
240
this . messageStream = this . socket
227
241
. on ( 'error' , this . onError . bind ( this ) )
@@ -232,13 +246,13 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
232
246
233
247
const socketWrite = promisify ( this . socket . write . bind ( this . socket ) ) ;
234
248
this . socketWrite = async buffer => {
235
- return abortable ( socketWrite ( buffer ) , { signal : this . controller . signal } ) ;
249
+ return Promise . race ( [ socketWrite ( buffer ) , this . aborted ] ) ;
236
250
} ;
237
251
}
238
252
239
253
/** Indicates that the connection (including underlying TCP socket) has been closed. */
240
254
public get closed ( ) : boolean {
241
- return this . controller . signal . aborted ;
255
+ return this . signal . aborted ;
242
256
}
243
257
244
258
public get hello ( ) {
@@ -407,7 +421,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
407
421
}
408
422
409
423
private async * sendWire ( message : WriteProtocolMessageType , options : CommandOptions ) {
410
- this . controller . signal . throwIfAborted ( ) ;
424
+ this . throwIfAborted ( ) ;
411
425
412
426
if ( typeof options . socketTimeoutMS === 'number' ) {
413
427
this . socket . setTimeout ( options . socketTimeoutMS ) ;
@@ -426,7 +440,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
426
440
return ;
427
441
}
428
442
429
- this . controller . signal . throwIfAborted ( ) ;
443
+ this . throwIfAborted ( ) ;
430
444
431
445
for await ( const response of this . readMany ( ) ) {
432
446
this . socket . setTimeout ( 0 ) ;
@@ -447,7 +461,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
447
461
}
448
462
449
463
yield document ;
450
- this . controller . signal . throwIfAborted ( ) ;
464
+ this . throwIfAborted ( ) ;
451
465
452
466
if ( typeof options . socketTimeoutMS === 'number' ) {
453
467
this . socket . setTimeout ( options . socketTimeoutMS ) ;
@@ -481,7 +495,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
481
495
482
496
let document ;
483
497
try {
484
- this . controller . signal . throwIfAborted ( ) ;
498
+ this . throwIfAborted ( ) ;
485
499
for await ( document of this . sendWire ( message , options ) ) {
486
500
if ( ! Buffer . isBuffer ( document ) && document . writeConcernError ) {
487
501
throw new MongoWriteConcernError ( document . writeConcernError , document ) ;
@@ -511,7 +525,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
511
525
}
512
526
513
527
yield document ;
514
- this . controller . signal . throwIfAborted ( ) ;
528
+ this . throwIfAborted ( ) ;
515
529
}
516
530
} catch ( error ) {
517
531
if ( this . shouldEmitAndLogCommand ) {
@@ -554,7 +568,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
554
568
command : Document ,
555
569
options : CommandOptions = { }
556
570
) : Promise < Document > {
557
- this . controller . signal . throwIfAborted ( ) ;
571
+ this . throwIfAborted ( ) ;
558
572
for await ( const document of this . sendCommand ( ns , command , options ) ) {
559
573
return document ;
560
574
}
@@ -568,16 +582,20 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
568
582
replyListener : Callback
569
583
) {
570
584
const exhaustLoop = async ( ) => {
571
- this . controller . signal . throwIfAborted ( ) ;
585
+ this . throwIfAborted ( ) ;
572
586
for await ( const reply of this . sendCommand ( ns , command , options ) ) {
573
587
replyListener ( undefined , reply ) ;
574
- this . controller . signal . throwIfAborted ( ) ;
588
+ this . throwIfAborted ( ) ;
575
589
}
576
590
throw new MongoUnexpectedServerResponseError ( 'Server ended moreToCome unexpectedly' ) ;
577
591
} ;
578
592
exhaustLoop ( ) . catch ( replyListener ) ;
579
593
}
580
594
595
+ private throwIfAborted ( ) {
596
+ this . signal . throwIfAborted ( ) ;
597
+ }
598
+
581
599
/**
582
600
* @internal
583
601
*
@@ -611,7 +629,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
611
629
* Note that `for-await` loops call `return` automatically when the loop is exited.
612
630
*/
613
631
private async * readMany ( ) : AsyncGenerator < OpMsgResponse | OpQueryResponse > {
614
- for await ( const message of onData ( this . messageStream , { signal : this . controller . signal } ) ) {
632
+ for await ( const message of onData ( this . messageStream , { signal : this . signal } ) ) {
615
633
const response = await decompressResponse ( message ) ;
616
634
yield response ;
617
635
0 commit comments