UNPKG

herald-server

Version:

Server for sending messages by publish-subscribe, and whisper.

245 lines (218 loc) 9.64 kB
/* * The MIT License (MIT) * * Copyright (c) 2015 (NumminorihSF) Konstantine Petryaev * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ 'use strict'; /** * Module to parse and make messages. * Модуль, для парсинга и создания сообщений. * @class HeraldServer.MessageParser * @alternateClassName MessageParser * @member HeraldServer */ /** * Constructor. * Конструктор. * @method constructor * @param {RPCWorker} rpcWorker Object to do rpc. * Объект для испольнения rpc. * @param {CryptMaker|Object} [messageMaker] Object with make and parse logic. * Объект, реализующий логику создания сообщений и их парсинга. * @param {String} [alg='no'] Algorithm to encrypt messages. * Алгоритм шифрования сообщений. * @param {String} [key] Key to encrypt messages. * Ключ шифрования. * @returns {MessageParser} MessageParser object. * Объект MessageParser. */ function MessageParser (rpcWorker, messageMaker, alg, key){ this.rpcWorker = rpcWorker; this.cm = messageMaker || new (require('crypt-maker'))({algorithm: alg || 'no', key: key}); return this; } /** * Wrapper. Обертка. * @param {String} message * @returns {Object|null} * @private */ MessageParser.prototype._getHeader = function(message){ return this.cm.getHeader(message); }; /** * Wrapper. Обертка. * @param {String} message * @returns {Object|null} * @private */ MessageParser.prototype._getBody = function(message){ return this.cm.getBody(message); }; /** * Wrapper * @param {Object} header * @param {String} message * @returns {String} * @private */ MessageParser.prototype._replaceHeader = function(header, message){ return this.cm.replaceHeader(header, message); }; /** * Get info about message. Возвращает информацию о сообщении. * @param {String} message Raw (encrypted) message. Сырое (зашифрованное) сообщение. * @returns {Object} rule * @returns {Object} rule.uidType How find socket * @returns {Object} rule.mask There are fields: uidType (how search socket), mesType (type of message), * mask (mask for Router to find sockets), regExp (true if mask is regexp), * header (header of message). Содержит поля: uidType (правило поиска сокета), mesType (тип сообщения), * mask (маска поиска сокета), regExp (true если маска это регулярное выражение), * header (заголовок сообщения). */ MessageParser.prototype.getRule = function(message){ var header = this._getHeader(message); if (header.rpc) { if (header.rpc === 'herald-server') return {uidType: 'self', mesType: 'rpc', header: header, body: this._getBody(message)}; return {uidType: 'name', mesType:'rpc', mask: header.rpc, regExp: null, header: header, raw: message}; } if (header.whisp) return {uidType: 'name', mesType:'event', mask: header.whisp, regExp: null, header: header, raw: message}; if (header.whispUid) return {uidType: 'uid', mesType:'event', mask: header.whispUid, regExp: null, header: header, raw: message}; if (header.event) return {uidType: 'event', mesType:'event', mask: header.event, regExp: null, header: header, raw: message}; if (header.rpcRes) return {uidType: 'uid', mesType:'rpcRes', mask: header.rpcRes, regExp: null, header: header, raw: message}; if (header.rpcUid) return {uidType: 'uid', mesType:'rpc', mask: header.rpcUid, regExp: null, header: header, raw: message}; if (header.rpcRegExp) return {uidType: 'name', mesType:'rpc', mask: header.rpcRegExp, regExp: true, header: header, raw: message}; if (header.rpcBroad) return {uidType: 'name', mesType:'rpc', mask: '.*', regExp: true, header: header, raw: message}; return {uidType:'fail', header: header}; }; /** * Prepare message to send. Fix header, replace it on raw message. * Готовит сообщение. Правит заголовок и подставляет его в сырое сообщение. * @param {Object} messageRule Object, that {@link MessageParser#getRule} method returns. * Объект, возвращаемый методом {@link MessageParser#getRule}. * @returns {Object|null} */ MessageParser.prototype.prepareMessage = function(messageRule){ if (!messageRule) return null; if (messageRule.uidType === 'event') {} else if (messageRule.mesType === 'event') { if (messageRule.header.whisp) messageRule.header.event = 'whisp'; else if (messageRule.header.whispUid) messageRule.header.event = 'whisp'; } else if (messageRule.mesType === 'rpcRes') {} else if (messageRule.mesType === 'rpc'){ if (messageRule.header.rpc) {} else if (messageRule.header.rpcUid) messageRule.header.rpc = messageRule.header.rpcUid; else if (messageRule.header.rpcRegExp) messageRule.header.rpc = messageRule.header.rpcRegExp; else if (messageRule.header.rpcBroad) messageRule.header.rpc = messageRule.header.rpcBroad; } messageRule.raw = this._replaceHeader(messageRule.header, messageRule.raw); return messageRule; }; /** * Listen socket and parse messages. Слушает сокет и парсит сообщение. * @param {net.Socket} socket * @param {Object} socket.header Authorize message header. Заголовок сообщения авторизации. * @param {Function} callback Function spawned for every message. Функция вызывается на каждое сообщение. * @returns {*} */ MessageParser.prototype.listen = function(socket, callback){ if (!socket) return; var mes = this.prepareMessage({ uidType: 'uid', mesType: 'rpcRes', mask: socket.header.uid, regExp: false, header: {rpcRes: socket.header.uid, messId: socket.header.messId, actionId: socket.header.actionId, action: socket.header.action}, body: {error: null, result: 'OK'}, raw: this.cm.makeMessage({ header: {rpcRes: socket.header.uid, actionId: socket.header.actionId, action: socket.header.action}, body: {error: null, result: 'OK'} }) }); callback(mes); var self = this; var tail = ''; function onData (chunk){ tail += chunk.replace(/\r\n\r\n[\r\n]*/g, '\r\n\r\n'); var array = self.cm.splitMessagesForce(tail); tail = array.pop(); if (tail > 1048576) tail = ''; array.map(function(raw){ return self.getRule(raw); }).filter(function(m){ if (m.uidType === 'fail') return false; if (m.uidType === 'self') { m.body = m.body || {}; m.body.uid = m.header.uid; self.rpcWorker.do(m.header.action, m.body, function(err, data){ socket.write(self.cm.makeMessage({ header: {rpcRes: m.header.uid, actionId: m.header.actionId, action: m.header.action, name: m.header.name, uid: m.header.uid, messId: m.header.messId }, body: {error: (err instanceof Error) ? err.message : err, result: data} }), 'utf8', function(err){ if (err) self.logger(err); }); }); return false; } return true; }).map(function(m){ var rule = self.prepareMessage(m); if (rule) callback(rule); }); } socket.on('data', onData); socket.once('close', function(){ socket.removeListener('data', onData); }); return true; }; /** * Parse message. Парсит сообщение. * @param {String} message * @returns {Object|null} */ MessageParser.prototype.parseMessage = function(message){ return this.cm.parseMessage(message); }; /** * Another constructor. Еще один конструктор * @param {RPCWorker} rpcWorker Object to do rpc. * Объект для испольнения rpc. * @param {CryptMaker|Object} [messageMaker] Object with make and parse logic. * Объект, реализующий логику создания сообщений и их парсинга. * @param {String} [alg='no'] Algorithm to encrypt messages. * Алгоритм шифрования сообщений. * @param {String} [key] Key to encrypt messages. * Ключ шифрования. * @static * @returns {MessageParser} */ MessageParser.createParser = function(rpcWorker, messageMaker, alg, key){ return new MessageParser(rpcWorker, messageMaker, alg, key); }; module.exports = MessageParser;