1
1
import { type Readable , Transform , type TransformCallback } from 'stream' ;
2
2
import { clearTimeout , setTimeout } from 'timers' ;
3
- import { promisify } from 'util' ;
4
3
5
4
import type { BSONSerializeOptions , Document , ObjectId } from '../bson' ;
6
5
import type { AutoEncrypter } from '../client-side-encryption/auto_encrypter' ;
@@ -182,18 +181,18 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
182
181
* Once connection is established, command logging can log events (if enabled)
183
182
*/
184
183
public established : boolean ;
184
+ /** Indicates that the connection (including underlying TCP socket) has been closed. */
185
+ public closed = false ;
185
186
186
187
private lastUseTime : number ;
187
188
private clusterTime : Document | null = null ;
189
+ private error : Error | null = null ;
190
+ private dataEvents : AsyncGenerator < Buffer , void , void > | null = null ;
188
191
189
192
private readonly socketTimeoutMS : number ;
190
193
private readonly monitorCommands : boolean ;
191
194
private readonly socket : Stream ;
192
- private readonly controller : AbortController ;
193
- private readonly signal : AbortSignal ;
194
195
private readonly messageStream : Readable ;
195
- private readonly socketWrite : ( buffer : Uint8Array ) => Promise < void > ;
196
- private readonly aborted : Promise < never > ;
197
196
198
197
/** @event */
199
198
static readonly COMMAND_STARTED = COMMAND_STARTED ;
@@ -213,6 +212,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
213
212
constructor ( stream : Stream , options : ConnectionOptions ) {
214
213
super ( ) ;
215
214
215
+ this . socket = stream ;
216
216
this . id = options . id ;
217
217
this . address = streamIdentifier ( stream , options ) ;
218
218
this . socketTimeoutMS = options . socketTimeoutMS ?? 0 ;
@@ -225,39 +225,12 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
225
225
this . generation = options . generation ;
226
226
this . lastUseTime = now ( ) ;
227
227
228
- this . socket = stream ;
229
-
230
- // TODO: Remove signal from connection layer
231
- this . controller = new AbortController ( ) ;
232
- const { signal } = this . controller ;
233
- this . signal = signal ;
234
- const { promise : aborted , reject } = promiseWithResolvers < never > ( ) ;
235
- aborted . then ( undefined , ( ) => null ) ; // Prevent unhandled rejection
236
- this . signal . addEventListener (
237
- 'abort' ,
238
- function onAbort ( ) {
239
- reject ( signal . reason ) ;
240
- } ,
241
- { once : true }
242
- ) ;
243
- this . aborted = aborted ;
244
-
245
228
this . messageStream = this . socket
246
229
. on ( 'error' , this . onError . bind ( this ) )
247
230
. pipe ( new SizedMessageTransform ( { connection : this } ) )
248
231
. on ( 'error' , this . onError . bind ( this ) ) ;
249
232
this . socket . on ( 'close' , this . onClose . bind ( this ) ) ;
250
233
this . socket . on ( 'timeout' , this . onTimeout . bind ( this ) ) ;
251
-
252
- const socketWrite = promisify ( this . socket . write . bind ( this . socket ) ) ;
253
- this . socketWrite = async buffer => {
254
- return Promise . race ( [ socketWrite ( buffer ) , this . aborted ] ) ;
255
- } ;
256
- }
257
-
258
- /** Indicates that the connection (including underlying TCP socket) has been closed. */
259
- public get closed ( ) : boolean {
260
- return this . signal . aborted ;
261
234
}
262
235
263
236
public get hello ( ) {
@@ -357,7 +330,11 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
357
330
}
358
331
359
332
this . socket . destroy ( ) ;
360
- this . controller . abort ( error ) ;
333
+ if ( error ) {
334
+ this . error = error ;
335
+ this . dataEvents ?. throw ( error ) . then ( undefined , ( ) => null ) ; // squash unhandled rejection
336
+ }
337
+ this . closed = true ;
361
338
this . emit ( Connection . CLOSE ) ;
362
339
}
363
340
@@ -598,7 +575,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
598
575
}
599
576
600
577
private throwIfAborted ( ) {
601
- this . signal . throwIfAborted ( ) ;
578
+ if ( this . error ) throw this . error ;
602
579
}
603
580
604
581
/**
@@ -621,7 +598,18 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
621
598
622
599
const buffer = Buffer . concat ( await finalCommand . toBin ( ) ) ;
623
600
624
- return this . socketWrite ( buffer ) ;
601
+ if ( this . socket . write ( buffer ) ) return ;
602
+
603
+ const { promise : drained , resolve, reject } = promiseWithResolvers < void > ( ) ;
604
+ const onDrain = ( ) => resolve ( ) ;
605
+ const onError = ( error : Error ) => reject ( error ) ;
606
+
607
+ this . socket . once ( 'drain' , onDrain ) . once ( 'error' , onError ) ;
608
+ try {
609
+ return await drained ;
610
+ } finally {
611
+ this . socket . off ( 'drain' , onDrain ) . off ( 'error' , onError ) ;
612
+ }
625
613
}
626
614
627
615
/**
@@ -634,13 +622,19 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
634
622
* Note that `for-await` loops call `return` automatically when the loop is exited.
635
623
*/
636
624
private async * readMany ( ) : AsyncGenerator < OpMsgResponse | OpQueryResponse > {
637
- for await ( const message of onData ( this . messageStream , { signal : this . signal } ) ) {
638
- const response = await decompressResponse ( message ) ;
639
- yield response ;
625
+ try {
626
+ this . dataEvents = this . dataEvents = onData ( this . messageStream ) ;
627
+ for await ( const message of this . dataEvents ) {
628
+ const response = await decompressResponse ( message ) ;
629
+ yield response ;
640
630
641
- if ( ! response . moreToCome ) {
642
- return ;
631
+ if ( ! response . moreToCome ) {
632
+ return ;
633
+ }
643
634
}
635
+ } finally {
636
+ this . dataEvents = null ;
637
+ this . throwIfAborted ( ) ;
644
638
}
645
639
}
646
640
}
0 commit comments