Skip to content

Commit d958c3d

Browse files
mustard-mhleibale
andauthored
Auth before select database (#1679)
* Auth before select database * fix #1681 Co-authored-by: leibale <[email protected]>
1 parent 83ddbbf commit d958c3d

File tree

4 files changed

+62
-10
lines changed

4 files changed

+62
-10
lines changed

lib/client/index.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ describe('Client', () => {
119119

120120
assert.equal(client.isOpen, false);
121121
});
122+
123+
itWithClient(TestRedisServers.PASSWORD, 'should execute AUTH before SELECT', async client => {
124+
assert.equal(
125+
(await client.clientInfo()).db,
126+
2
127+
);
128+
}, {
129+
minimumRedisVersion: [6, 2],
130+
clientOptions: {
131+
database: 2
132+
}
133+
});
122134
});
123135

124136
describe('legacyMode', () => {

lib/client/index.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,24 +177,44 @@ export default class RedisClient<M extends RedisModules, S extends RedisScripts>
177177

178178
#initiateSocket(): RedisSocket {
179179
const socketInitiator = async (): Promise<void> => {
180-
const v4Commands = this.#options?.legacyMode ? this.#v4 : this,
181-
promises = [];
180+
const promises = [];
182181

183182
if (this.#selectedDB !== 0) {
184-
promises.push(v4Commands.select(RedisClient.commandOptions({ asap: true }), this.#selectedDB));
183+
promises.push(
184+
this.#queue.addCommand(
185+
['SELECT', this.#selectedDB.toString()],
186+
{ asap: true }
187+
)
188+
);
185189
}
186190

187191
if (this.#options?.readonly) {
188-
promises.push(v4Commands.readonly(RedisClient.commandOptions({ asap: true })));
192+
promises.push(
193+
this.#queue.addCommand(
194+
COMMANDS.READONLY.transformArguments(),
195+
{ asap: true }
196+
)
197+
);
189198
}
190199

191200
if (this.#options?.username || this.#options?.password) {
192-
promises.push(v4Commands.auth(RedisClient.commandOptions({ asap: true }), this.#options));
201+
promises.push(
202+
this.#queue.addCommand(
203+
COMMANDS.AUTH.transformArguments({
204+
username: this.#options.username,
205+
password: this.#options.password ?? ''
206+
}),
207+
{ asap: true }
208+
)
209+
);
193210
}
194211

195212
const resubscribePromise = this.#queue.resubscribe();
196213
if (resubscribePromise) {
197214
promises.push(resubscribePromise);
215+
}
216+
217+
if (promises.length) {
198218
this.#tick();
199219
}
200220

@@ -410,7 +430,7 @@ export default class RedisClient<M extends RedisModules, S extends RedisScripts>
410430
quit = this.QUIT;
411431

412432
#tick(): void {
413-
if (!this.#socket.isSocketExists) {
433+
if (!this.#socket.isSocketExists || this.#socket.writableNeedDrain) {
414434
return;
415435
}
416436

lib/client/socket.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ export default class RedisSocket extends EventEmitter {
7676
return !!this.#socket;
7777
}
7878

79+
// `writable.writableNeedDrain` was added in v15.2.0 and therefore can't be used
80+
// https://nodejs.org/api/stream.html#stream_writable_writableneeddrain
81+
#writableNeedDrain = false;
82+
83+
get writableNeedDrain(): boolean {
84+
return this.#writableNeedDrain;
85+
}
86+
7987
constructor(initiator?: RedisSocketInitiator, options?: RedisSocketOptions) {
8088
super();
8189

@@ -163,7 +171,10 @@ export default class RedisSocket extends EventEmitter {
163171
this.#onSocketError(new Error('Socket closed unexpectedly'));
164172
}
165173
})
166-
.on('drain', () => this.emit('drain'))
174+
.on('drain', () => {
175+
this.#writableNeedDrain = false;
176+
this.emit('drain');
177+
})
167178
.on('data', (data: Buffer) => this.emit('data', data));
168179

169180
resolve(socket);
@@ -198,7 +209,9 @@ export default class RedisSocket extends EventEmitter {
198209
throw new ClientClosedError();
199210
}
200211

201-
return this.#socket.write(toWrite);
212+
const wasFullyWritten = this.#socket.write(toWrite);
213+
this.#writableNeedDrain = !wasFullyWritten;
214+
return wasFullyWritten;
202215
}
203216

204217
async disconnect(ignoreIsOpen = false): Promise<void> {

lib/test-utils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,16 +284,23 @@ export function describeHandleMinimumRedisVersion(minimumVersion: PartialRedisVe
284284
});
285285
}
286286

287+
interface RedisClientTestOptions extends RedisTestOptions {
288+
clientOptions?: RedisClientOptions<{}, {}>;
289+
}
290+
287291
export function itWithClient(
288292
type: TestRedisServers,
289293
title: string,
290294
fn: (client: RedisClientType) => Promise<void>,
291-
options?: RedisTestOptions
295+
options?: RedisClientTestOptions
292296
): void {
293297
it(title, async function () {
294298
if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return;
295299

296-
const client = RedisClient.create(TEST_REDIS_SERVERS[type]);
300+
const client = RedisClient.create({
301+
...TEST_REDIS_SERVERS[type],
302+
...options?.clientOptions
303+
});
297304

298305
await client.connect();
299306

0 commit comments

Comments
 (0)