initial commit
This commit is contained in:
commit
4b60ece582
327 changed files with 28286 additions and 0 deletions
4
magnus/lib/wSocket/CMakeLists.txt
Normal file
4
magnus/lib/wSocket/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
configure_file(socket.js socket.js)
|
||||
configure_file(server.js server.js)
|
154
magnus/lib/wSocket/server.js
Normal file
154
magnus/lib/wSocket/server.js
Normal file
|
@ -0,0 +1,154 @@
|
|||
"use strict";
|
||||
|
||||
var WebSocketServer = require("ws").Server;
|
||||
var Socket = require("./socket");
|
||||
var Subscribable = require("../utils/subscribable");
|
||||
var AbstractMap = require("../wContainer/abstractmap");
|
||||
var AbstractSet = require("../wContainer/abstractset");
|
||||
var String = require("../wType/string");
|
||||
var Uint64 = require("../wType/uint64");
|
||||
|
||||
var Server = Subscribable.inherit({
|
||||
"className": "Server",
|
||||
"constructor": function(name) {
|
||||
if (!name) {
|
||||
throw new Error("Can't construct a socket without a name");
|
||||
}
|
||||
Subscribable.fn.constructor.call(this);
|
||||
|
||||
this._lastId = new Uint64(0);
|
||||
this._pool = new Server.Uint64Set(true);
|
||||
this._name = name instanceof String ? name : new String(name);
|
||||
this._server = undefined;
|
||||
this._connections = new Server.ConnectionsMap(true);
|
||||
this._listening = false;
|
||||
|
||||
this._initProxy();
|
||||
},
|
||||
"destructor": function() {
|
||||
if (this._listening) {
|
||||
this._server.stop();
|
||||
delete this._server;
|
||||
}
|
||||
this._lastId.destructor();
|
||||
this._pool.destructor();
|
||||
this._name.destructor();
|
||||
this._connections.destructor();
|
||||
|
||||
Subscribable.fn.destructor.call(this);
|
||||
},
|
||||
"getName": function() {
|
||||
return this._name;
|
||||
},
|
||||
"listen": function(port) {
|
||||
if (!this._listening) {
|
||||
this._listening = true;
|
||||
this._server = new WebSocketServer({port: port}, this._proxy.onReady);
|
||||
this._server.on("connection", this._proxy.onConnection);
|
||||
}
|
||||
},
|
||||
"stop": function() {
|
||||
if (this._listening) {
|
||||
this._listening = false;
|
||||
this._server.stop();
|
||||
this._lastId = new Uint64(0);
|
||||
this._connections.clear();
|
||||
this._pool.clear();
|
||||
delete this._server;
|
||||
}
|
||||
},
|
||||
"getConnection": function(id) {
|
||||
var itr = this._connections.find(id);
|
||||
if (itr["=="](this._connections.end())) {
|
||||
throw new Error("Connection not found");
|
||||
}
|
||||
return itr["*"]().second;
|
||||
},
|
||||
"getConnectionsCount": function() {
|
||||
return this._connections.size();
|
||||
},
|
||||
"openConnection": function(addr, port) {
|
||||
var webSocket = new Subscribable();
|
||||
var wSocket = this._createSocket(webSocket);
|
||||
wSocket._socket.destructor();
|
||||
wSocket.open(addr, port);
|
||||
},
|
||||
"closeConnection": function(id) {
|
||||
var itr = this._connections.find(id);
|
||||
if (itr["=="](this._connections.end())) {
|
||||
throw new Error("Connection not found");
|
||||
}
|
||||
itr["*"]().second.close();
|
||||
},
|
||||
"_createSocket": function(socket) {
|
||||
var connectionId;
|
||||
if (this._pool.size() === 0) {
|
||||
this._lastId["++"]()
|
||||
connectionId = this._lastId.clone();
|
||||
} else {
|
||||
var itr = this._pool.begin();
|
||||
connectionId = itr["*"]().clone();
|
||||
this._pool.erase(itr);
|
||||
}
|
||||
var wSocket = new Socket(this._name, socket, connectionId);
|
||||
this._connections.insert(connectionId, wSocket);
|
||||
|
||||
wSocket.on("connected", this._onSocketConnected.bind(this, wSocket));
|
||||
wSocket.on("disconnected", this._onSocketDisconnected.bind(this, wSocket));
|
||||
wSocket.on("negotiationId", this._onSocketNegotiationId.bind(this, wSocket));
|
||||
|
||||
return wSocket;
|
||||
},
|
||||
"_initProxy": function() {
|
||||
this._proxy = {
|
||||
onConnection: this._onConnection.bind(this),
|
||||
onReady: this._onReady.bind(this)
|
||||
};
|
||||
},
|
||||
"_onConnection": function(socket) {
|
||||
var wSocket = this._createSocket(socket);
|
||||
wSocket._setRemoteId();
|
||||
},
|
||||
"_onReady": function() {
|
||||
this.trigger("ready");
|
||||
},
|
||||
"_onSocketConnected": function(socket) {
|
||||
this.trigger("newConnection", socket);
|
||||
this.trigger("connectionCountChange", this._connections.size());
|
||||
},
|
||||
"_onSocketDisconnected": function(socket) {
|
||||
var cItr = this._connections.find(socket.getId());
|
||||
this._pool.insert(socket.getId().clone());
|
||||
this.trigger("closedConnection", socket);
|
||||
this.trigger("connectionCountChange", this._connections.size());
|
||||
setTimeout(this._connections.erase.bind(this._connections, cItr), 1);
|
||||
},
|
||||
"_onSocketNegotiationId": function(socket, id) {
|
||||
var oldId = socket.getId();
|
||||
if (id["=="](oldId)) {
|
||||
socket._setRemoteName();
|
||||
} else {
|
||||
var pItr = this._pool.lowerBound(id);
|
||||
var newId;
|
||||
if (pItr["=="](this._pool.end())) {
|
||||
this._lastId["++"]();
|
||||
newId = this._lastId.clone();
|
||||
} else {
|
||||
newId = pItr["*"]().clone();
|
||||
this._pool.erase(pItr);
|
||||
}
|
||||
var itr = this._connections.find(oldId);
|
||||
itr["*"]().second = undefined; //to prevent autodestruction of the socket;
|
||||
this._connections.erase(itr);
|
||||
this._pool.insert(oldId);
|
||||
socket._id = newId;
|
||||
this._connections.insert(newId.clone(), socket);
|
||||
socket._setRemoteId();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Server.ConnectionsMap = AbstractMap.template(Uint64, Socket);
|
||||
Server.Uint64Set = AbstractSet.template(Uint64);
|
||||
|
||||
module.exports = Server;
|
217
magnus/lib/wSocket/socket.js
Normal file
217
magnus/lib/wSocket/socket.js
Normal file
|
@ -0,0 +1,217 @@
|
|||
"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;
|
Loading…
Add table
Add a link
Reference in a new issue