11import { type Readable , Transform , type TransformCallback } from 'stream' ;
22import { clearTimeout , setTimeout } from 'timers' ;
3- import { promisify } from 'util' ;
43
54import type { BSONSerializeOptions , Document , ObjectId } from '../bson' ;
65import type { AutoEncrypter } from '../client-side-encryption/auto_encrypter' ;
@@ -182,18 +181,18 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
182181 * Once connection is established, command logging can log events (if enabled)
183182 */
184183 public established : boolean ;
184+ /** Indicates that the connection (including underlying TCP socket) has been closed. */
185+ public closed = false ;
185186
186187 private lastUseTime : number ;
187188 private clusterTime : Document | null = null ;
189+ private error : Error | null = null ;
190+ private dataEvents : AsyncGenerator < Buffer , void , void > | null = null ;
188191
189192 private readonly socketTimeoutMS : number ;
190193 private readonly monitorCommands : boolean ;
191194 private readonly socket : Stream ;
192- private readonly controller : AbortController ;
193- private readonly signal : AbortSignal ;
194195 private readonly messageStream : Readable ;
195- private readonly socketWrite : ( buffer : Uint8Array ) => Promise < void > ;
196- private readonly aborted : Promise < never > ;
197196
198197 /** @event */
199198 static readonly COMMAND_STARTED = COMMAND_STARTED ;
@@ -213,6 +212,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
213212 constructor ( stream : Stream , options : ConnectionOptions ) {
214213 super ( ) ;
215214
215+ this . socket = stream ;
216216 this . id = options . id ;
217217 this . address = streamIdentifier ( stream , options ) ;
218218 this . socketTimeoutMS = options . socketTimeoutMS ?? 0 ;
@@ -225,39 +225,12 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
225225 this . generation = options . generation ;
226226 this . lastUseTime = now ( ) ;
227227
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-
245228 this . messageStream = this . socket
246229 . on ( 'error' , this . onError . bind ( this ) )
247230 . pipe ( new SizedMessageTransform ( { connection : this } ) )
248231 . on ( 'error' , this . onError . bind ( this ) ) ;
249232 this . socket . on ( 'close' , this . onClose . bind ( this ) ) ;
250233 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 ;
261234 }
262235
263236 public get hello ( ) {
@@ -357,7 +330,11 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
357330 }
358331
359332 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 ;
361338 this . emit ( Connection . CLOSE ) ;
362339 }
363340
@@ -598,7 +575,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
598575 }
599576
600577 private throwIfAborted ( ) {
601- this . signal . throwIfAborted ( ) ;
578+ if ( this . error ) throw this . error ;
602579 }
603580
604581 /**
@@ -621,7 +598,18 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
621598
622599 const buffer = Buffer . concat ( await finalCommand . toBin ( ) ) ;
623600
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+ }
625613 }
626614
627615 /**
@@ -634,13 +622,19 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
634622 * Note that `for-await` loops call `return` automatically when the loop is exited.
635623 */
636624 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 ;
640630
641- if ( ! response . moreToCome ) {
642- return ;
631+ if ( ! response . moreToCome ) {
632+ return ;
633+ }
643634 }
635+ } finally {
636+ this . dataEvents = null ;
637+ this . throwIfAborted ( ) ;
644638 }
645639 }
646640}
0 commit comments