From 6d0cebd665fde91ecb8335b195f2e4abbab329ea Mon Sep 17 00:00:00 2001 From: Igor Loskutov Date: Mon, 16 Sep 2024 14:09:21 -0400 Subject: [PATCH] createClient url+tls invariant violation check --- packages/client/lib/client/index.spec.ts | 49 +++++++++++++++++++++++- packages/client/lib/client/index.ts | 49 ++++++++++++++++-------- 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 7f93efaa1c..493ae1c715 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -58,7 +58,8 @@ describe('Client', () => { { socket: { host: 'localhost', - port: 6379 + port: 6379, + tls: false }, username: 'user', password: 'secret', @@ -103,12 +104,58 @@ describe('Client', () => { { socket: { host: 'localhost', + tls: false } } ); }); }); + describe('parseOptions', () => { + it('should throw error if tls socket option is set to true and the url protocol is "redis:"', () => { + assert.throws( + () => RedisClient.parseOptions({ + url: 'redis://localhost', + socket: { + tls: true + } + }), + TypeError + ); + }); + it('should throw error if tls socket option is set to false and the url protocol is "rediss:"', () => { + assert.throws( + () => RedisClient.parseOptions({ + url: 'rediss://localhost', + socket: { + tls: false + } + }), + TypeError + ); + }); + it('should not throw when tls socket option and url protocol matches"', () => { + assert.equal( + RedisClient.parseOptions({ + url: 'rediss://localhost', + socket: { + tls: true + } + }).socket.tls, + true + ); + assert.equal( + RedisClient.parseOptions({ + url: 'redis://localhost', + socket: { + tls: false + } + }).socket.tls, + false + ); + }); + }); + describe('connect', () => { testUtils.testWithClient('connect should return the client instance', async client => { try { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index d7f33e97b1..61faa29fc8 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -157,21 +157,45 @@ export default class RedisClient< return new (RedisClient.extend(options))(options); } - static parseURL(url: string): RedisClientOptions { + static parseOptions(options: O): O { + if (options?.url) { + const parsed = RedisClient.parseURL(options.url); + if (options.socket) { + if (options.socket.tls !== undefined && options.socket.tls !== parsed.socket.tls) { + throw new TypeError(`tls socket option is set to ${options.socket.tls} which is mismatch with protocol or the URL ${options.url} passed`) + } + parsed.socket = Object.assign(options.socket, parsed.socket); + } + + Object.assign(options, parsed); + } + return options; + } + + static parseURL(url: string): RedisClientOptions & { + socket: Exclude & { + tls: boolean + } + } { // https://www.iana.org/assignments/uri-schemes/prov/redis const { hostname, port, protocol, username, password, pathname } = new URL(url), - parsed: RedisClientOptions = { + parsed: RedisClientOptions & { + socket: Exclude & { + tls: boolean + } + } = { socket: { - host: hostname + host: hostname, + tls: false } }; - if (protocol === 'rediss:') { - (parsed.socket as RedisTlsSocketOptions).tls = true; - } else if (protocol !== 'redis:') { + if (protocol !== 'redis:' && protocol !== 'rediss:') { throw new TypeError('Invalid protocol'); } + parsed.socket.tls = protocol === 'rediss:'; + if (port) { (parsed.socket as TcpSocketConnectOpts).port = Number(port); } @@ -239,19 +263,14 @@ export default class RedisClient< } #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { - if (options?.url) { - const parsed = RedisClient.parseURL(options.url); - if (options.socket) { - parsed.socket = Object.assign(options.socket, parsed.socket); - } - - Object.assign(options, parsed); - } - if (options?.database) { this.#selectedDB = options.database; } + if (options) { + return RedisClient.parseOptions(options); + } + return options; }