Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 118 additions & 100 deletions lib/Redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,115 +189,122 @@

const { options } = this;

this.condition = {
select: options.db,
auth: options.username
? [options.username, options.password]
: options.password,
subscriber: false,
};

const _this = this;
asCallback(
this.connector.connect(function (type, err) {
_this.silentEmit(type, err);
}) as Promise<NetStream>,
function (err: Error | null, stream?: NetStream) {
if (err) {
_this.flushQueue(err);
_this.silentEmit("error", err);
reject(err);
_this.setStatus("end");
return;
}
let CONNECT_EVENT = options.tls ? "secureConnect" : "connect";
if (
"sentinels" in options &&
options.sentinels &&
!options.enableTLSForSentinelMode
) {
CONNECT_EVENT = "connect";
}
this.resolvePassword().then((resolvedPassword) => {
this.condition = {
select: options.db,
auth: options.username
? [options.username, resolvedPassword]
: resolvedPassword,
subscriber: false,
};

const _this = this;
asCallback(
this.connector.connect(function (type, err) {
_this.silentEmit(type, err);
}) as Promise<NetStream>,
function (err: Error | null, stream?: NetStream) {
if (err) {
_this.flushQueue(err);
_this.silentEmit("error", err);
reject(err);
_this.setStatus("end");
return;
}
let CONNECT_EVENT = options.tls ? "secureConnect" : "connect";
if (
"sentinels" in options &&
options.sentinels &&
!options.enableTLSForSentinelMode
) {
CONNECT_EVENT = "connect";
}

_this.stream = stream;
_this.stream = stream;

if (options.noDelay) {
stream.setNoDelay(true);
}
if (options.noDelay) {
stream.setNoDelay(true);
}

// Node ignores setKeepAlive before connect, therefore we wait for the event:
// https://github.com/nodejs/node/issues/31663
if (typeof options.keepAlive === "number") {
if (stream.connecting) {
stream.once(CONNECT_EVENT, () => {
// Node ignores setKeepAlive before connect, therefore we wait for the event:
// https://github.com/nodejs/node/issues/31663
if (typeof options.keepAlive === "number") {
if (stream.connecting) {
stream.once(CONNECT_EVENT, () => {
stream.setKeepAlive(true, options.keepAlive);
});
} else {
stream.setKeepAlive(true, options.keepAlive);
});
} else {
stream.setKeepAlive(true, options.keepAlive);
}
}
}

if (stream.connecting) {
stream.once(CONNECT_EVENT, eventHandler.connectHandler(_this));

if (options.connectTimeout) {
/*
* Typically, Socket#setTimeout(0) will clear the timer
* set before. However, in some platforms (Electron 3.x~4.x),
* the timer will not be cleared. So we introduce a variable here.
*
* See https://github.com/electron/electron/issues/14915
*/
let connectTimeoutCleared = false;
stream.setTimeout(options.connectTimeout, function () {
if (connectTimeoutCleared) {
return;
}
stream.setTimeout(0);
stream.destroy();

const err = new Error("connect ETIMEDOUT");
// @ts-expect-error
err.errorno = "ETIMEDOUT";
// @ts-expect-error
err.code = "ETIMEDOUT";
// @ts-expect-error
err.syscall = "connect";
eventHandler.errorHandler(_this)(err);
});
stream.once(CONNECT_EVENT, function () {
connectTimeoutCleared = true;
stream.setTimeout(0);
});
if (stream.connecting) {
stream.once(CONNECT_EVENT, eventHandler.connectHandler(_this));

if (options.connectTimeout) {
/*
* Typically, Socket#setTimeout(0) will clear the timer
* set before. However, in some platforms (Electron 3.x~4.x),
* the timer will not be cleared. So we introduce a variable here.
*
* See https://github.com/electron/electron/issues/14915
*/
let connectTimeoutCleared = false;
stream.setTimeout(options.connectTimeout, function () {
if (connectTimeoutCleared) {
return;
}
stream.setTimeout(0);
stream.destroy();

const err = new Error("connect ETIMEDOUT");
// @ts-expect-error
err.errorno = "ETIMEDOUT";
// @ts-expect-error
err.code = "ETIMEDOUT";
// @ts-expect-error
err.syscall = "connect";
eventHandler.errorHandler(_this)(err);
});
stream.once(CONNECT_EVENT, function () {
connectTimeoutCleared = true;
stream.setTimeout(0);
});
}
} else if (stream.destroyed) {
const firstError = _this.connector.firstError;
if (firstError) {
process.nextTick(() => {
eventHandler.errorHandler(_this)(firstError);
});
}
process.nextTick(eventHandler.closeHandler(_this));
} else {
process.nextTick(eventHandler.connectHandler(_this));
}
} else if (stream.destroyed) {
const firstError = _this.connector.firstError;
if (firstError) {
process.nextTick(() => {
eventHandler.errorHandler(_this)(firstError);
});
if (!stream.destroyed) {
stream.once("error", eventHandler.errorHandler(_this));
stream.once("close", eventHandler.closeHandler(_this));
}
process.nextTick(eventHandler.closeHandler(_this));
} else {
process.nextTick(eventHandler.connectHandler(_this));
}
if (!stream.destroyed) {
stream.once("error", eventHandler.errorHandler(_this));
stream.once("close", eventHandler.closeHandler(_this));
}

const connectionReadyHandler = function () {
_this.removeListener("close", connectionCloseHandler);
resolve();
};
var connectionCloseHandler = function () {
_this.removeListener("ready", connectionReadyHandler);
reject(new Error(CONNECTION_CLOSED_ERROR_MSG));
};
_this.once("ready", connectionReadyHandler);
_this.once("close", connectionCloseHandler);
}
);
const connectionReadyHandler = function () {
_this.removeListener("close", connectionCloseHandler);
resolve();
};
var connectionCloseHandler = function () {
_this.removeListener("ready", connectionReadyHandler);
reject(new Error(CONNECTION_CLOSED_ERROR_MSG));
};
_this.once("ready", connectionReadyHandler);
_this.once("close", connectionCloseHandler);
}
);
}).catch((err) => {
this.flushQueue(err);
this.silentEmit("error", err);
reject(err);
this.setStatus("end");
});
});

return asCallback(promise, callback);
Expand Down Expand Up @@ -559,35 +566,35 @@
});
}

scanStream(options?: ScanStreamOptions) {

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member scanStream should be declared before all private instance method definitions
return this.createScanStream("scan", { options });
}

scanBufferStream(options?: ScanStreamOptions) {

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member scanBufferStream should be declared before all private instance method definitions
return this.createScanStream("scanBuffer", { options });
}

sscanStream(key: string, options?: ScanStreamOptions) {

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member sscanStream should be declared before all private instance method definitions
return this.createScanStream("sscan", { key, options });
}

sscanBufferStream(key: string, options?: ScanStreamOptions) {

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member sscanBufferStream should be declared before all private instance method definitions
return this.createScanStream("sscanBuffer", { key, options });
}

hscanStream(key: string, options?: ScanStreamOptions) {

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member hscanStream should be declared before all private instance method definitions
return this.createScanStream("hscan", { key, options });
}

hscanBufferStream(key: string, options?: ScanStreamOptions) {

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member hscanBufferStream should be declared before all private instance method definitions
return this.createScanStream("hscanBuffer", { key, options });
}

zscanStream(key: string, options?: ScanStreamOptions) {

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member zscanStream should be declared before all private instance method definitions
return this.createScanStream("zscan", { key, options });
}

zscanBufferStream(key: string, options?: ScanStreamOptions) {

Check warning on line 597 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 597 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 597 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 597 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 597 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member zscanBufferStream should be declared before all private instance method definitions
return this.createScanStream("zscanBuffer", { key, options });
}

Expand All @@ -596,7 +603,7 @@
*
* @ignore
*/
silentEmit(eventName: string, arg?: unknown): boolean {

Check warning on line 606 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 606 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 606 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 606 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 606 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member silentEmit should be declared before all private instance method definitions
let error: unknown;
if (eventName === "error") {
error = arg;
Expand Down Expand Up @@ -631,7 +638,7 @@
/**
* @ignore
*/
recoverFromFatalError(

Check warning on line 641 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (18.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 641 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (20.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 641 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (16.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 641 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (12.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 641 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test / test (14.x)

Member recoverFromFatalError should be declared before all private instance method definitions
_commandError: Error,
err: Error,
options: FlushQueueOptions
Expand Down Expand Up @@ -858,6 +865,17 @@
}
}).catch(noop);
}

private async resolvePassword(): Promise<string | null> {
const { password } = this.options;
if (!password) {
return null;
}
if (typeof password === "function") {
return await password();
}
return password;
}
}

interface Redis extends EventEmitter {
Expand Down
2 changes: 1 addition & 1 deletion lib/cluster/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface RedisOptions {
port: number;
host: string;
username?: string;
password?: string;
password?: string | (() => Promise<string> | string);
[key: string]: any;
}

Expand Down
24 changes: 18 additions & 6 deletions lib/connectors/SentinelConnector/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export interface SentinelConnectionOptions {
role?: "master" | "slave";
tls?: ConnectionOptions;
sentinelUsername?: string;
sentinelPassword?: string;
sentinelPassword?: string | (() => Promise<string> | string);
sentinels?: Array<Partial<SentinelAddress>>;
sentinelRetryStrategy?: (retryAttempts: number) => number | void | null;
sentinelReconnectStrategy?: (retryAttempts: number) => number | void | null;
Expand Down Expand Up @@ -294,15 +294,27 @@ export default class SentinelConnector extends AbstractConnector {
return result;
}

private connectToSentinel(
private async resolveSentinelPassword(): Promise<string | null> {
const { sentinelPassword } = this.options;
if (!sentinelPassword) {
return null;
}
if (typeof sentinelPassword === "function") {
return await sentinelPassword();
}
return sentinelPassword;
}

private async connectToSentinel(
endpoint: Partial<SentinelAddress>,
options?: Partial<RedisOptions>
): RedisClient {
): Promise<RedisClient> {
const resolvedPassword = await this.resolveSentinelPassword();
const redis = new Redis({
port: endpoint.port || 26379,
host: endpoint.host,
username: this.options.sentinelUsername || null,
password: this.options.sentinelPassword || null,
password: resolvedPassword,
family:
endpoint.family ||
// @ts-expect-error
Expand All @@ -324,7 +336,7 @@ export default class SentinelConnector extends AbstractConnector {
private async resolve(
endpoint: Partial<SentinelAddress>
): Promise<TcpNetConnectOpts | null> {
const client = this.connectToSentinel(endpoint);
const client = await this.connectToSentinel(endpoint);

// ignore the errors since resolve* methods will handle them
client.on("error", noop);
Expand Down Expand Up @@ -357,7 +369,7 @@ export default class SentinelConnector extends AbstractConnector {
break;
}

const client = this.connectToSentinel(value, {
const client = await this.connectToSentinel(value, {
lazyConnect: true,
retryStrategy: this.options.sentinelReconnectStrategy,
});
Expand Down
3 changes: 2 additions & 1 deletion lib/redis/RedisOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ export interface CommonRedisOptions extends CommanderOptions {

/**
* If set, client will send AUTH command with the value of this option when connected.
* Can be a string or a function that returns a string or Promise<string>.
*/
password?: string;
password?: string | (() => Promise<string> | string);

/**
* Database index to use.
Expand Down
Loading
Loading