11import { expect } from 'chai' ;
2+ import { execSync } from 'child_process' ;
3+ import * as crypto from 'crypto' ;
4+ import * as net from 'net' ;
25import * as process from 'process' ;
6+ import * as sinon from 'sinon' ;
7+ import * as tls from 'tls' ;
38
49import {
510 CancellationToken ,
@@ -11,6 +16,7 @@ import {
1116 isHello ,
1217 LEGACY_HELLO_COMMAND ,
1318 makeClientMetadata ,
19+ makeSocket ,
1420 MongoClientAuthProviders ,
1521 MongoCredentials ,
1622 MongoNetworkError ,
@@ -448,4 +454,119 @@ describe('Connect Tests', function () {
448454 } ) ;
449455 } ) ;
450456 } ) ;
457+
458+ describe ( 'makeSocket' , function ( ) {
459+ // TLS sockets created by tls.connect() do not honor keepAlive/noDelay constructor
460+ // options due to a Node.js bug (options are not forwarded to the net.Socket constructor).
461+ // The driver must call setKeepAlive/setNoDelay explicitly on all sockets.
462+ // See: https://github.com/nodejs/node/issues/...
463+
464+ let tlsServer : tls . Server ;
465+ let tlsPort : number ;
466+ let setKeepAliveSpy : sinon . SinonSpy ;
467+ let setNoDelaySpy : sinon . SinonSpy ;
468+
469+ before ( function ( done ) {
470+ const { privateKey } = crypto . generateKeyPairSync ( 'rsa' , { modulusLength : 2048 } ) ;
471+ const key = privateKey . export ( { type : 'pkcs8' , format : 'pem' } ) ;
472+ const cert = execSync (
473+ 'openssl req -new -x509 -key /dev/stdin -out /dev/stdout -days 1 -subj /CN=localhost -batch 2>/dev/null' ,
474+ { input : key }
475+ ) . toString ( ) ;
476+
477+ tlsServer = tls . createServer ( { key, cert } , ( ) => {
478+ /* empty */
479+ } ) ;
480+ tlsServer . listen ( 0 , '127.0.0.1' , ( ) => {
481+ tlsPort = ( tlsServer . address ( ) as net . AddressInfo ) . port ;
482+ done ( ) ;
483+ } ) ;
484+ } ) ;
485+
486+ after ( function ( ) {
487+ tlsServer ?. close ( ) ;
488+ } ) ;
489+
490+ beforeEach ( function ( ) {
491+ setKeepAliveSpy = sinon . spy ( net . Socket . prototype , 'setKeepAlive' ) ;
492+ setNoDelaySpy = sinon . spy ( net . Socket . prototype , 'setNoDelay' ) ;
493+ } ) ;
494+
495+ afterEach ( function ( ) {
496+ sinon . restore ( ) ;
497+ } ) ;
498+
499+ context ( 'when tls is enabled' , function ( ) {
500+ it ( 'calls setKeepAlive with default keepAliveInitialDelay' , async function ( ) {
501+ const socket = await makeSocket ( {
502+ hostAddress : new HostAddress ( `127.0.0.1:${ tlsPort } ` ) ,
503+ tls : true ,
504+ rejectUnauthorized : false
505+ } as ConnectionOptions ) ;
506+
507+ try {
508+ expect ( setKeepAliveSpy ) . to . have . been . calledWith ( true , 120000 ) ;
509+ } finally {
510+ socket . destroy ( ) ;
511+ }
512+ } ) ;
513+
514+ it ( 'calls setKeepAlive with custom keepAliveInitialDelay' , async function ( ) {
515+ const socket = await makeSocket ( {
516+ hostAddress : new HostAddress ( `127.0.0.1:${ tlsPort } ` ) ,
517+ tls : true ,
518+ rejectUnauthorized : false ,
519+ keepAliveInitialDelay : 5000
520+ } as ConnectionOptions ) ;
521+
522+ try {
523+ expect ( setKeepAliveSpy ) . to . have . been . calledWith ( true , 5000 ) ;
524+ } finally {
525+ socket . destroy ( ) ;
526+ }
527+ } ) ;
528+
529+ it ( 'calls setNoDelay with true by default' , async function ( ) {
530+ const socket = await makeSocket ( {
531+ hostAddress : new HostAddress ( `127.0.0.1:${ tlsPort } ` ) ,
532+ tls : true ,
533+ rejectUnauthorized : false
534+ } as ConnectionOptions ) ;
535+
536+ try {
537+ expect ( setNoDelaySpy ) . to . have . been . calledWith ( true ) ;
538+ } finally {
539+ socket . destroy ( ) ;
540+ }
541+ } ) ;
542+ } ) ;
543+
544+ context ( 'when tls is disabled' , function ( ) {
545+ it ( 'calls setKeepAlive with default keepAliveInitialDelay' , async function ( ) {
546+ const socket = await makeSocket ( {
547+ hostAddress : new HostAddress ( `127.0.0.1:${ tlsPort } ` ) ,
548+ tls : false
549+ } as ConnectionOptions ) ;
550+
551+ try {
552+ expect ( setKeepAliveSpy ) . to . have . been . calledWith ( true , 120000 ) ;
553+ } finally {
554+ socket . destroy ( ) ;
555+ }
556+ } ) ;
557+
558+ it ( 'calls setNoDelay with true by default' , async function ( ) {
559+ const socket = await makeSocket ( {
560+ hostAddress : new HostAddress ( `127.0.0.1:${ tlsPort } ` ) ,
561+ tls : false
562+ } as ConnectionOptions ) ;
563+
564+ try {
565+ expect ( setNoDelaySpy ) . to . have . been . calledWith ( true ) ;
566+ } finally {
567+ socket . destroy ( ) ;
568+ }
569+ } ) ;
570+ } ) ;
571+ } ) ;
451572} ) ;
0 commit comments