You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
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.
This commit is contained in:
@@ -10,6 +10,8 @@ import { once } from 'events';
|
|||||||
import { ClientKillFilters } from '../commands/CLIENT_KILL';
|
import { ClientKillFilters } from '../commands/CLIENT_KILL';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
|
||||||
|
import {version} from '../../package.json';
|
||||||
|
|
||||||
export const SQUARE_SCRIPT = defineScript({
|
export const SQUARE_SCRIPT = defineScript({
|
||||||
SCRIPT: 'return ARGV[1] * ARGV[1];',
|
SCRIPT: 'return ARGV[1] * ARGV[1];',
|
||||||
NUMBER_OF_KEYS: 0,
|
NUMBER_OF_KEYS: 0,
|
||||||
@@ -118,6 +120,44 @@ describe('Client', () => {
|
|||||||
...GLOBAL.SERVERS.PASSWORD,
|
...GLOBAL.SERVERS.PASSWORD,
|
||||||
disableClientSetup: true
|
disableClientSetup: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUtils.testWithClient('should set default lib name and version', async client => {
|
||||||
|
const clientInfo = await client.clientInfo();
|
||||||
|
|
||||||
|
assert.equal(clientInfo.libName, 'node-redis');
|
||||||
|
assert.equal(clientInfo.libVer, version);
|
||||||
|
}, {
|
||||||
|
...GLOBAL.SERVERS.PASSWORD,
|
||||||
|
minimumDockerVersion: [7, 2]
|
||||||
|
});
|
||||||
|
|
||||||
|
testUtils.testWithClient('disable sending lib name and version', async client => {
|
||||||
|
const clientInfo = await client.clientInfo();
|
||||||
|
|
||||||
|
assert.equal(clientInfo.libName, '');
|
||||||
|
assert.equal(clientInfo.libVer, '');
|
||||||
|
}, {
|
||||||
|
...GLOBAL.SERVERS.PASSWORD,
|
||||||
|
clientOptions: {
|
||||||
|
...GLOBAL.SERVERS.PASSWORD.clientOptions,
|
||||||
|
disableClientInfo: true
|
||||||
|
},
|
||||||
|
minimumDockerVersion: [7, 2]
|
||||||
|
});
|
||||||
|
|
||||||
|
testUtils.testWithClient('send client name tag', async client => {
|
||||||
|
const clientInfo = await client.clientInfo();
|
||||||
|
|
||||||
|
assert.equal(clientInfo.libName, 'node-redis(test)');
|
||||||
|
assert.equal(clientInfo.libVer, version);
|
||||||
|
}, {
|
||||||
|
...GLOBAL.SERVERS.PASSWORD,
|
||||||
|
clientOptions: {
|
||||||
|
...GLOBAL.SERVERS.PASSWORD.clientOptions,
|
||||||
|
clientInfoTag: "test"
|
||||||
|
},
|
||||||
|
minimumDockerVersion: [7, 2]
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('authentication', () => {
|
describe('authentication', () => {
|
||||||
|
@@ -11,11 +11,13 @@ import { ScanCommandOptions } from '../commands/SCAN';
|
|||||||
import { HScanTuple } from '../commands/HSCAN';
|
import { HScanTuple } from '../commands/HSCAN';
|
||||||
import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander';
|
import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander';
|
||||||
import { Pool, Options as PoolOptions, createPool } from 'generic-pool';
|
import { Pool, Options as PoolOptions, createPool } from 'generic-pool';
|
||||||
import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors';
|
import { ClientClosedError, ClientOfflineError, DisconnectsClientError, ErrorReply } from '../errors';
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import { TcpSocketConnectOpts } from 'net';
|
import { TcpSocketConnectOpts } from 'net';
|
||||||
import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
|
import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
|
||||||
|
|
||||||
|
import {version} from '../../package.json';
|
||||||
|
|
||||||
export interface RedisClientOptions<
|
export interface RedisClientOptions<
|
||||||
M extends RedisModules = RedisModules,
|
M extends RedisModules = RedisModules,
|
||||||
F extends RedisFunctions = RedisFunctions,
|
F extends RedisFunctions = RedisFunctions,
|
||||||
@@ -66,6 +68,14 @@ export interface RedisClientOptions<
|
|||||||
* Useful with Redis deployments that do not use TCP Keep-Alive.
|
* Useful with Redis deployments that do not use TCP Keep-Alive.
|
||||||
*/
|
*/
|
||||||
pingInterval?: number;
|
pingInterval?: number;
|
||||||
|
/**
|
||||||
|
* If set to true, disables sending client identifier (user-agent like message) to the redis server
|
||||||
|
*/
|
||||||
|
disableClientInfo?: boolean;
|
||||||
|
/**
|
||||||
|
* Tag to append to library name that is sent to the Redis server
|
||||||
|
*/
|
||||||
|
clientInfoTag?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type WithCommands = {
|
type WithCommands = {
|
||||||
@@ -274,6 +284,33 @@ export default class RedisClient<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.#options?.disableClientInfo) {
|
||||||
|
promises.push(
|
||||||
|
this.#queue.addCommand(
|
||||||
|
[ 'CLIENT', 'SETINFO', 'LIB-VER', version],
|
||||||
|
{ asap: true }
|
||||||
|
).catch(err => {
|
||||||
|
if (!(err instanceof ErrorReply)) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
promises.push(
|
||||||
|
this.#queue.addCommand(
|
||||||
|
[
|
||||||
|
'CLIENT', 'SETINFO', 'LIB-NAME',
|
||||||
|
this.#options?.clientInfoTag ? `node-redis(${this.#options.clientInfoTag})` : 'node-redis'
|
||||||
|
],
|
||||||
|
{ asap: true }
|
||||||
|
).catch(err => {
|
||||||
|
if (!(err instanceof ErrorReply)) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.#options?.name) {
|
if (this.#options?.name) {
|
||||||
promises.push(
|
promises.push(
|
||||||
this.#queue.addCommand(
|
this.#queue.addCommand(
|
||||||
|
@@ -31,6 +31,9 @@ export interface ClientInfoReply {
|
|||||||
user?: string; // 6.0
|
user?: string; // 6.0
|
||||||
redir?: number; // 6.2
|
redir?: number; // 6.2
|
||||||
resp?: number; // 7.0
|
resp?: number; // 7.0
|
||||||
|
// 7.2
|
||||||
|
libName?: string;
|
||||||
|
libVer?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g;
|
const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g;
|
||||||
@@ -62,7 +65,9 @@ export function transformReply(rawReply: string): ClientInfoReply {
|
|||||||
totMem: Number(map['tot-mem']),
|
totMem: Number(map['tot-mem']),
|
||||||
events: map.events,
|
events: map.events,
|
||||||
cmd: map.cmd,
|
cmd: map.cmd,
|
||||||
user: map.user
|
user: map.user,
|
||||||
|
libName: map['lib-name'],
|
||||||
|
libVer: map['lib-ver'],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (map.laddr !== undefined) {
|
if (map.laddr !== undefined) {
|
||||||
|
@@ -4,7 +4,8 @@ import { promiseTimeout } from './utils';
|
|||||||
|
|
||||||
const utils = new TestUtils({
|
const utils = new TestUtils({
|
||||||
dockerImageName: 'redis',
|
dockerImageName: 'redis',
|
||||||
dockerImageVersionArgument: 'redis-version'
|
dockerImageVersionArgument: 'redis-version',
|
||||||
|
defaultDockerVersion: '7.2'
|
||||||
});
|
});
|
||||||
|
|
||||||
export default utils;
|
export default utils;
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./index.ts",
|
"./index.ts",
|
||||||
"./lib/**/*.ts"
|
"./lib/**/*.ts",
|
||||||
|
"./package.json"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"./lib/test-utils.ts",
|
"./lib/test-utils.ts",
|
||||||
|
@@ -4,7 +4,8 @@
|
|||||||
"declaration": true,
|
"declaration": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"esModuleInterop": false
|
"esModuleInterop": false,
|
||||||
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"files": true
|
"files": true
|
||||||
|
Reference in New Issue
Block a user