@@ -4,11 +4,11 @@ import { BasicAuth, CredentialsError, CredentialsProvider, StreamingCredentialsP
4
4
import RedisCommandsQueue , { CommandOptions } from './commands-queue' ;
5
5
import { EventEmitter } from 'node:events' ;
6
6
import { attachConfig , functionArgumentsPrefix , getTransformReply , scriptArgumentsPrefix } from '../commander' ;
7
- import { ClientClosedError , ClientOfflineError , DisconnectsClientError , WatchError } from '../errors' ;
7
+ import { ClientClosedError , ClientOfflineError , DisconnectsClientError , SimpleError , WatchError } from '../errors' ;
8
8
import { URL } from 'node:url' ;
9
9
import { TcpSocketConnectOpts } from 'node:net' ;
10
10
import { PUBSUB_TYPE , PubSubType , PubSubListener , PubSubTypeListeners , ChannelListeners } from './pub-sub' ;
11
- import { Command , CommandSignature , TypeMapping , CommanderConfig , RedisFunction , RedisFunctions , RedisModules , RedisScript , RedisScripts , ReplyUnion , RespVersions , RedisArgument , ReplyWithTypeMapping , SimpleStringReply , TransformReply } from '../RESP/types' ;
11
+ import { Command , CommandSignature , TypeMapping , CommanderConfig , RedisFunction , RedisFunctions , RedisModules , RedisScript , RedisScripts , ReplyUnion , RespVersions , RedisArgument , ReplyWithTypeMapping , SimpleStringReply , TransformReply , CommandArguments } from '../RESP/types' ;
12
12
import RedisClientMultiCommand , { RedisClientMultiCommandType } from './multi-command' ;
13
13
import { RedisMultiQueuedCommand } from '../multi-command' ;
14
14
import HELLO , { HelloOptions } from '../commands/HELLO' ;
@@ -19,6 +19,7 @@ import { RedisVariadicArgument, parseArgs, pushVariadicArguments } from '../comm
19
19
import { BasicClientSideCache , ClientSideCacheConfig , ClientSideCacheProvider } from './cache' ;
20
20
import { BasicCommandParser , CommandParser } from './parser' ;
21
21
import SingleEntryCache from '../single-entry-cache' ;
22
+ import { version } from '../../package.json'
22
23
23
24
export interface RedisClientOptions <
24
25
M extends RedisModules = RedisModules ,
@@ -135,6 +136,14 @@ export interface RedisClientOptions<
135
136
* ```
136
137
*/
137
138
clientSideCache ?: ClientSideCacheProvider | ClientSideCacheConfig ;
139
+ /**
140
+ * If set to true, disables sending client identifier (user-agent like message) to the redis server
141
+ */
142
+ disableClientInfo ?: boolean ;
143
+ /**
144
+ * Tag to append to library name that is sent to the Redis server
145
+ */
146
+ clientInfoTag ?: string ;
138
147
}
139
148
140
149
type WithCommands <
@@ -514,7 +523,30 @@ export default class RedisClient<
514
523
} ) ;
515
524
}
516
525
517
- async #handshake( selectedDB : number ) {
526
+ async #handshake( chainId : symbol , asap : boolean ) {
527
+ const promises = [ ] ;
528
+ const commandsWithErrorHandlers = await this . #getHandshakeCommands( this . #selectedDB ?? 0 ) ;
529
+
530
+ if ( asap ) commandsWithErrorHandlers . reverse ( )
531
+
532
+ for ( const { cmd, errorHandler } of commandsWithErrorHandlers ) {
533
+ promises . push (
534
+ this . #queue
535
+ . addCommand ( cmd , {
536
+ chainId,
537
+ asap
538
+ } )
539
+ . catch ( errorHandler )
540
+ ) ;
541
+ }
542
+ return promises ;
543
+ }
544
+
545
+ async #getHandshakeCommands(
546
+ selectedDB : number
547
+ ) : Promise <
548
+ Array < { cmd : CommandArguments } & { errorHandler ?: ( err : Error ) => void } >
549
+ > {
518
550
const commands = [ ] ;
519
551
const cp = this . #options?. credentialsProvider ;
520
552
@@ -532,8 +564,8 @@ export default class RedisClient<
532
564
}
533
565
534
566
if ( cp && cp . type === 'streaming-credentials-provider' ) {
535
-
536
- const [ credentials , disposable ] = await this . #subscribeForStreamingCredentials( cp )
567
+ const [ credentials , disposable ] =
568
+ await this . #subscribeForStreamingCredentials( cp ) ;
537
569
this . #credentialsSubscription = disposable ;
538
570
539
571
if ( credentials . password ) {
@@ -548,59 +580,88 @@ export default class RedisClient<
548
580
hello . SETNAME = this . #options. name ;
549
581
}
550
582
551
- commands . push (
552
- parseArgs ( HELLO , this . #options. RESP , hello )
553
- ) ;
583
+ commands . push ( { cmd : parseArgs ( HELLO , this . #options. RESP , hello ) } ) ;
554
584
} else {
555
-
556
585
if ( cp && cp . type === 'async-credentials-provider' ) {
557
-
558
586
const credentials = await cp . credentials ( ) ;
559
587
560
588
if ( credentials . username || credentials . password ) {
561
- commands . push (
562
- parseArgs ( COMMANDS . AUTH , {
589
+ commands . push ( {
590
+ cmd : parseArgs ( COMMANDS . AUTH , {
563
591
username : credentials . username ,
564
592
password : credentials . password ?? ''
565
593
} )
566
- ) ;
594
+ } ) ;
567
595
}
568
596
}
569
597
570
598
if ( cp && cp . type === 'streaming-credentials-provider' ) {
571
-
572
- const [ credentials , disposable ] = await this . #subscribeForStreamingCredentials( cp )
599
+ const [ credentials , disposable ] =
600
+ await this . #subscribeForStreamingCredentials( cp ) ;
573
601
this . #credentialsSubscription = disposable ;
574
602
575
603
if ( credentials . username || credentials . password ) {
576
- commands . push (
577
- parseArgs ( COMMANDS . AUTH , {
604
+ commands . push ( {
605
+ cmd : parseArgs ( COMMANDS . AUTH , {
578
606
username : credentials . username ,
579
607
password : credentials . password ?? ''
580
608
} )
581
- ) ;
609
+ } ) ;
582
610
}
583
611
}
584
612
585
613
if ( this . #options?. name ) {
586
- commands . push (
587
- parseArgs ( COMMANDS . CLIENT_SETNAME , this . #options. name )
588
- ) ;
614
+ commands . push ( {
615
+ cmd : parseArgs ( COMMANDS . CLIENT_SETNAME , this . #options. name )
616
+ } ) ;
589
617
}
590
618
}
591
619
592
620
if ( selectedDB !== 0 ) {
593
- commands . push ( [ 'SELECT' , this . #selectedDB. toString ( ) ] ) ;
621
+ commands . push ( { cmd : [ 'SELECT' , this . #selectedDB. toString ( ) ] } ) ;
594
622
}
595
623
596
624
if ( this . #options?. readonly ) {
597
- commands . push (
598
- parseArgs ( COMMANDS . READONLY )
599
- ) ;
625
+ commands . push ( { cmd : parseArgs ( COMMANDS . READONLY ) } ) ;
626
+ }
627
+
628
+ if ( ! this . #options?. disableClientInfo ) {
629
+ commands . push ( {
630
+ cmd : [ 'CLIENT' , 'SETINFO' , 'LIB-VER' , version ] ,
631
+ errorHandler : ( err : Error ) => {
632
+ // Only throw if not a SimpleError - unknown subcommand
633
+ // Client libraries are expected to ignore failures
634
+ // of type SimpleError - unknown subcommand, which are
635
+ // expected from older servers ( < v7 )
636
+ if ( ! ( err instanceof SimpleError ) || ! err . isUnknownSubcommand ( ) ) {
637
+ throw err ;
638
+ }
639
+ }
640
+ } ) ;
641
+
642
+ commands . push ( {
643
+ cmd : [
644
+ 'CLIENT' ,
645
+ 'SETINFO' ,
646
+ 'LIB-NAME' ,
647
+ this . #options?. clientInfoTag
648
+ ? `node-redis(${ this . #options. clientInfoTag } )`
649
+ : 'node-redis'
650
+ ] ,
651
+ errorHandler : ( err : Error ) => {
652
+ // Only throw if not a SimpleError - unknown subcommand
653
+ // Client libraries are expected to ignore failures
654
+ // of type SimpleError - unknown subcommand, which are
655
+ // expected from older servers ( < v7 )
656
+ if ( ! ( err instanceof SimpleError ) || ! err . isUnknownSubcommand ( ) ) {
657
+ throw err ;
658
+ }
659
+ }
660
+ } ) ;
600
661
}
601
662
602
663
if ( this . #clientSideCache) {
603
- commands . push ( this . #clientSideCache. trackingOn ( ) ) ;
664
+ commands . push ( { cmd : this . #clientSideCache. trackingOn ( ) } ) ;
604
665
}
605
666
606
667
return commands ;
@@ -629,15 +690,7 @@ export default class RedisClient<
629
690
) ;
630
691
}
631
692
632
- const commands = await this . #handshake( this . #selectedDB) ;
633
- for ( let i = commands . length - 1 ; i >= 0 ; -- i ) {
634
- promises . push (
635
- this . #queue. addCommand ( commands [ i ] , {
636
- chainId,
637
- asap : true
638
- } )
639
- ) ;
640
- }
693
+ promises . push ( ...( await this . #handshake( chainId , true ) ) ) ;
641
694
642
695
if ( promises . length ) {
643
696
this . #write( ) ;
@@ -1221,13 +1274,7 @@ export default class RedisClient<
1221
1274
selectedDB = this . _self . #options?. database ?? 0 ;
1222
1275
this . _self . #credentialsSubscription?. dispose ( ) ;
1223
1276
this . _self . #credentialsSubscription = null ;
1224
- for ( const command of ( await this . _self . #handshake( selectedDB ) ) ) {
1225
- promises . push (
1226
- this . _self . #queue. addCommand ( command , {
1227
- chainId
1228
- } )
1229
- ) ;
1230
- }
1277
+ promises . push ( ...( await this . _self . #handshake( chainId , false ) ) ) ;
1231
1278
this . _self . #scheduleWrite( ) ;
1232
1279
await Promise . all ( promises ) ;
1233
1280
this . _self . #selectedDB = selectedDB ;
0 commit comments