@@ -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,28 @@ 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( ) ;
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( ) : Promise <
546
+ Array < { cmd : CommandArguments } & { errorHandler ?: ( err : Error ) => void } >
547
+ > {
518
548
const commands = [ ] ;
519
549
const cp = this . #options?. credentialsProvider ;
520
550
@@ -532,8 +562,8 @@ export default class RedisClient<
532
562
}
533
563
534
564
if ( cp && cp . type === 'streaming-credentials-provider' ) {
535
-
536
- const [ credentials , disposable ] = await this . #subscribeForStreamingCredentials( cp )
565
+ const [ credentials , disposable ] =
566
+ await this . #subscribeForStreamingCredentials( cp ) ;
537
567
this . #credentialsSubscription = disposable ;
538
568
539
569
if ( credentials . password ) {
@@ -548,59 +578,88 @@ export default class RedisClient<
548
578
hello . SETNAME = this . #options. name ;
549
579
}
550
580
551
- commands . push (
552
- parseArgs ( HELLO , this . #options. RESP , hello )
553
- ) ;
581
+ commands . push ( { cmd : parseArgs ( HELLO , this . #options. RESP , hello ) } ) ;
554
582
} else {
555
-
556
583
if ( cp && cp . type === 'async-credentials-provider' ) {
557
-
558
584
const credentials = await cp . credentials ( ) ;
559
585
560
586
if ( credentials . username || credentials . password ) {
561
- commands . push (
562
- parseArgs ( COMMANDS . AUTH , {
587
+ commands . push ( {
588
+ cmd : parseArgs ( COMMANDS . AUTH , {
563
589
username : credentials . username ,
564
590
password : credentials . password ?? ''
565
591
} )
566
- ) ;
592
+ } ) ;
567
593
}
568
594
}
569
595
570
596
if ( cp && cp . type === 'streaming-credentials-provider' ) {
571
-
572
- const [ credentials , disposable ] = await this . #subscribeForStreamingCredentials( cp )
597
+ const [ credentials , disposable ] =
598
+ await this . #subscribeForStreamingCredentials( cp ) ;
573
599
this . #credentialsSubscription = disposable ;
574
600
575
601
if ( credentials . username || credentials . password ) {
576
- commands . push (
577
- parseArgs ( COMMANDS . AUTH , {
602
+ commands . push ( {
603
+ cmd : parseArgs ( COMMANDS . AUTH , {
578
604
username : credentials . username ,
579
605
password : credentials . password ?? ''
580
606
} )
581
- ) ;
607
+ } ) ;
582
608
}
583
609
}
584
610
585
611
if ( this . #options?. name ) {
586
- commands . push (
587
- parseArgs ( COMMANDS . CLIENT_SETNAME , this . #options. name )
588
- ) ;
612
+ commands . push ( {
613
+ cmd : parseArgs ( COMMANDS . CLIENT_SETNAME , this . #options. name )
614
+ } ) ;
589
615
}
590
616
}
591
617
592
- if ( selectedDB !== 0 ) {
593
- commands . push ( [ 'SELECT' , this . #selectedDB. toString ( ) ] ) ;
618
+ if ( this . # selectedDB !== 0 ) {
619
+ commands . push ( { cmd : [ 'SELECT' , this . #selectedDB. toString ( ) ] } ) ;
594
620
}
595
621
596
622
if ( this . #options?. readonly ) {
597
- commands . push (
598
- parseArgs ( COMMANDS . READONLY )
599
- ) ;
623
+ commands . push ( { cmd : parseArgs ( COMMANDS . READONLY ) } ) ;
624
+ }
625
+
626
+ if ( ! this . #options?. disableClientInfo ) {
627
+ commands . push ( {
628
+ cmd : [ 'CLIENT' , 'SETINFO' , 'LIB-VER' , version ] ,
629
+ errorHandler : ( err : Error ) => {
630
+ // Only throw if not a SimpleError - unknown subcommand
631
+ // Client libraries are expected to ignore failures
632
+ // of type SimpleError - unknown subcommand, which are
633
+ // expected from older servers ( < v7 )
634
+ if ( ! ( err instanceof SimpleError ) || ! err . isUnknownSubcommand ( ) ) {
635
+ throw err ;
636
+ }
637
+ }
638
+ } ) ;
639
+
640
+ commands . push ( {
641
+ cmd : [
642
+ 'CLIENT' ,
643
+ 'SETINFO' ,
644
+ 'LIB-NAME' ,
645
+ this . #options?. clientInfoTag
646
+ ? `node-redis(${ this . #options. clientInfoTag } )`
647
+ : 'node-redis'
648
+ ] ,
649
+ errorHandler : ( err : Error ) => {
650
+ // Only throw if not a SimpleError - unknown subcommand
651
+ // Client libraries are expected to ignore failures
652
+ // of type SimpleError - unknown subcommand, which are
653
+ // expected from older servers ( < v7 )
654
+ if ( ! ( err instanceof SimpleError ) || ! err . isUnknownSubcommand ( ) ) {
655
+ throw err ;
656
+ }
657
+ }
658
+ } ) ;
600
659
}
601
660
602
661
if ( this . #clientSideCache) {
603
- commands . push ( this . #clientSideCache. trackingOn ( ) ) ;
662
+ commands . push ( { cmd : this . #clientSideCache. trackingOn ( ) } ) ;
604
663
}
605
664
606
665
return commands ;
@@ -629,15 +688,7 @@ export default class RedisClient<
629
688
) ;
630
689
}
631
690
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
- }
691
+ promises . push ( ...( await this . #handshake( chainId , true ) ) ) ;
641
692
642
693
if ( promises . length ) {
643
694
this . #write( ) ;
@@ -1221,13 +1272,7 @@ export default class RedisClient<
1221
1272
selectedDB = this . _self . #options?. database ?? 0 ;
1222
1273
this . _self . #credentialsSubscription?. dispose ( ) ;
1223
1274
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
- }
1275
+ promises . push ( ...( await this . _self . #handshake( chainId , false ) ) ) ;
1231
1276
this . _self . #scheduleWrite( ) ;
1232
1277
await Promise . all ( promises ) ;
1233
1278
this . _self . #selectedDB = selectedDB ;
0 commit comments