jam/js/dos/connection.js

446 lines
15 KiB
JavaScript

/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2017 bLAB
** $CREATED: 7-7-16 by sbosse.
** $VERSION: 1.5.2
**
** $INFO:
**
* ================================
* DOS: Main Client-side Network Module.
* ================================
*
** Provides rpc-based inter-node connection and rpc-based services (std,cs,dns,dios)
*
** $ENDOFINFO
*/
"use strict";
var log = 0;
var Io = Require('com/io');
var Buf = Require('dos/buf');
var util = Require('util');
var xmldoc = Require('dos/ext/xmldoc');
var Comp = Require('com/compat');
var Perv = Comp.pervasives;
var Hashtbl = Comp.hashtbl;
var String = Comp.string;
var Rand = Comp.random;
var Array = Comp.array;
var trace = Io.tracing;
var div = Perv.div;
var Conn = Require('dos/connutils');
var Sch = Require('dos/scheduler');
var ConnHTTP = Require('dos/connHTTP');
var ConnNET;
var ConnUDP;
var ConnFIFO;
var Router = Require('dos/router');
var Rpc = Require('dos/rpc');
var Net = Require('dos/network');
var Cs = Require('dos/capset');
var Std = Require('dos/std');
var Dns = Require('dos/dns');
var Run = Require('dos/run');
var Mode = Conn.Mode;
if (global && global.TARGET=='node') {
ConnNET = Require('dos/connNET');
ConnUDP = Require('dos/connUDP');
ConnFIFO = Require('dos/connFIFO');
}
/**
* typeof options = {http?,tcpnet?,dports?,links?,pubhostport,bip?,bport?,myip?,
* default?,verbose:number?,monitor:number?,log:function?,debug:number?}
*
*/
function setup (options,verbose) {
var broker;
var network = {
callback:undefined,
connHTTP:_,
connTCPNET:_,
connUDP:{connections:[]},
context:_,
events:[],
initializing:[], // connect schedules
notify:function () {},
schedules:[], // main schedules
starting:[], // start schedules
mode:Mode.ONECHAN
};
network.router = Router.RpcRouter(options.pubhostport,options);
if (options.monitor) network.router.monitor=options.monitor;
if (options.env) network.env=options.env; else network.env=options.env={};
if (!options.log) options.log=Io.out;
/*
** Look-up environment variables for comm. setup ..
*/
if (Io.getenv('BROKER','') != '') {
broker=String.split(':',Io.getenv('BROKER',''));
options.bip=broker[0];
if (broker.length>=2 && String.isNumeric(broker[1]))
options.bport=Perv.int_of_string(broker[1]);
if ((broker.length>=2 && broker[1]=='nokeepalive') ||
(broker.length>2 && broker[2]=='nokeepalive'))
options.keepalive=false;
}
if (options.bip=='127.0.0.1') options.myip=options.bip;
/*
** RPC-based services
*/
network.rpc = Rpc.RpcInt(network.router);
network.std = Std.StdInt(network.rpc);
network.dns = Dns.DnsInt(network.rpc);
network.cs = Cs.CsInt(network.rpc);
network.run = Run.RunInt(network.rpc);
network.dos = true;
if (!options.env) options.env.rootdir=options.env.workdir=undefined;
// Schedule asynchronous function execution by iterating with a next function
// called in async. cllbacks.
function schedule(f,list) {
list.push(f);
}
function next() {
var f=network.schedules[0];
if (f) {
network.schedules.shift();
f();
} else {
Sch.Wakeup(network.context); // We're ready.
if (network.callback) {
var f=network.callback;
network.callback=undefined;
f(network.status())};
Sch.Schedule();
}
}
schedule(function () {network.router.init();next()},network.initializing);
schedule(function () {network.router.start();next()},network.starting);
if (ConnUDP && options.dports && options.dports.length>0)
for (var i in options.dports) {
var dport = options.dports[i];
var create = function (dport) {
var vlc = ConnUDP.Connection({
conn_port:Net.uniqport(),
rcv_ip:options.dip,
rcv_ipport:options.dport,
snd_ip:undefined,
snd_ipport:undefined,
router:network.router,
verbose:verbose||options.verbose
});
vlc.init();
schedule(function(next) {
vlc.start(function (stat) {
if(stat==Status.STD_OK) {
// Something to connect to ?
var connectit = Array.find(options.links,function (link) {return link[0]==dport;});
if (connectit != undefined) {
vlc.link(connectit[1],connectit[2]);
vlc.watchdog(true);
}
next();
}
});
},network.initializing);
return vlc;
};
network.connUDP.connections.push(create(dport));
}
if (options.http) {
if (verbose||options.verbose)
options.log('[NET] Connecting to broker '+options.bip+':'+options.bport+' (HTTP) ..');
network.connHTTP = ConnHTTP.Connection({
hostport:options.pubhostport,
srv_ip:options.bip,srv_ipport:options.bport,
my_ip:options.myip,
log:options.log,
verbose:verbose||options.verbose,
router:network.router
});
schedule(function() {
network.connHTTP.init(function() {
network.router.add_conn(network.connHTTP.rpccon);
next();
})
},network.initializing);
if (options.debug) network.connHTTP.debug(options.debug);
} else if (ConnNET && options.tcpnet) {
network.mode=options.tcpnet==1?Mode.ONECHAN:Mode.TWOCHAN;
if (verbose||options.verbose)
options.log('[NET] Connecting to broker '+options.bip+':'+options.bport+' (TCPNET) in mode '+
network.mode+' '+(options.keepalive?'KEEPALIVE':'')+'..');
network.connTCPNET = ConnNET.Connection({
hostport:options.pubhostport,
srv_ip:options.bip,srv_ipport:(options.bport+100),
my_ip:options.myip,
verbose:verbose||options.verbose,
mode:network.mode,
keepalive:options.keepalive,
log:options.log,
router:network.router
});
schedule(function() {
network.connTCPNET.init(function() {
network.router.add_conn(network.connTCPNET.rpccon);
next()
});
},network.initializing);
if (options.debug) network.connTCPNET.debug(options.debug);
}
if (options.fifo) {
network.mode=Mode.TWOCHAN;
if (verbose||options.verbose)
options.log('[NET] Connecting to broker '+options.fifodir+':'+options.fifochan+' (FIFO) in mode '+
network.mode+' '+(options.keepalive?'KEEPALIVE':'')+'..');
network.connFIFO=ConnFIFO.Connection({
hostport:options.pubhostport,
path:options.fifodir,channel:options.fifochan,
log:options.log,
verbose:verbose||options.verbose,
router:network.router
});
schedule(function() {
network.connFIFO.init(function() {
network.router.add_conn(network.connFIFO.rpccon);
next()
});
},network.initializing);
if (options.debug) network.connFIFO.debug(options.debug);
}
schedule(function () {
// Start router now ..
network.router.set_cachetimeout(1000);
next ();
},network.initializing);
// Startup actions ...
if (network.connHTTP!=undefined) {
network.connHTTP.on('error',function () {network.emit('error')});
schedule(function () {
network.connHTTP.start(function () {next()});
},network.starting);
schedule(function () {
// Connect to broker ..
network.connHTTP.alive(function (stat) {
if (stat==Net.Status.STD_OK) network.emit('connect');
if (stat==Net.Status.STD_IOERR) network.emit('disconnect');
next();
});
},network.starting);
}
if (network.connTCPNET!=undefined) {
network.connTCPNET.on('error',function () {network.emit('error')});
schedule(function () {
network.connTCPNET.start(function () {next()});
},network.starting);
schedule(function () {
// Connect to broker ..
// print('WAIT');
network.connTCPNET.alive(function (stat) {
// print('GO');
if (stat==Net.Status.STD_OK) network.emit('connect');
if (stat==Net.Status.STD_IOERR) network.emit('disconnect');
next();
});
},network.starting);
}
if (network.connFIFO!=undefined) {
network.connFIFO.on('error',function () {network.emit('error')});
schedule(function () {
network.connFIFO.start(function () {next()});
},network.starting);
schedule(function () {
// Connect to broker ..
//console.log('WAIT');
network.connFIFO.alive(function (stat) {
//console.log('GO');
if (stat==Net.Status.STD_OK) network.emit('connect');
if (stat==Net.Status.STD_IOERR) network.emit('disconnect');
next();
});
},network.starting);
}
// Ask broker for default DNS/AFS ..
if (options.default) {
schedule(function () {
// Ask broker for default DNS ..
var callback= function (data) {
var cap=Net.Parse.capability(data,0);
if (cap && cap.cap) {
options.env.rootdir=options.env.workdir=network.cs.cs_singleton(cap.cap);
if (verbose) options.log('[NET] Broker said: Default DNS capability is '+Net.Print.capability(cap.cap));
} else
if (verbose) options.log('[NET] No default DNS capability from broker!');
next ();
};
if (network.connHTTP) network.connHTTP.ask('DNS',callback);
else if (network.connTCPNET) network.connTCPNET.ask('DNS',callback);
else if (network.connFIFO) network.connFIFO.ask('DNS',callback);
},network.starting);
}
/***************** API **************/
network.emit=function (event,args) {
var e;
for (e in network.events) {
var ev=network.events[e];
if (ev[0]==event) ev[1](args);
}
};
network.init=function (callback) {
// if (callback) schedule(callback);
Sch.ScheduleBlock([
function () {
options.log('[NET] Initializing ..');
network.callback=callback;
network.schedules=Array.copy(network.initializing);
network.context=Sch.Suspend();
next();
}
]);
};
network.notify = function (xparam,xvalue) {
if (network.connHTTP) network.connHTTP.notify(xparam,xvalue);
if (network.connTCPNET) network.connTCPNET.notify(xparam,xvalue);
if (network.connFIFO) network.connFIFO.notify(xparam,xvalue);
};
//connHTTP.log(2);
network.on=function (event,callback) {
network.events.push([event,callback]);
};
// Register a node for RPC communication
network.register= function (node) {
// Register a DOS link-connection for agent migration
node.connections.dos = {
/* OLDCOMM send: function (text,dest,context) { */
send: function (msg) { /* NEWCOMM */
var text=msg.agent||msg.signal;
node.connections.dos._count += text.length;
if (Obj.isObject(msg.to /*dest*/)) // cap
{
var stat;
// This schedule block must by passed to the global (DOS) scheduler!!
Sch.B([
function () {
if (msg.agent)
network.run.ps_migrate(msg.to /*dest*/,text,function (_stat) {
stat=_stat;
});
else if (msg.signal)
network.run.ps_signal(msg.to /*dest*/,text,function (_stat) {
stat=_stat;
});
},
function () {
if (stat!=Net.Status.STD_OK) {
// context???
msg.context.error='Migration to server '+Net.Print.capability(msg.to/*dest*/)+' failed: '+Net.Print.status(stat);
// We're still in the agent process context! Throw an error for this agent ..
throw 'MOVE';
};
// kill ghost agent
msg.context.process.finalize();
}
]);
} else if (Obj.isString(msg.to /*dest*/)) { // path
}
},
status: function () {
// TODO
return network.status()==Net.Status.STD_OK;
},
_count:0,
count: function () {return node.connections.dos._count}
}
}
/*
if (network.connHTTP!=undefined) network.connHTTP.start();
if (network.connTCPNET!=undefined) network.connTCPNET.start();
if (network.connFIFO!=undefined) network.connFIFO.start();
if (network.router) network.router.start();
*/
network.start=function (callback) {
Sch.ScheduleBlock([
function () {
options.log('[NET] Starting ..');
network.callback=callback;
network.schedules=Array.copy(network.starting);
network.context=Sch.Suspend();
next();
}
]);
};
network.status = function () {
if ((network.connHTTP!=undefined && network.connHTTP.status() == Net.Status.STD_OK) ||
(network.connTCPNET!=undefined && network.connTCPNET.status() == Net.Status.STD_OK) ||
(network.connFIFO!=undefined && network.connFIFO.status() == Net.Status.STD_OK))
return Net.Status.STD_OK;
else
return Net.Status.STD_UNKNOWN;
};
network.stop=function (callback) {
options.log('[NET] Stopping ..');
if (network.connHTTP!=undefined) network.connHTTP.stop();
if (network.connTCPNET!=undefined) network.connTCPNET.stop();
if (network.connFIFO!=undefined) network.connFIFO.stop();
if (network.router) network.router.stop();
if (network.context && network.context.blocked) Sch.Wakeup(network.context);
if (callback) callback();
};
return network;
}
module.exports = {
Mode:Mode,
setup: setup
};