UNPKG

respress

Version:

A RESP 'Redis Serialization Protocol' library implementation to generate a server, uses a similar approach to express to define you serer, making it easy and fast.

141 lines (123 loc) 4.23 kB
let { Buffer } = require('buffer'); /** * Encodes a message to the RESP protocol * @param {String | Array | Object | Number | Function} request * @param {Boolean} encodeArray * @returns RESP encoded value */ function encode(request, encodeArray = true) { if (request === null) { return '$-1\r\n' } if (Array.isArray(request) && encodeArray) { const content = [`*${request.length}\r\n`] for (let i = 0, length = request.length; i < length; i++) { content.push(encode(request[i], false)) } return content.join('') } if (request instanceof Error) { return `-ERROR ${request.message}\r\n` } let value if (typeof request === 'object') { value = JSON.stringify(request); return encode(value); } else if (typeof request === 'function') { value = {}.toString.call(request) } else { value = String(request) } return `$${value.length}\r\n${value}\r\n` } /** * Decodes the message of from RESP protocol to its proper type * @param {*} content The content to be decoded * @param {*} startIndex Where to start the decoding * @returns {String | Array | Object | Number | Function} */ function decodeProgressive(content, startIndex) { let currentIndex = startIndex const type = content.toString('utf8', currentIndex, currentIndex + 1) // +1 because type takes 1 character currentIndex++ if (type === '*') { // Array const lengthEnd = content.indexOf('\r\n', currentIndex) const length = parseInt(content.toString('utf8', currentIndex, lengthEnd), 10) // +2 because of \r\n after length ends currentIndex = lengthEnd + 2 const value = [] for (let i = 0; i < length; i++) { const entry = decodeProgressive(content, currentIndex) currentIndex = entry.index value.push(entry.value) } return { index: currentIndex, value } } if (type === '$') { // String or Null const lengthEnd = content.indexOf('\r\n', currentIndex) const length = parseInt(content.toString('utf8', currentIndex, lengthEnd), 10) // +2 because of \r\n after length ends currentIndex = lengthEnd + 2 let value if (length === -1) { // Null value = null } else { // String value = content.toString('utf8', currentIndex, currentIndex + length) // +2 because of \r\n at the end of string currentIndex += length + 2 } return { index: currentIndex, value } } if (type === '+') { // Simple string const valueEnd = content.indexOf('\r\n', currentIndex) const value = content.toString('utf8', currentIndex, valueEnd) // +2 because of \r\n at the end of simple string currentIndex = valueEnd + 2 return { index: currentIndex, value } } if (type === ':') { // Integer const valueEnd = content.indexOf('\r\n', currentIndex) const value = parseInt(content.toString('utf8', currentIndex, valueEnd), 10) // +2 because of \r\n at the end of simple string currentIndex = valueEnd + 2 return { index: currentIndex, value } } if (type === '-') { // Error const valueEnd = content.indexOf('\r\n', currentIndex) const value = content.toString('utf8', currentIndex, valueEnd) throw new Error(value) } throw new Error('Malformed Input') } /** * The decoding function to decode RESP * @param {String} givenContent * @returns {String | Array | Object | Number | Function} */ function decode(givenContent) { let index = 0 const value = [] const content = Buffer.from(givenContent) for (; ;) { const entry = decodeProgressive(content, index) index = entry.index value.push(entry.value) if (index === content.length) { // We have read it all! break } } return value[0] } module.exports.encode = encode; module.exports.decodeProgressive = decodeProgressive; module.exports.decode = decode;