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' ;
@@ -180,18 +179,18 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
180179 * Once connection is established, command logging can log events (if enabled)
181180 */
182181 public established : boolean ;
182+ /** Indicates that the connection (including underlying TCP socket) has been closed. */
183+ public closed = false ;
183184
184185 private lastUseTime : number ;
185186 private clusterTime : Document | null = null ;
187+ private error : Error | null = null ;
188+ private dataEvents : AsyncGenerator < Buffer , void , void > | null = null ;
186189
187190 private readonly socketTimeoutMS : number ;
188191 private readonly monitorCommands : boolean ;
189192 private readonly socket : Stream ;
190- private readonly controller : AbortController ;
191- private readonly signal : AbortSignal ;
192193 private readonly messageStream : Readable ;
193- private readonly socketWrite : ( buffer : Uint8Array ) => Promise < void > ;
194- private readonly aborted : Promise < never > ;
195194
196195 /** @event */
197196 static readonly COMMAND_STARTED = COMMAND_STARTED ;
@@ -211,6 +210,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
211210 constructor ( stream : Stream , options : ConnectionOptions ) {
212211 super ( ) ;
213212
213+ this . socket = stream ;
214214 this . id = options . id ;
215215 this . address = streamIdentifier ( stream , options ) ;
216216 this . socketTimeoutMS = options . socketTimeoutMS ?? 0 ;
@@ -223,39 +223,12 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
223223 this . generation = options . generation ;
224224 this . lastUseTime = now ( ) ;
225225
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-
243226 this . messageStream = this . socket
244227 . on ( 'error' , this . onError . bind ( this ) )
245228 . pipe ( new SizedMessageTransform ( { connection : this } ) )
246229 . on ( 'error' , this . onError . bind ( this ) ) ;
247230 this . socket . on ( 'close' , this . onClose . bind ( this ) ) ;
248231 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 ;
259232 }
260233
261234 public get hello ( ) {
@@ -355,7 +328,11 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
355328 }
356329
357330 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 ;
359336 this . emit ( Connection . CLOSE ) ;
360337 }
361338
@@ -596,7 +573,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
596573 }
597574
598575 private throwIfAborted ( ) {
599- this . signal . throwIfAborted ( ) ;
576+ if ( this . error ) throw this . error ;
600577 }
601578
602579 /**
@@ -619,7 +596,18 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
619596
620597 const buffer = Buffer . concat ( await finalCommand . toBin ( ) ) ;
621598
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+ }
623611 }
624612
625613 /**
@@ -632,13 +620,19 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
632620 * Note that `for-await` loops call `return` automatically when the loop is exited.
633621 */
634622 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 ;
638628
639- if ( ! response . moreToCome ) {
640- return ;
629+ if ( ! response . moreToCome ) {
630+ return ;
631+ }
641632 }
633+ } finally {
634+ this . dataEvents = null ;
635+ this . throwIfAborted ( ) ;
642636 }
643637 }
644638}
0 commit comments