Skip to content

Commit a8b81bd

Browse files
authored
Send client user-agent during connection, via CLIENT SETINFO (#2645)
* Add SETINFO support to client connection, with the ability to disable sending the user agent if the end user desires. * Also enables modifying the user-agent with a tag to enable distinguishing different usages.
1 parent c64ce74 commit a8b81bd

File tree

6 files changed

+90
-5
lines changed

6 files changed

+90
-5
lines changed

packages/client/lib/client/index.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { once } from 'events';
1010
import { ClientKillFilters } from '../commands/CLIENT_KILL';
1111
import { promisify } from 'util';
1212

13+
import {version} from '../../package.json';
14+
1315
export const SQUARE_SCRIPT = defineScript({
1416
SCRIPT: 'return ARGV[1] * ARGV[1];',
1517
NUMBER_OF_KEYS: 0,
@@ -118,6 +120,44 @@ describe('Client', () => {
118120
...GLOBAL.SERVERS.PASSWORD,
119121
disableClientSetup: true
120122
});
123+
124+
testUtils.testWithClient('should set default lib name and version', async client => {
125+
const clientInfo = await client.clientInfo();
126+
127+
assert.equal(clientInfo.libName, 'node-redis');
128+
assert.equal(clientInfo.libVer, version);
129+
}, {
130+
...GLOBAL.SERVERS.PASSWORD,
131+
minimumDockerVersion: [7, 2]
132+
});
133+
134+
testUtils.testWithClient('disable sending lib name and version', async client => {
135+
const clientInfo = await client.clientInfo();
136+
137+
assert.equal(clientInfo.libName, '');
138+
assert.equal(clientInfo.libVer, '');
139+
}, {
140+
...GLOBAL.SERVERS.PASSWORD,
141+
clientOptions: {
142+
...GLOBAL.SERVERS.PASSWORD.clientOptions,
143+
disableClientInfo: true
144+
},
145+
minimumDockerVersion: [7, 2]
146+
});
147+
148+
testUtils.testWithClient('send client name tag', async client => {
149+
const clientInfo = await client.clientInfo();
150+
151+
assert.equal(clientInfo.libName, 'node-redis(test)');
152+
assert.equal(clientInfo.libVer, version);
153+
}, {
154+
...GLOBAL.SERVERS.PASSWORD,
155+
clientOptions: {
156+
...GLOBAL.SERVERS.PASSWORD.clientOptions,
157+
clientInfoTag: "test"
158+
},
159+
minimumDockerVersion: [7, 2]
160+
});
121161
});
122162

123163
describe('authentication', () => {

packages/client/lib/client/index.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import { ScanCommandOptions } from '../commands/SCAN';
1111
import { HScanTuple } from '../commands/HSCAN';
1212
import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander';
1313
import { Pool, Options as PoolOptions, createPool } from 'generic-pool';
14-
import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors';
14+
import { ClientClosedError, ClientOfflineError, DisconnectsClientError, ErrorReply } from '../errors';
1515
import { URL } from 'url';
1616
import { TcpSocketConnectOpts } from 'net';
1717
import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
1818

19+
import {version} from '../../package.json';
20+
1921
export interface RedisClientOptions<
2022
M extends RedisModules = RedisModules,
2123
F extends RedisFunctions = RedisFunctions,
@@ -66,6 +68,14 @@ export interface RedisClientOptions<
6668
* Useful with Redis deployments that do not use TCP Keep-Alive.
6769
*/
6870
pingInterval?: number;
71+
/**
72+
* If set to true, disables sending client identifier (user-agent like message) to the redis server
73+
*/
74+
disableClientInfo?: boolean;
75+
/**
76+
* Tag to append to library name that is sent to the Redis server
77+
*/
78+
clientInfoTag?: string;
6979
}
7080

7181
type WithCommands = {
@@ -274,6 +284,33 @@ export default class RedisClient<
274284
);
275285
}
276286

287+
if (!this.#options?.disableClientInfo) {
288+
promises.push(
289+
this.#queue.addCommand(
290+
[ 'CLIENT', 'SETINFO', 'LIB-VER', version],
291+
{ asap: true }
292+
).catch(err => {
293+
if (!(err instanceof ErrorReply)) {
294+
throw err;
295+
}
296+
})
297+
);
298+
299+
promises.push(
300+
this.#queue.addCommand(
301+
[
302+
'CLIENT', 'SETINFO', 'LIB-NAME',
303+
this.#options?.clientInfoTag ? `node-redis(${this.#options.clientInfoTag})` : 'node-redis'
304+
],
305+
{ asap: true }
306+
).catch(err => {
307+
if (!(err instanceof ErrorReply)) {
308+
throw err;
309+
}
310+
})
311+
);
312+
}
313+
277314
if (this.#options?.name) {
278315
promises.push(
279316
this.#queue.addCommand(

packages/client/lib/commands/CLIENT_INFO.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ export interface ClientInfoReply {
3131
user?: string; // 6.0
3232
redir?: number; // 6.2
3333
resp?: number; // 7.0
34+
// 7.2
35+
libName?: string;
36+
libVer?: string;
3437
}
3538

3639
const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g;
@@ -62,7 +65,9 @@ export function transformReply(rawReply: string): ClientInfoReply {
6265
totMem: Number(map['tot-mem']),
6366
events: map.events,
6467
cmd: map.cmd,
65-
user: map.user
68+
user: map.user,
69+
libName: map['lib-name'],
70+
libVer: map['lib-ver'],
6671
};
6772

6873
if (map.laddr !== undefined) {

packages/client/lib/test-utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { promiseTimeout } from './utils';
44

55
const utils = new TestUtils({
66
dockerImageName: 'redis',
7-
dockerImageVersionArgument: 'redis-version'
7+
dockerImageVersionArgument: 'redis-version',
8+
defaultDockerVersion: '7.2'
89
});
910

1011
export default utils;

packages/client/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
},
66
"include": [
77
"./index.ts",
8-
"./lib/**/*.ts"
8+
"./lib/**/*.ts",
9+
"./package.json"
910
],
1011
"exclude": [
1112
"./lib/test-utils.ts",

tsconfig.base.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"declaration": true,
55
"allowJs": true,
66
"useDefineForClassFields": true,
7-
"esModuleInterop": false
7+
"esModuleInterop": false,
8+
"resolveJsonModule": true
89
},
910
"ts-node": {
1011
"files": true

0 commit comments

Comments
 (0)