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