You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-09 00:22:08 +03:00
chore: refactor parts out of the index.js file
This commit is contained in:
@@ -1,23 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const commands = require('redis-commands')
|
||||
const Multi = require('./multi')
|
||||
const RedisClient = require('../').RedisClient
|
||||
const Command = require('./command')
|
||||
|
||||
const clientProto = RedisClient.prototype
|
||||
const multiProto = Multi.prototype
|
||||
|
||||
// TODO: Rewrite this including the individual commands into a Commands class
|
||||
// that provided a functionality to add new commands to the client
|
||||
commands.list.forEach((command) => {
|
||||
function addCommand (clientProto, multiProto, command) {
|
||||
// Some rare Redis commands use special characters in their command name
|
||||
// Convert those to a underscore to prevent using invalid function names
|
||||
const commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1')
|
||||
|
||||
// Do not override existing functions
|
||||
if (!clientProto[command]) {
|
||||
clientProto[command] = function () {
|
||||
clientProto[commandName] = function () {
|
||||
const len = arguments.length
|
||||
const arr = new Array(len)
|
||||
for (var i = 0; i < len; i += 1) {
|
||||
@@ -25,16 +17,16 @@ commands.list.forEach((command) => {
|
||||
}
|
||||
return this.internalSendCommand(new Command(command, arr))
|
||||
}
|
||||
if (clientProto[command] !== commandName) {
|
||||
Object.defineProperty(clientProto[command], 'name', {
|
||||
if (!clientProto[commandName].name) {
|
||||
Object.defineProperty(clientProto[commandName], 'name', {
|
||||
value: commandName
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Do not override existing functions
|
||||
if (!multiProto[command]) {
|
||||
multiProto[command] = function () {
|
||||
if (!multiProto[command] && command !== 'multi') {
|
||||
multiProto[commandName] = function () {
|
||||
const len = arguments.length
|
||||
const arr = new Array(len)
|
||||
for (var i = 0; i < len; i += 1) {
|
||||
@@ -43,10 +35,12 @@ commands.list.forEach((command) => {
|
||||
this._queue.push(new Command(command, arr))
|
||||
return this
|
||||
}
|
||||
if (multiProto[command] !== commandName) {
|
||||
Object.defineProperty(multiProto[command], 'name', {
|
||||
if (!multiProto[commandName].name) {
|
||||
Object.defineProperty(multiProto[commandName], 'name', {
|
||||
value: commandName
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = addCommand
|
||||
|
131
lib/connect.js
Normal file
131
lib/connect.js
Normal file
@@ -0,0 +1,131 @@
|
||||
'use strict'
|
||||
|
||||
const tls = require('tls')
|
||||
const Parser = require('redis-parser')
|
||||
const net = require('net')
|
||||
const debug = require('./debug')
|
||||
|
||||
/**
|
||||
* @description Create a new Parser instance and pass all the necessary options to it
|
||||
*
|
||||
* @param {RedisClient} client
|
||||
* @returns JavascriptRedisParser
|
||||
*/
|
||||
function createParser (client) {
|
||||
return new Parser({
|
||||
returnReply (data) {
|
||||
client.returnReply(data)
|
||||
},
|
||||
returnError (err) {
|
||||
client.returnError(err)
|
||||
},
|
||||
returnFatalError (err) {
|
||||
// Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again
|
||||
// Note: the execution order is important. First flush and emit, then create the stream
|
||||
err.message += '. Please report this.'
|
||||
client.ready = false
|
||||
client.flushAndError({
|
||||
message: 'Fatal error encountered. Command aborted.',
|
||||
code: 'NR_FATAL'
|
||||
}, {
|
||||
error: err,
|
||||
queues: ['commandQueue']
|
||||
})
|
||||
connect(client)
|
||||
setImmediate(() => client.emit('error', err))
|
||||
},
|
||||
returnBuffers: client.buffers || client.messageBuffers,
|
||||
stringNumbers: client.options.stringNumbers || false
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Open a PR for fakeredis to pass a mocked stream with the options
|
||||
/**
|
||||
* @description Connect to the provided client and add all the listeners.
|
||||
*
|
||||
* It will also init a parser and fire the auth command if a password exists.
|
||||
*
|
||||
* @param {RedisClient} client
|
||||
*/
|
||||
function connect (client) {
|
||||
// Init parser
|
||||
const parser = createParser(client)
|
||||
client._replyParser = parser
|
||||
|
||||
if (client.options.stream) {
|
||||
// Only add the listeners once in case of a reconnect try (that won't work)
|
||||
if (client._stream) {
|
||||
return
|
||||
}
|
||||
client._stream = client.options.stream
|
||||
} else {
|
||||
// On a reconnect destroy the former stream and retry
|
||||
if (client._stream) {
|
||||
client._stream.removeAllListeners()
|
||||
client._stream.destroy()
|
||||
}
|
||||
|
||||
/* istanbul ignore if: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */
|
||||
if (client.options.tls) {
|
||||
client._stream = tls.connect(client.connectionOptions)
|
||||
} else {
|
||||
client._stream = net.createConnection(client.connectionOptions)
|
||||
}
|
||||
}
|
||||
|
||||
if (client.options.connectTimeout) {
|
||||
// TODO: Investigate why this is not properly triggered
|
||||
client._stream.setTimeout(client.connectTimeout, () => {
|
||||
// Note: This is only tested if a internet connection is established
|
||||
client.connectionGone('timeout')
|
||||
})
|
||||
}
|
||||
|
||||
/* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */
|
||||
const connectEvent = client.options.tls ? 'secureConnect' : 'connect'
|
||||
client._stream.once(connectEvent, () => {
|
||||
client._stream.removeAllListeners('timeout')
|
||||
client.timesConnected++
|
||||
client.onConnect()
|
||||
})
|
||||
|
||||
client._stream.on('data', (bufferFromSocket) => {
|
||||
debug('Net read %s id %s', client.address, client.connectionId)
|
||||
parser.execute(bufferFromSocket)
|
||||
})
|
||||
|
||||
client._stream.on('error', (err) => {
|
||||
client.onError(err)
|
||||
})
|
||||
|
||||
/* istanbul ignore next: difficult to test and not important as long as we keep this listener */
|
||||
client._stream.on('clientError', (err) => {
|
||||
debug('clientError occurred')
|
||||
client.onError(err)
|
||||
})
|
||||
|
||||
client._stream.once('close', (hadError) => {
|
||||
client.connectionGone('close')
|
||||
})
|
||||
|
||||
client._stream.once('end', () => {
|
||||
client.connectionGone('end')
|
||||
})
|
||||
|
||||
client._stream.setNoDelay()
|
||||
|
||||
// Fire the command before redis is connected to be sure it's the first fired command
|
||||
if (client.authPass !== undefined) {
|
||||
client.ready = true
|
||||
client.auth(client.authPass).catch((err) => {
|
||||
client.closing = true
|
||||
process.nextTick(() => {
|
||||
client.emit('error', err)
|
||||
client.end(true)
|
||||
})
|
||||
})
|
||||
client.ready = false
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect
|
@@ -28,7 +28,7 @@ module.exports = function createClient (portArg, hostArg, options) {
|
||||
options.password = parsed.auth.split(':')[1]
|
||||
}
|
||||
if (parsed.protocol && parsed.protocol !== 'redis:') {
|
||||
console.warn(`nodeRedis: WARNING: You passed "${parsed.protocol.substring(0, parsed.protocol.length - 1) }" as protocol instead of the "redis" protocol!`)
|
||||
console.warn(`nodeRedis: WARNING: You passed "${parsed.protocol.substring(0, parsed.protocol.length - 1)}" as protocol instead of the "redis" protocol!`)
|
||||
}
|
||||
if (parsed.pathname && parsed.pathname !== '/') {
|
||||
options.db = parsed.pathname.substr(1)
|
||||
|
@@ -40,35 +40,34 @@ RedisClient.prototype.sendCommand = function (command, args) {
|
||||
}
|
||||
|
||||
RedisClient.prototype.end = function (flush) {
|
||||
if (typeof flush !== 'boolean') {
|
||||
throw new TypeError('You must call "end" with the flush argument.')
|
||||
}
|
||||
|
||||
// Flush queue if wanted
|
||||
if (flush) {
|
||||
this.flushAndError({
|
||||
message: 'Connection forcefully ended and command aborted.',
|
||||
code: 'NR_CLOSED'
|
||||
})
|
||||
} else if (arguments.length === 0) {
|
||||
this.warn(
|
||||
'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' +
|
||||
'Please check the documentation (https://github.com/NodeRedis/nodeRedis) and explicitly use flush.'
|
||||
)
|
||||
}
|
||||
// Clear retryTimer
|
||||
if (this.retryTimer) {
|
||||
clearTimeout(this.retryTimer)
|
||||
this.retryTimer = null
|
||||
}
|
||||
this.stream.removeAllListeners()
|
||||
this.stream.on('error', noop)
|
||||
this._stream.removeAllListeners()
|
||||
this._stream.on('error', noop)
|
||||
this.connected = false
|
||||
this.ready = false
|
||||
this.closing = true
|
||||
return this.stream.destroySoon()
|
||||
return this._stream.destroySoon()
|
||||
}
|
||||
|
||||
RedisClient.prototype.unref = function () {
|
||||
if (this.connected) {
|
||||
debug('Unref\'ing the socket connection')
|
||||
this.stream.unref()
|
||||
this._stream.unref()
|
||||
} else {
|
||||
debug('Not connected yet, will unref later')
|
||||
this.once('connect', function () {
|
||||
|
@@ -3,6 +3,7 @@
|
||||
const debug = require('./debug')
|
||||
const Multi = require('./multi')
|
||||
const Command = require('./command')
|
||||
const utils = require('./utils')
|
||||
const noPasswordIsSet = /no password is set/
|
||||
const RedisClient = require('../').RedisClient
|
||||
|
||||
@@ -21,11 +22,11 @@ const RedisClient = require('../').RedisClient
|
||||
TODO: Implement hooks to replace this. Most of these things are perfect for hooks
|
||||
********************************************************************************************/
|
||||
|
||||
function selectCallback (self, db) {
|
||||
function selectCallback (client, db) {
|
||||
return function (err, res) {
|
||||
if (err === null) {
|
||||
// Store db in this.selectDb to restore it on reconnect
|
||||
self.selectedDb = db
|
||||
client.selectedDb = db
|
||||
}
|
||||
return err || res
|
||||
}
|
||||
@@ -66,11 +67,11 @@ Multi.prototype.monitor = function monitor () {
|
||||
return this
|
||||
}
|
||||
|
||||
function quitCallback (self) {
|
||||
function quitCallback (client) {
|
||||
return function (err, res) {
|
||||
if (self.stream.writable) {
|
||||
if (client._stream.writable) {
|
||||
// If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code
|
||||
self.stream.destroy()
|
||||
client._stream.destroy()
|
||||
}
|
||||
if (err && err.code === 'NR_CLOSED') {
|
||||
// Pretend the quit command worked properly in this case.
|
||||
@@ -106,10 +107,10 @@ Multi.prototype.quit = function quit () {
|
||||
return this
|
||||
}
|
||||
|
||||
function infoCallback (self) {
|
||||
function infoCallback (client) {
|
||||
return function (err, res) {
|
||||
if (err) {
|
||||
self.serverInfo = {}
|
||||
client.serverInfo = {}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ function infoCallback (self) {
|
||||
})
|
||||
}
|
||||
// Expose info key/values to users
|
||||
self.serverInfo = obj
|
||||
client.serverInfo = obj
|
||||
return res
|
||||
}
|
||||
}
|
||||
@@ -156,11 +157,11 @@ Multi.prototype.info = function info (section) {
|
||||
return this
|
||||
}
|
||||
|
||||
function authCallback (self, pass) {
|
||||
function authCallback (client, pass) {
|
||||
return function (err, res) {
|
||||
if (err) {
|
||||
if (noPasswordIsSet.test(err.message)) {
|
||||
self.warn('Warning: Redis server does not require a password, but a password was supplied.')
|
||||
utils.warn(client, 'Warning: Redis server does not require a password, but a password was supplied.')
|
||||
return 'OK' // TODO: Fix this
|
||||
}
|
||||
return err
|
||||
|
17
lib/utils.js
17
lib/utils.js
@@ -115,11 +115,26 @@ function replyInOrder (client, callback, err, res, queue) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Emit or print a warning. E.g. deprecations
|
||||
*
|
||||
* @param {RedisClient} client
|
||||
* @param {string} msg
|
||||
*/
|
||||
function warn (client, msg) {
|
||||
if (client.listeners('warning').length !== 0) {
|
||||
client.emit('warning', msg)
|
||||
} else {
|
||||
console.warn('NodeRedis:', msg)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
replyToStrings,
|
||||
replyToObject,
|
||||
errCode: /^([A-Z]+)\s+(.+)$/,
|
||||
monitorRegex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+]( ".+?")+$/,
|
||||
clone: convenienceClone,
|
||||
replyInOrder
|
||||
replyInOrder,
|
||||
warn
|
||||
}
|
||||
|
Reference in New Issue
Block a user