UNPKG

ampjs

Version:

A Javascript(NodeJS) framework for handling the AMP protocol

754 lines (668 loc) 18.6 kB
////////////////////////////////// AmpDate /////////////////////////////////// var net = require('net'); class AmpDate extends Date{ constructor(dateStr = undefined) { let strdate = dateStr if(strdate == undefined){ super(); } else{ super(dateStr); } } toAmpDate(){ let datestr = super.toISOString(); let timeoffset = super.getTimezoneOffset(); datestr = datestr.substring(0, datestr.length - 1); datestr += "000"; timeoffset = Math.abs(timeoffset); let zonehr = String(Math.floor(timeoffset/60)); let zonemin = String(timeoffset % 60); let direction = undefined; if(timeoffset>0){ direction = "+"; } else{ direction = "-"; } if(zonehr.length == 1){ zonehr = "0" + zonehr; } if(zonemin.length == 1){ zonemin = "0" + zonemin; } datestr = datestr + direction + zonehr + ":" + zonemin; return datestr; } } ///////////////////////////////////////////////////Commands //////////////////////////////// //var MapExt = require('./mapext'); //var server = require('./server'); class CommandLocator{ constructor(factorycommands, factorycallbacks){ this.commands = factorycommands; this.callback = factorycallbacks; } createcommand(commandtype, arguments1){ try{ return new this.commands[commandtype](arguments1, this.callback[commandtype]); } catch{ console.log("Invalid command: something happen over the wire"); return 0; } } } class Command { constructor(maparg, mapres, arguments1, fn ) { this.fn = fn; this.arguments = maparg; this.response = mapres; var is_error = this.checkerror(maparg, mapres, arguments1); if(is_error){ // Is an error command } else{ var res = this.checkarguments(arguments1); if(res == true){ this.setarguments(arguments1); } else{ console.log("[Error] - arguments does not match"); this.seterror("120", "arguments does not match"); } } } seterror(code, description){ errorObj.set_code(code); errorObj.set_description(description); } checkerror(maparg, mapres, arguments1){ var res1 = MapExt.isEmpty(arguments1); var res2 = MapExt.isEmpty(MapExt); var res3 = false; var result = false; var keyarray = Array.from(mapres.keys()); if(keyarray[0] == "_error" || keyarray[1] == "_error_code" || keyarray[2] == "_error_description"){ res3 = true; } if(res1==true && res2==true && res3 == true){ result = true; } return result; } checkresponse(response){ return MapExt.compareMapsKeys(this.response, response); } checkarguments(arguments1){ return MapExt.compareMapsKeys(this.arguments, arguments1); } setarguments(arguments1){ for (var [key, value] of arguments1) { var valuetype = this.arguments.get(key); var value1 = this.parsetype(valuetype, value); this.arguments.set(key, value1); } } setresponse(mapanswer, mapdata){ var mapresponse = new Map([...mapanswer, ...mapdata]); for (var [key, value] of mapresponse) { var valuetype = this.response.get(key); var value1 = String(value) this.response.set(key, value1); } } parsetype(valuetype, value){ if(valuetype == "integer"){; var value = parseInt(value); } else if(valuetype == "string"){ var value = String(value); } else if(valuetype == "float"){ var value = parseFloat(value); } else if(valuetype == "boolean"){ if(value == 'false' || value == '0'){ var value = Boolean(false); } else{ var value = Boolean(true); } } else if(valuetype == "datetime"){ if(value.length == 32){ var value = value; } else{ console.log("Not the right format for the datetime value"); this.seterror("160", "Not the right format for the datetime value"); } } else if(valuetype == "list[string]"){ var value = String(value); } else if(valuetype == "amplist"){ var value = parseFloat(value); } else{ console.log("[Error] - not a valid valuetype"); this.seterror("150", "not a valid valuetype"); } return value; } } //var command = require('./command'); //var server = require('./server'); var factory = { factorycommands : "", factorycallbacks : "", } function CreateController(data){ var con = new Controller(data, factory.factorycommands, factory.factorycallbacks); if(errorObj.code == ""){ answer = con.run_responder(); con.from_command(answer); } else{ con.to_error(errorObj.code, errorObj.description); } } class Controller { constructor(data, factorycommands, factorycallbacks) { this._data = data; this._ask = undefined; this._answer = undefined; this._command = undefined; this._error = undefined; this._error_code = undefined; this._error_description = undefined; this.factorycommands = factorycommands; this.factorycallbacks = factorycallbacks; this.arguments = new Map(); this.parsedata() this.locator = this.to_command() } get data() { return this._data; } set data(newdata) { this._data = newdata; } get ask() { return this._ask; } set ask(newdata) { this._ask = newdata; } get answer() { return this._answer; } set answer(newdata) { this._answer = newdata; } get command() { return this._command; } set command(newdata) { this._command = newdata; } get error() { return this._error; } set error(newdata) { this._error = newdata; } get error_code() { return this._error_code; } set error_code(newdata) { this._error_code = newdata; } get error_description() { return this._error_description; } set error_description(newdata) { this._error_description = newdata; } parsedata(){ Object.entries(this.data).forEach(entry => { let key = entry[0]; let value = entry[1]; if(key=="_ask"){ this.ask = value; } else if(key=="_answer"){ this.answer = value; } else if(key=="_command"){ this.command = value; } else if(key=="_error"){ this.error = value; this.command = "error"; } else if(key=="_error_code"){ this.error_code = value; } else if(key=="_error_description"){ this.error_description = value; } else{ this.arguments.set(key, value); } }); } checkkey(key, value){ if(this.arguments.has(key)){ value1 = this.arguments.get(key); if(Array.isArray(value1)){ value1.push(value); this.arguments.set(key, value1); } else{ valuearray = [value1, value]; this.arguments.set(key, valuearray); } return true; } return false; } run_responder(){ return this.locator.responder(this.locator.arguments); } to_command(){ let customlocator = new CommandLocator(this.factorycommands, this.factorycallbacks); let com = customlocator.createcommand(this.command, this.arguments); if(com==0){ this.to_error("111","the incoming data has been corrupted"); } else{ return com } } from_command(mapdata){ this.answer = String(this.ask); var mapanswer = new Map(); mapanswer.set('_answer', this.answer); var res = this.locator.checkresponse(mapdata); if(res == true){ this.locator.setresponse(mapanswer, mapdata); } else{ console.log("[Error] - the responds arguments does not match"); this.to_error("101","the response arguments does not match"); return; } this.to_box(); } dict_reverse(obj) { var new_obj = {}; let rev_obj = Object.keys(obj).reverse(); rev_obj.forEach(function(i) { new_obj[i] = obj[i]; }) return new_obj; } to_error(error_code, error_description){ this.error = String(this.ask); this.error_code = error_code; this.error_description = error_description; var maperror = new Map([["_error", this.error], ["_error_code", this.error_code], ["_error_description", this.error_description]]); var errorobj = {}; errorobj["_error"] = Buffer.alloc((String(this.error).length), String(this.error), 'utf8'); errorobj["_error_code"] = Buffer.alloc((String(this.error_code).length), String(this.error_code), 'utf8'); errorobj["_error_description"] = Buffer.alloc((String(this.error_description).length), String(this.error_description), 'utf8'); server.reply(errorobj); } to_box(data){ var map1 = this.locator.response; var replyCall = {}; for (var [key, value] of map1) { replyCall[key] = Buffer.alloc((String(value).length), String(value), 'utf8'); } var res = this.dict_reverse(replyCall); const entries = Object.entries(res); console.log("replycall => " + entries ); server.reply(res); } from_box(data){ } } //////////////////////////////////// mapext ////////////////////////////////////////// class MapExt extends Map{ static reverseMap(map1){ return new Map([...map1].reverse()); } static invertMap(map1){ let map2 = new Map(); for (let [key, value] of map1) { let v = map1.get(key); map2.set(v, key); } return map2 } static isEmpty(map1){ if(map1.size == 0){ return false; } return true; } static compareMapSize(map1, map2) { if (map1.size !== map2.size) { return false; } return true; } static compareMaps(map1, map2) { var testVal; if(!MapExt.compareMapSize(map1, map2)){ return false; } for (var [key, val] of map1) { testVal = map2.get(key); if (testVal !== val || (testVal === undefined && !map2.has(key))) { return false; } } return true; } static compareMapsKeys(map1, map2) { if(!MapExt.compareMapSize(map1, map2)){ return false; } for (var [key, val] of map1) { if (!map2.has(key)) { return false; } } return true; } static mergeMaps(map1, map2) { return new Map([...map1, ...map2]); } } ////////////////////////////////////////// receiverbox /////////////////////////////////// function ReceiverBox (receiveBoxCallback) { this.box = {}; this._unprocessed = Buffer.alloc(0); this._nextKey = null; this.receiveBox = receiveBoxCallback || function(box) { console.log(box); }; //receiveBoxCallback } ReceiverBox.prototype.dataReceived = function (data) { this._unprocessed = Buffer.concat([this._unprocessed, data]); var offset = 0; var nextOffset = 0; var flag = true; var arrlength = []; while(flag){ var datalength = this._unprocessed.length; var length = this._unprocessed.readInt16BE(offset, offset + 2); arrlength.push(length); if(datalength < length + 2 || datalength < nextOffset ){ return; } nextOffset = offset + length + 2; try{ var len1 = this._unprocessed.readInt16BE(nextOffset, nextOffset + 2); } catch{ return; } if(len1 == 0){ flag = false; } offset = nextOffset; } offset = 2; keystate = 0; for (var index = 0; index < arrlength.length; index++) { if(keystate==0){ key = this._unprocessed.slice(offset, arrlength[index] + offset); keystate = 1; } else{ value = this._unprocessed.slice(offset, arrlength[index] + offset); this.box[key] = value; keystate = 0; } offset = offset + arrlength[index] + 2; } this.receiveBox(this.box); this.box = {}; this._unprocessed = Buffer.alloc(0); } //////////////////////////////////////////// SenderBox //////////////////////////////////////// function BoxSender (box) { var self = this; var keys; self.box = box; self._wireBuffers = []; function pushBuffer(buffer) { var idx = self._wireBuffers.length; self._wireBuffers.push(Buffer.alloc(2)); if (buffer.length > 0) { self._wireBuffers.push(buffer); } self._wireBuffers[idx].writeInt16BE(buffer.length, 0); } keys = Object.keys(self.box); for (var i=0; i<keys.length; i++) { pushBuffer(Buffer.from(keys[i], 'utf8')); pushBuffer(self.box[keys[i]]); } pushBuffer(Buffer.alloc(0)); } BoxSender.prototype.writeToTransport = function (transportWrite) { var self = this; for (var i=0; i<self._wireBuffers.length; i++) { transportWrite(self._wireBuffers[i]); } }; /////////////////////////////////// Server //////////////////////////////////// //var net = require('net'); //var senderbox = require('./senderbox'); //var receiverbox = require('./receiverbox'); //var MapExt = require('./mapext'); var callback = { fname : "", connected : "", received : "", setcallback : function(data) { this.fname = data; }, getcallback : function() { return this.fname; }, setconnected : function(data) { this.connected = data; }, getconnected : function() { return this.connected; }, setreceived : function(data) { this.received = data; }, getreceived : function() { return this.received; }, } var errorObj = { code : "", description: "", set_code : function(data) { this.code = data; }, get_code : function() { return this.code; }, set_description : function(data) { this.description = data; }, get_description : function() { return this.description; }, reset_error : function(){ this.code = ""; this.description = ""; }, } const ask = { ask : 0, get_ask : function(){ this.ask += 1; if(this.ask > 256){ this.ask = 1; } return this.ask; } } var server = net.createServer(function (socket) { console.log('server connected'); this.callback = callback.getcallback(); br = new ReceiverBox(this.callback); this.reply = function(data) { bx = new BoxSender(data); console.log('sending AMP box'); bx.writeToTransport(function(data) {socket.write(data.toString('utf8')); }); return; }; socket.on('error', function(err) { console.log('An error has occured => ' + err); }); socket.on('end', function() { console.log(br.box); console.log('client has disconnected'); }); socket.on('close', function() { console.log(br.box); console.log('server has closed'); }); socket.on('data', function(data) { br.dataReceived(data); }); }); var client = new function() { this.clientport = ""; this.clientaddress = ""; this.connected = ""; this.received = ""; this.remoteCall = ""; this.printInfo = function() { console.log(this.clientport); console.log(this.clientaddress); console.log(this.connected); console.log(this.received); }; this.remoteCall = function(mapdata1) { mapdata = commandmapping(mapdata1); var myCall = {}; for (var [key, value] of mapdata) { myCall[key] = Buffer.alloc(String(value).length, String(value), 'utf8'); } const entries = Object.entries(myCall); console.log("Outgoing Data => " + entries); var bx = new BoxSender(myCall); console.log('sending AMP box'); bx.writeToTransport(function(myCall) {client.connection.write(myCall.toString('utf8')); }); }; this.start = function(){ this.connection = net.createConnection(this.clientport, this.clientaddress, function () { this.br = new ReceiverBox(checklist); }); this.connection.on('connect', function(data) { console.log("Connect to the server"); client.connected(); }); this.connection.on('data', function(data) { this.br.dataReceived(data); }); this.connection.on('end', function() { console.log(br.box); console.log('server disconnected'); }); }; } function checklist(data){ for (const property in data) { if(property != '_answer'){ temp = data[property]; tempbuf = Buffer.from(temp); if(tempbuf.length<2){ break; } len1 = tempbuf.readInt16BE(0, 2); if(len1 + 2 < tempbuf.length){ arr = toArray(data[property]); data[property] = arr; } } } client.received(data); } function toArray(buf){ var buflen = buf.length; offset = 0; flag = true; dataarray = [] while(flag){ len1 = buf.readInt16BE(offset, offset + 2); word = buf.slice(offset + 2, len1 + offset + 2); dataarray.push(String(word)); offset = offset + 2 + len1; if(offset >= buflen){ flag = false } } return dataarray; } function fromArray(dataarray){ var buf = Buffer.alloc(0); len = dataarray.length; for (i = 0; i < len; i++) { data = dataarray[i]; lendata = data.length; var tempbuf = Buffer.alloc(2); var tempbuf1 = Buffer.from(data); tempbuf.writeInt16BE(lendata, 0); tempbuf = Buffer.concat([tempbuf, tempbuf1]); buf = Buffer.concat([buf, tempbuf]); //self._unprocessed = Buffer.concat([self._unprocessed, data]); } return buf.toString(); } function commandmapping(map1){ resultmap = new Map(); var ask1 = String(ask.get_ask()); resultmap.set('_ask', ask1); for (var [key, value] of map1) { if(key.toLowerCase() == "command"){ resultmap.set('_command', value); } else{ if(typeof(value) == 'object'){ value = fromArray(value); } else{ value = String(value); } resultmap.set(key, value); } } return resultmap; } module.exports = { server: server, callback: callback, errorObj: errorObj, client: client, BoxSender: BoxSender, ReceiverBox: ReceiverBox, Controller: Controller, CreateController: CreateController, factory: factory, AmpDate: AmpDate, CommandLocator: CommandLocator, Command: Command }; //////////////////////////////////////////////////////////////////////////////