radio/magnus/lib/wSocket/socket.js

218 lines
7.1 KiB
JavaScript

"use strict";
var WebSocket = require("ws");
var Subscribable = require("../utils/subscribable");
var Event = require("../wType/event");
var ByteArray = require("../wType/bytearray");
var String = require("../wType/string");
var Vocabulary = require("../wType/vocabulary");
var Uint64 = require("../wType/uint64");
var Address = require("../wType/address");
var factory = require("../wType/factory");
var Socket = Subscribable.inherit({
"className": "Socket",
"constructor": function(name, socket, id) {
if (!name) {
throw new Error("Can't construct a socket without a name");
}
Subscribable.fn.constructor.call(this);
this._state = DISCONNECTED;
this._dState = SIZE;
this._name = name instanceof String ? name : new String(name);
this._remoteName = new String();
this._id = new Uint64(0);
this._serverCreated = false;
this._helperBuffer = new ByteArray(4);
this._initProxy();
if (socket) {
this._serverCreated = true;
this._socket = socket;
this._id.destructor();
this._id = id.clone();
this._socket.on("close", this._proxy.onClose);
this._socket.on("error", this._proxy.onError);
this._socket.on("message", this._proxy.onMessage);
}
},
"destructor": function() {
this.close();
if (this._state === DISCONNECTING) {
var onclose = function() {
Subscribable.fn.destructor.call(this);
}
this.on("disconnected", onclose.bind(this));
} else {
Subscribable.fn.destructor.call(this);
}
},
"close": function() {
if ((this._state !== DISCONNECTED) && (this._state !== DISCONNECTING)) {
this._state = DISCONNECTING;
this._socket.close();
}
},
"getId": function() {
return this._id;
},
"getRemoteName": function() {
return this._remoteName;
},
"_initProxy": function() {
this._proxy = {
onClose: this._onClose.bind(this),
onError: this._onError.bind(this),
onMessage: this._onMessage.bind(this)
};
},
"isOpened": function() {
return this._state !== undefined && this._state === CONNECTED;
},
"_onClose": function(ev) {
this._state = DISCONNECTED;
this.trigger("disconnected", ev, this);
},
"_onError": function(err) {
this.trigger("error", err);
},
"_onEvent": function(ev) {
if (ev.isSystem()) {
var cmd = ev._data.at("command").toString();
switch(cmd) {
case "setId":
if (this._serverCreated) {
if (this._state === CONNECTING) {
this.trigger("negotiationId", ev._data.at("id"));
} else {
throw new Error("An attempt to set id in unexpected time");
}
} else {
this._setId(ev._data.at("id"));
this._setRemoteName();
}
break;
case "setName":
this._setName(ev._data.at("name"));
if (!ev._data.at("yourName")["=="](this._name)) {
this._setRemoteName();
}
this._state = CONNECTED;
this.trigger("connected");
break;
default:
throw new Error("Unknown system command: " + cmd);
}
} else {
this.trigger("message", ev);
}
ev.destructor();
},
"_onMessage": function(msg) {
var raw = new Uint8Array(msg);
var i = 0;
while (i < raw.length) {
switch (this._dState) {
case SIZE:
i = this._helperBuffer.fill(raw, raw.length, i);
if (this._helperBuffer.filled()) {
var size = this._helperBuffer.pop32();
this._helperBuffer.destructor();
this._helperBuffer = new ByteArray(size + 1);
this._dState = BODY;
}
break;
case BODY:
i = this._helperBuffer.fill(raw, raw.length, i);
if (this._helperBuffer.filled()) {
var ev = factory(this._helperBuffer);
this._onEvent(ev);
this._helperBuffer.destructor();
this._helperBuffer = new ByteArray(4);
this._dState = SIZE;
}
break;
}
}
},
"open": function(addr, port) {
if (this._state === DISCONNECTED) {
this._state = CONNECTING;
this._remoteName.destructor();
this._remoteName = new String();
this._socket = new WebSocket("ws://"+ addr + ":" + port);
this._socket.on("close", this._proxy.onClose);
this._socket.on("error", this._proxy.onError);
this._socket.on("message", this._proxy.onMessage);
}
},
"send": function(ev) {
var size = ev.size();
var ba = new ByteArray(size + 5);
ba.push32(size);
ba.push8(ev.getType());
ev.serialize(ba);
this._socket.send(ba.data().buffer);
},
"_setId": function(id) {
if (this._state === CONNECTING) {
this._id.destructor();
this._id = id.clone();
} else {
throw new Error("An attempt to set id in unexpected time");
}
},
"_setName": function(name) {
if ((this._state === CONNECTING) && (this._id.valueOf() !== 0)) {
this._remoteName.destructor();
this._remoteName = name.clone();
} else {
throw new Error("An attempt to set name in unexpected time");
}
},
"_setRemoteName": function() {
var vc = new Vocabulary();
vc.insert("command", new String("setName"));
vc.insert("name", this._name.clone());
vc.insert("yourName", this._remoteName.clone());
var ev = new Event(new Address(), vc, true);
ev.setSenderId(this._id.clone());
this.send(ev);
ev.destructor();
},
"_setRemoteId": function() {
if (this._state === DISCONNECTED) {
this._state = CONNECTING;
}
var vc = new Vocabulary();
vc.insert("command", new String("setId"));
vc.insert("id", this._id.clone());
var ev = new Event(new Address(), vc, true);
ev.setSenderId(this._id.clone());
this.send(ev);
ev.destructor();
}
});
var DISCONNECTED = 111;
var DISCONNECTING = 110;
var CONNECTING = 101;
var CONNECTED = 100;
var SIZE = 1
var BODY = 10;
module.exports = Socket;