UNPKG

nxkit

Version:

This is a collection of tools, independent of any other libraries

301 lines (300 loc) 11 kB
"use strict"; /* ***** BEGIN LICENSE BLOCK ***** * Distributed under the BSD license: * * Copyright (c) 2015, xuewen.chu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of xuewen.chu nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL xuewen.chu BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ***** END LICENSE BLOCK ***** */ Object.defineProperty(exports, "__esModule", { value: true }); const util_1 = require("../util"); const path_1 = require("../path"); const fmtc_1 = require("./fmtc"); const service_1 = require("../service"); const wss = require("../ws/service"); const cli = require("../ws/cli"); const path_2 = require("../path"); const errno_1 = require("../errno"); class FNode { constructor(center) { this.m_initTime = 0; this.m_center = center; } get initTime() { return this.m_initTime; } get center() { return this.m_center; } initialize(initTime = 0) { this.m_initTime = initTime; return this.m_center.addNode(this); } destroy() { return this.m_center.deleteNode(this); } } exports.FNode = FNode; /** * @class FMTNodeLocal */ class FNodeLocal extends FNode { get id() { return this.m_center.id; } get publishURL() { return this.m_center.publishURL; } async publish(event, data) { this.m_center.host.getNoticer(event).trigger(data); } async broadcast(event, data, id) { this.m_center.host.getNoticer(event).trigger(data); } triggerTo(id, event, data, sender) { return this.m_center.getFMTService(id).trigger(event, data, sender); // trigger event } callTo(id, method, data, timeout, sender) { return this.m_center.getFMTService(id).call(method, data, timeout, sender); // call method } sendTo(id, method, data, sender) { return this.m_center.getFMTService(id).send(method, data, sender); // call method } async user(id) { return this.m_center.getFMTService(id).user; } async query(id, more = false) { var s = this.m_center.getFMTServiceNoError(id); if (more) { return s ? { time: s.time, uuid: s.uuid } : null; } else { return s ? true : false; } } } exports.FNodeLocal = FNodeLocal; /** * @class FNodeRemote */ class FNodeRemote extends FNode { constructor(center, impl, id) { super(center); this.m_isInit = false; this.m_impl = impl; this.m_node_id = id; } async initialize(initTime = 0) { util_1.default.assert(!this.m_isInit); try { this.m_impl.conv.onClose.on(async (e) => { if (this.m_isInit) { await this.m_center.deleteNode(this); var url = this.publishURL; if (url) { // recontect console.log('recontect', url.href); await util_1.default.sleep(1e2 + util_1.default.random(1e2)); // 100+ms if (!this.m_center.getFnodeFrom(url.href)) { console.log('recontect, start', url.href); this.m_center.connect(url.href).catch(console.error); } } } }); console.log('FNodeRemote.initialize()', this.m_node_id); this.m_initTime = initTime ? initTime : Date.now(); await this.m_center.addNode(this); console.log('FNodeRemote.initialize(), ok', this.m_node_id); this.m_isInit = true; } catch (err) { this.destroy(); throw err; } } async destroy() { this.m_impl.conv.close(); await this.m_center.deleteNode(this); } // ---------------- IMPL ---------------- get id() { return this.m_node_id; } get publishURL() { return this.m_impl.thatFnode; } publish(event, data) { return this.m_impl.send('publish', [event, data]); } broadcast(event, data, id) { return this.m_impl.send('broadcast', [event, data, id]); } triggerTo(id, event, data, sender) { return this.m_impl.call('triggerTo', [id, event, data, sender]); // trigger event } callTo(id, method, data, timeout, sender) { return this.m_impl.call('callTo', [id, method, data, timeout, sender], timeout); // call method } sendTo(id, method, data, sender) { return this.m_impl.call('sendTo', [id, method, data, sender]); // call method } user(id) { return this.m_impl.call('user', [id]); // call method } query(id, more = false) { return this.m_impl.call('query', [id, more ? true : false]); } } exports.FNodeRemote = FNodeRemote; /** * @class FNodeRemoteIMPL */ class FNodeRemoteIMPL { publish([event, data]) { this.center.host.getNoticer(event).trigger(data); } broadcast([event, data, id]) { this.center._forwardBroadcast(event, data, id, this.fnode); } triggerTo([id, event, data, sender]) { return this.center.getFMTService(id).trigger(event, data, sender); } callTo([id, method, data, timeout, sender]) { return this.center.getFMTService(id).call(method, data, timeout, sender); } sendTo([id, method, data, sender]) { return this.center.getFMTService(id).send(method, data, sender); } user([id]) { return this.center.getFMTService(id).user; } query([id, more]) { var s = this.center.getFMTServiceNoError(id); if (more) { return s ? { time: s.time, uuid: s.uuid } : null; } else { return s ? true : false; } } } exports.FNodeRemoteIMPL = FNodeRemoteIMPL; /** * @class FNodeRemoteService */ class FNodeRemoteService extends wss.WSService { constructor() { super(...arguments); this.m_center = null; this.m_that_fnode = null; this.m_fnode = null; } get thatFnode() { return this.m_that_fnode; } get center() { return this.m_center; } get fnode() { return this.m_fnode; } async requestAuth() { var center = fmtc_1.default._fmtc(this.conv.server); util_1.default.assert(center, 'FNodeRemoteService.requestAuth() fmt center No found'); util_1.default.assert(this.params.id, 'FNodeRemoteService.loaded() node id param undefined'); util_1.default.assert(this.params.id != center.id, 'Cannot connect to itself'); if (!await center.delegate.authFnode(this)) return false; this.m_center = center; return true; } async load() { try { var { id, publish } = this.params; this.m_that_fnode = publish ? new path_1.URL(decodeURIComponent(publish)) : null; this.m_fnode = new FNodeRemote(this.center, this, id); await this.m_fnode.initialize(); await util_1.default.sleep(200); // 在同一个node进程中同时开启多个节点时socket无法写入 this.trigger('InitComplete', { id: this.center.id, time: this.m_fnode.initTime }); console.log('FNodeRemoteService.load', id, this.m_that_fnode && this.m_that_fnode.href); } catch (err) { console.error('FNodeRemoteService.load, err', err); this.conv.close(); } } async destroy() { try { if (!this.m_fnode) return; console.log('FNodeRemoteService.destroy()', this.m_fnode.id); await this.m_fnode.destroy(); this.m_fnode = null; this.m_center = null; } catch (err) { console.error(err); } } } exports.FNodeRemoteService = FNodeRemoteService; /** * @class WSConv */ class WSConv extends cli.WSConversation { constructor(center, s) { super(s); this.m_center = center; } getRequestHeaders() { return { certificate: this.m_center.delegate.getCertificate() }; } } /** * @class FNodeRemoteClient */ class FNodeRemoteClient extends cli.WSClient { constructor(center, fnode = 'fnode://localhost/') { var url = new path_2.default.URL(fnode); url.setParam('id', center.id); if (center.publishURL) url.setParam('publish', encodeURIComponent(center.publishURL.href)); var s = url.protocol == 'fnodes:' ? 'wss:' : 'ws:'; s += '//' + url.host + url.path; super('_fnode', new WSConv(center, s)); this.m_center = center; this.m_that_fnode = new path_2.default.URL(fnode); this.m_fnode = null; } get center() { return this.m_center; } get thatFnode() { return this.m_that_fnode; } get fnode() { return this.m_fnode; } async _init() { try { var { id, time } = await Promise.race([new Promise((resolve) => { this.addEventListenerOnce('InitComplete', e => resolve(e.data)); }), util_1.default.sleep(5e3, { id: 0 })]); util_1.default.assert(id, errno_1.default.ERR_FNODE_CONNECT_TIMEOUT); this.m_fnode = new FNodeRemote(this.m_center, this, id); await this.fnode.initialize(time); } catch (err) { this.conv.close(); throw err; } } } exports.FNodeRemoteClient = FNodeRemoteClient; util_1.default.extendClass(FNodeRemoteService, FNodeRemoteIMPL); util_1.default.extendClass(FNodeRemoteClient, FNodeRemoteIMPL); service_1.default.set('_fnode', FNodeRemoteService);