Mon 21 Jul 22:43:21 CEST 2025
This commit is contained in:
parent
6a39b189ab
commit
4db3e7e02b
614
js/dos/rpc.js
Normal file
614
js/dos/rpc.js
Normal file
|
@ -0,0 +1,614 @@
|
|||
/**
|
||||
** ==============================
|
||||
** 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-2016 bLAB
|
||||
** $CREATED: 31-3-15 by sbosse.
|
||||
** $VERSION: 1.4.4
|
||||
**
|
||||
** $INFO:
|
||||
**
|
||||
** DOS: Remote Procedure Call Interface
|
||||
**
|
||||
** $ENDOFINFO
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var log = 0;
|
||||
|
||||
var util = Require('util');
|
||||
var Io = Require('com/io');
|
||||
var Comp = Require('com/compat');
|
||||
var String = Comp.string;
|
||||
var Array = Comp.array;
|
||||
var Perv = Comp.pervasives;
|
||||
var Buf = Require('dos/buf');
|
||||
var Net = Require('dos/network');
|
||||
var Sch = Require('dos/scheduler');
|
||||
var Status = Net.Status;
|
||||
|
||||
// Transaction identifier number tracking. Must be shared by all Rpc_int instances!
|
||||
var _transaction_id = 0;
|
||||
function transaction_id() {
|
||||
var tid=_transaction_id;
|
||||
_transaction_id=(_transaction_id+1)%65536;
|
||||
return tid;
|
||||
}
|
||||
|
||||
/** RPC Operation Type
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
var Operation = {
|
||||
SEND:1,
|
||||
RECV:2,
|
||||
TRANSREQ:3,
|
||||
TRANSREP:4,
|
||||
TRANSAWAIT:5,
|
||||
TRANS:6,
|
||||
GETREQ:7,
|
||||
PUTREP:8,
|
||||
LOOKUP:9, // Search the host port that has a RPC server
|
||||
IAMHERE:10,
|
||||
IAMNOTHERE:11,
|
||||
WHOIS:12,
|
||||
WHEREIS:13, // Search a connection to a host
|
||||
HEREIS:14, // Reply of host
|
||||
print:function(op) {
|
||||
switch (op) {
|
||||
case Operation.SEND: return "SEND";
|
||||
case Operation.RECV: return "RECV";
|
||||
case Operation.TRANSREQ: return "TRANSREQ";
|
||||
case Operation.TRANSREP: return "TRANSREP";
|
||||
case Operation.TRANSAWAIT: return "TRANSAWAIT";
|
||||
case Operation.TRANS: return "TRANS";
|
||||
case Operation.GETREQ: return "GETREQ";
|
||||
case Operation.PUTREP: return "PUTREP";
|
||||
case Operation.IAMHERE: return "IAMHERE";
|
||||
case Operation.IAMNOTHERE: return "IAMNOTHERE";
|
||||
case Operation.WHOIS: return "WHOIS";
|
||||
case Operation.WHEREIS: return "WHEREIS";
|
||||
case Operation.HEREIS: return "HEREIS";
|
||||
case Operation.LOOKUP: return "LOOKUP";
|
||||
default: return "Rpc.Operation?";
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function getData(data) {
|
||||
if (!String.equal(data.val,'')) return data.val;
|
||||
else return data.children.toString();
|
||||
}
|
||||
|
||||
/** RPCIO Packet Object (message context handle)
|
||||
** Each transaction, each request and reply get an RPCIO handle.
|
||||
** Additionally, server localization uses RPCIO packets.
|
||||
**
|
||||
*
|
||||
* @param {(Operation.SEND|*)} [operation]
|
||||
* @param {header} [hdr]
|
||||
* @param {Buffer|string} [data]
|
||||
* @param {taskcontext} [context]
|
||||
* @param {function} [callback]
|
||||
* @constructor
|
||||
* @typedef {{operation,pubport,header,data,pos,context:context,callback,host,hop,hop_max,tid,timeout,status,index,
|
||||
* }} rpcio~obj
|
||||
* @see rpcio~obj
|
||||
* @see rpcio~meth
|
||||
*/
|
||||
var rpcio = function(operation,hdr,data,context,callback) {
|
||||
if (operation==undefined) {
|
||||
// Create empty rpcio with headers
|
||||
this.operation=undefined;
|
||||
/*
|
||||
** Public server port (GETREQ only)
|
||||
*/
|
||||
this.pubport=undefined;
|
||||
/*
|
||||
** Connection Link Port Identifier
|
||||
*/
|
||||
this.connport=undefined;
|
||||
this.header=Net.Header();
|
||||
this.data=undefined;
|
||||
this.pos=0;
|
||||
this.context=Sch.GetCurrent(); // TODO Should be undefined?
|
||||
this.callback=undefined;
|
||||
|
||||
/*
|
||||
** Source host port (initiator)
|
||||
*/
|
||||
this.hostport=undefined;
|
||||
/*
|
||||
** Destination host port (executor).
|
||||
*/
|
||||
this.sendport=undefined;
|
||||
|
||||
this.hop=0;
|
||||
this.hop_max=Net.DEF_RPC_MAX_HOP;
|
||||
this.tid=0;
|
||||
this.timeout=-1;
|
||||
this.status=undefined;
|
||||
this.index=-1;
|
||||
this.cache=[];
|
||||
|
||||
} else {
|
||||
this.operation = operation;
|
||||
/*
|
||||
** Public server port (GETREQ only)
|
||||
*/
|
||||
this.pubport=Net.Port();
|
||||
/*
|
||||
** Connection Link Port Identifier
|
||||
*/
|
||||
this.connport=undefined;
|
||||
this.header = hdr;
|
||||
this.data = Buf.Buffer(data).data;
|
||||
this.pos=0;
|
||||
this.context = context;
|
||||
this.callback = callback;
|
||||
/*
|
||||
** Source host port (initiator)
|
||||
*/
|
||||
this.hostport=undefined;
|
||||
/*
|
||||
** Destination host port (executor)
|
||||
*/
|
||||
this.sendport=undefined;
|
||||
|
||||
this.hop=0;
|
||||
this.hop_max=Net.DEF_RPC_MAX_HOP;
|
||||
this.tid=0;
|
||||
this.timeout=-1;
|
||||
this.status=undefined;
|
||||
this.index=-1;
|
||||
this.cache=[];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param [operation]
|
||||
* @param {header} [hdr]
|
||||
* @param {Buffer|string} [data]
|
||||
* @param {taskcontext} [context]
|
||||
* @returns {rpcio}
|
||||
*/
|
||||
function Rpcio(operation,hdr,data,context) {
|
||||
var obj = new rpcio(operation,hdr,data,context);
|
||||
Object.preventExtensions(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** 1. Return a (weak) copy of this rpcio packet
|
||||
** 2. Copy source content to this rpcio (weak, only first level copy)
|
||||
*
|
||||
*/
|
||||
rpcio.prototype.copy = function (src,callback) {
|
||||
if (src) {
|
||||
this.operation=src.operation;
|
||||
this.pubport=src.pubport;
|
||||
this.connport=src.connport;
|
||||
this.header=src.header;
|
||||
this.pos=src.pos;
|
||||
this.data=src.data;
|
||||
this.hostport=src.hostport;
|
||||
this.sendport=src.sendport;
|
||||
this.tid=src.tid;
|
||||
//this.timeout=src.timeout;
|
||||
this.timeout=-1;
|
||||
this.hop=src.hop;
|
||||
this.hop_max=src.hop_max;
|
||||
this.status=src.status;
|
||||
this.cache=[];
|
||||
this.context=undefined;
|
||||
this.callback=callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Initialize a rpcio object
|
||||
*
|
||||
* @param {(Operation.SEND|*)} [operation]
|
||||
* @param {header} [hdr]
|
||||
* @param {Buffer|string} [data]
|
||||
* @param [context]
|
||||
* @param {function} [callback]
|
||||
*/
|
||||
rpcio.prototype.init = function(operation,hdr,data,context,callback) {
|
||||
// Create empty rpcio with headers
|
||||
this.operation=operation;
|
||||
this.pubport=undefined;
|
||||
this.connport=undefined;
|
||||
// if (hdr!=undefined && !(hdr==this.header))
|
||||
// Net.Copy.header(hdr,this.header);
|
||||
if (hdr!=undefined)
|
||||
this.header=Net.Header(hdr.h_port,hdr.h_priv,hdr.h_command,hdr.h_status);
|
||||
else if (hdr == undefined) {
|
||||
this.header=Net.Header(); // start with a clean unreferenced header structure - may be relinked
|
||||
// this.header.h_status=undefined;
|
||||
// this.header.h_command=undefined;
|
||||
// this.header.h_port=Net.Port();
|
||||
// this.header.h_priv.prv_obj=0;
|
||||
// this.header.h_priv.prv_rights=0;
|
||||
// this.header.h_priv.prv_rand=Net.Port();
|
||||
}
|
||||
if (data) this.data=Buf.Buffer(data).data; else this.data=new Buffer('');
|
||||
//if (context) this.context=context;
|
||||
//if (callback) this.callback=undefined;
|
||||
this.pos=0;
|
||||
this.hostport=undefined;
|
||||
this.sendport=undefined;
|
||||
this.tid=-1;
|
||||
this.timeout=-1;
|
||||
this.hop=0;
|
||||
this.hop_max=Net.DEF_RPC_MAX_HOP;
|
||||
this.status=undefined;
|
||||
this.cache=[];
|
||||
|
||||
this.context=context;
|
||||
this.callback=callback;
|
||||
};
|
||||
|
||||
// TODO? HOP/HOPMAX fields?????
|
||||
|
||||
/** Encode RPCIO packet to XML
|
||||
*
|
||||
* @param {string} wrap xml/rpc
|
||||
* @returns {string}
|
||||
*/
|
||||
rpcio.prototype.to_xml=function(wrap) {
|
||||
var self=this,
|
||||
body,buf;
|
||||
buf = Buf.Buffer();
|
||||
Buf.buf_put_hdr(buf, this.header);
|
||||
String.match(wrap,[
|
||||
['xml',function() {
|
||||
body = '<xml><header>' + Buf.buf_to_hex(buf) + '</header>';
|
||||
body = body + '<data>' + Buf.buf_to_hex(self) + '</data></xml>';
|
||||
}],
|
||||
['rpc',function(){
|
||||
body = '<rpc operation="'+Operation.print(self.operation)+
|
||||
'" hostport="' + Net.port_to_str(self.hostport) +
|
||||
'" sendport="' + Net.port_to_str(self.sendport) +
|
||||
'" tid="' + self.tid + '">';
|
||||
body = body + '<header>' + Buf.buf_to_hex(buf) + '</header>';
|
||||
body = body + '<data>' + Buf.buf_to_hex(self) + '</data></rpc>';
|
||||
}]
|
||||
]);
|
||||
return body;
|
||||
};
|
||||
|
||||
/** Decode XML to RPCIO
|
||||
*
|
||||
* .. xml = new xmldoc.XmlDocument(body) ..
|
||||
*/
|
||||
rpcio.prototype.of_xml=function(xml) {
|
||||
var self=this,
|
||||
header,data,buf,
|
||||
hdr = Net.Header(),
|
||||
rpc,tid,hostport,sendport,operation;
|
||||
if (String.equal(xml.name,'rpc')) {
|
||||
/*
|
||||
** Wrapped RPCIO <rpc><header/></data>
|
||||
*/
|
||||
rpc = xml;
|
||||
tid = rpc.attr.tid;
|
||||
hostport = rpc.attr.hostport;
|
||||
sendport = rpc.attr.sendport;
|
||||
operation = rpc.attr.operation;
|
||||
header = rpc.childNamed('header');
|
||||
data = rpc.childNamed('data');
|
||||
} else if (String.equal(xml.name,'xml')) {
|
||||
/*
|
||||
** Plain RPCIO <xml><header/><data/>
|
||||
*/
|
||||
header = xml.childNamed('header');
|
||||
data = xml.childNamed('data');
|
||||
}
|
||||
if (header != undefined) {
|
||||
buf = Buf.Buffer();
|
||||
Buf.buf_of_hex(buf, getData(header));
|
||||
Buf.buf_get_hdr(buf, hdr);
|
||||
}
|
||||
switch (operation) {
|
||||
case 'TRANSREQ':
|
||||
self.init(Operation.TRANSREQ, hdr, getData(data));
|
||||
self.tid = Perv.int_of_string(tid);
|
||||
self.hostport = Net.port_of_param(hostport);
|
||||
self.sendport = Net.port_of_param(sendport);
|
||||
return 1;
|
||||
break;
|
||||
case 'TRANSREP':
|
||||
// TRANSREP for a client on this host
|
||||
self.init(Operation.TRANSREP, hdr, getData(data));
|
||||
self.hostport = Net.port_of_param(hostport);
|
||||
self.sendport = Net.port_of_param(sendport);
|
||||
self.tid = Perv.int_of_string(tid);
|
||||
return 1;
|
||||
break;
|
||||
case 'LOOKUP':
|
||||
buf = Buf.Buffer(getData(xml));
|
||||
hdr.h_port = Buf.buf_get_port(buf);
|
||||
self.init(Operation.LOOKUP, hdr);
|
||||
return 1;
|
||||
break;
|
||||
case 'IAMHERE':
|
||||
buf = Buf.Buffer(getData(xml));
|
||||
hdr.h_port = Buf.buf_get_port(buf);
|
||||
self.init(Operation.IAMHERE, hdr);
|
||||
self.hostport = Net.port_of_param(hostport);
|
||||
self.sendport = Net.port_of_param(sendport);
|
||||
return 1;
|
||||
break;
|
||||
case 'WHEREIS':
|
||||
case 'HEREIS':
|
||||
self.init(Operation.IAMHERE, hdr);
|
||||
self.hostport = Net.port_of_param(hostport);
|
||||
self.sendport = Net.port_of_param(sendport);
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/** Encode RPCIO packet to JSON object
|
||||
*
|
||||
* @param {string} format simple/extended
|
||||
* @returns {string}
|
||||
*/
|
||||
rpcio.prototype.to_json=function(format) {
|
||||
var self=this;
|
||||
|
||||
var obj={},buf;
|
||||
buf = Buf.Buffer();
|
||||
Buf.buf_put_hdr(buf, this.header);
|
||||
String.match(format,[
|
||||
['simple',function() {
|
||||
obj = {header:Buf.buf_to_hex(buf),data:Buf.buf_to_hex(self)};
|
||||
}],
|
||||
['extended',function(){
|
||||
obj = {
|
||||
operation: Operation.print(self.operation),
|
||||
hostport: self.hostport,
|
||||
sendport: self.sendport,
|
||||
tid: self.tid,
|
||||
header: Buf.buf_to_hex(buf),
|
||||
data: Buf.buf_to_hex(self)
|
||||
}
|
||||
}]
|
||||
]);
|
||||
return obj;
|
||||
};
|
||||
|
||||
/** Decode JSON object to RPCIO
|
||||
*
|
||||
*/
|
||||
rpcio.prototype.of_json=function(obj,format) {
|
||||
var self=this,
|
||||
header=obj.header,
|
||||
data=obj.data,
|
||||
buf,
|
||||
hostport = obj.hostport,
|
||||
sendport = obj.sendport,
|
||||
operation = obj.operation||'',
|
||||
tid = obj.tid,
|
||||
hdr = Net.Header();
|
||||
// console.log(obj)
|
||||
if (header != undefined) {
|
||||
buf = Buf.Buffer();
|
||||
Buf.buf_of_hex(buf, header);
|
||||
Buf.buf_get_hdr(buf, hdr);
|
||||
}
|
||||
switch (operation) {
|
||||
case 'TRANSREQ':
|
||||
self.init(Operation.TRANSREQ, hdr, data);
|
||||
self.tid = Perv.int_of_string(tid);
|
||||
self.hostport = hostport;
|
||||
self.sendport = sendport;
|
||||
return 1;
|
||||
break;
|
||||
case 'TRANSREP':
|
||||
// TRANSREP for a client on this host
|
||||
self.init(Operation.TRANSREP, hdr, data);
|
||||
self.hostport = hostport;
|
||||
self.sendport = sendport;
|
||||
self.tid = Perv.int_of_string(tid);
|
||||
return 1;
|
||||
break;
|
||||
case 'LOOKUP':
|
||||
buf = Buf.Buffer(data);
|
||||
hdr.h_port = Buf.buf_get_port(buf);
|
||||
self.init(Operation.LOOKUP, hdr);
|
||||
self.hostport = hostport;
|
||||
return 1;
|
||||
break;
|
||||
case 'IAMHERE':
|
||||
buf = Buf.Buffer(data);
|
||||
hdr.h_port = Buf.buf_get_port(buf);
|
||||
self.init(Operation.IAMHERE, hdr);
|
||||
self.hostport = hostport;
|
||||
self.sendport = sendport;
|
||||
return 1;
|
||||
break;
|
||||
case 'WHEREIS':
|
||||
case 'HEREIS':
|
||||
self.init(Operation.IAMHERE, hdr);
|
||||
self.hostport = hostport;
|
||||
self.sendport = sendport;
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/** A RPC connection. Used by the router only.
|
||||
*
|
||||
* @param {port} port
|
||||
* @param {function(rpcio)|undefined} [send]
|
||||
* @param {function()} [alive]
|
||||
* @constructor
|
||||
*/
|
||||
var rpcconn = function (port,send,alive) {
|
||||
this.port=port;
|
||||
// function(rpcio,callback?)
|
||||
// type callback is function (stat,rpcio)
|
||||
this.send=send;
|
||||
this.alive=alive||function() {return false;};
|
||||
this.multiport=false;
|
||||
this.stats = {
|
||||
op_ask:0,
|
||||
op_brokerrep:0,
|
||||
op_brokerreq:0,
|
||||
op_get:0,
|
||||
op_messages:0,
|
||||
op_notify:0,
|
||||
op_put:0,
|
||||
|
||||
op_broadcast:0,
|
||||
op_forward:0,
|
||||
op_schedule:0,
|
||||
|
||||
op_alive:0,
|
||||
op_send:0,
|
||||
|
||||
op_link:0,
|
||||
op_unlink:0,
|
||||
op_receive:0,
|
||||
|
||||
op_ping:0,
|
||||
op_pong:0,
|
||||
|
||||
op_error:0,
|
||||
op_noentr:0
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
** RPC Transaction Object
|
||||
*
|
||||
*
|
||||
* @param {rpcrouter} router
|
||||
* @constructor
|
||||
* @typedef {{router:rpcrouter}} rpcint~obj
|
||||
* @see rpcint~obj
|
||||
* @see rpcint~meth
|
||||
*/
|
||||
|
||||
// function (router:rpcrouter) -> rpcint template
|
||||
var rpcint = function (router) {
|
||||
// RPC message router
|
||||
this.router=router;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{trans:rpcint.trans,getreq:rpcint.getreq,putrep:rpcint.putrep}} rpcint~meth
|
||||
*/
|
||||
/**
|
||||
** Client Interface
|
||||
** Optional callback: function(rpcio)
|
||||
*
|
||||
* @param {rpcio} rpcio
|
||||
* @param {function} [callback]
|
||||
*/
|
||||
rpcint.prototype.trans = function(rpcio,callback) {
|
||||
var rpcio=rpcio;
|
||||
var hdr=rpcio.header;
|
||||
Sch.Suspend();
|
||||
rpcio.operation=Operation.TRANSREQ;
|
||||
rpcio.context=Sch.GetCurrent();
|
||||
rpcio.callback=callback;
|
||||
rpcio.tid=transaction_id();
|
||||
rpcio.hostport=this.router.hostport;
|
||||
this.router.route(rpcio);
|
||||
};
|
||||
|
||||
/**
|
||||
** Server interface
|
||||
*
|
||||
*
|
||||
* @param {rpcio} rpcio
|
||||
*/
|
||||
rpcint.prototype.getreq = function (rpcio) {
|
||||
rpcio.pubport=Net.prv2pub(rpcio.header.h_port);
|
||||
rpcio.operation=Operation.GETREQ;
|
||||
Sch.Suspend();
|
||||
rpcio.context=Sch.GetCurrent();
|
||||
this.router.route(rpcio)
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param {rpcio} rpcio
|
||||
*/
|
||||
rpcint.prototype.putrep = function (rpcio) {
|
||||
rpcio.operation=Operation.PUTREP;
|
||||
this.router.route(rpcio);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Rpcio: Rpcio,
|
||||
/**
|
||||
*
|
||||
* @param {rpcrouter} router
|
||||
* @returns {rpcint}
|
||||
*/
|
||||
RpcInt: function(router) {
|
||||
var obj = new rpcint(router);
|
||||
Object.preventExtensions(obj);
|
||||
return obj;
|
||||
},
|
||||
/**
|
||||
* @param {port} port
|
||||
* @param {function(rpcio)} [send]
|
||||
* @param {function()} [alive]
|
||||
* @returns {rpcconn}
|
||||
*/
|
||||
RpcConn: function(port,send,alive) {
|
||||
var obj = new rpcconn(port,send,alive);
|
||||
Object.preventExtensions(obj);
|
||||
return obj;
|
||||
},
|
||||
Print: {
|
||||
rpcio: function(rpcio) {
|
||||
if (rpcio) {
|
||||
var str = '{';
|
||||
str = str + 'header: ' + Net.Print.header(rpcio.header) + ', ';
|
||||
if (rpcio.data!=undefined) str = str + 'data[' + rpcio.data.length + '], ';
|
||||
str = str + 'hostport: ' + Net.Print.port(rpcio.hostport);
|
||||
str = str + ' connport: ' + Net.Print.port(rpcio.connport);
|
||||
str = str + ' sendport: ' + Net.Print.port(rpcio.sendport);
|
||||
if (rpcio.tid>=0) str = str + ', tid: ' + rpcio.tid;
|
||||
if (rpcio.operation!=undefined) str = str + ', operation: ' + Operation.print(rpcio.operation);
|
||||
if (rpcio.timeout>0) str=str + ', timeout: '+rpcio.timeout;
|
||||
if (rpcio.status!=undefined) str=str + ', status: '+Net.Status.print(rpcio.status);
|
||||
if (rpcio.index>=0) str=str + ', index: '+rpcio.index;
|
||||
if (rpcio.context!=undefined) str=str + ', context: '+rpcio.context.id;
|
||||
str = str + ' hop='+rpcio.hop+' hopmax='+rpcio.hop_max;
|
||||
str = str +(rpcio.callback!=undefined?' CB':'')+(rpcio.context!=undefined?' CT':'')+'}';
|
||||
return str;
|
||||
} else return "undefined";
|
||||
}
|
||||
},
|
||||
Operation:Operation,
|
||||
transaction_id:transaction_id
|
||||
};
|
Loading…
Reference in New Issue
Block a user