nxkit
Version:
This is a collection of tools, independent of any other libraries
301 lines (300 loc) • 11 kB
JavaScript
"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);