From e492ae974603ccf0191f0bfec18b65895c2f1f8d Mon Sep 17 00:00:00 2001
From: Nicolas Pope <nwpope@utu.fi>
Date: Wed, 3 Apr 2019 08:32:59 +0300
Subject: [PATCH] Initial javascript net and web-service functions

---
 net/js/package.json        |  18 +++++
 net/js/src/index.js        |   0
 net/js/src/socket.js       | 160 +++++++++++++++++++++++++++++++++++++
 net/js/test/socket_unit.js |  44 ++++++++++
 web-service/package.json   |  19 +++++
 web-service/src/index.js   |  25 ++++++
 6 files changed, 266 insertions(+)
 create mode 100644 net/js/package.json
 create mode 100644 net/js/src/index.js
 create mode 100644 net/js/src/socket.js
 create mode 100644 net/js/test/socket_unit.js
 create mode 100644 web-service/package.json
 create mode 100644 web-service/src/index.js

diff --git a/net/js/package.json b/net/js/package.json
new file mode 100644
index 000000000..1bc11f2cc
--- /dev/null
+++ b/net/js/package.json
@@ -0,0 +1,18 @@
+{
+  "name": "@ftl/net",
+  "version": "0.0.1",
+  "description": "P2P Network protocol for FT-Lab",
+  "main": "src/index.js",
+  "scripts": {
+    "test": "mocha test"
+  },
+  "author": "Nicolas Pope",
+  "license": "ISC",
+  "dependencies": {
+    "struct": "0.0.12",
+    "uri-js": "^4.2.2"
+  },
+  "devDependencies": {
+    "mocha": "^6.0.2"
+  }
+}
diff --git a/net/js/src/index.js b/net/js/src/index.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/net/js/src/socket.js b/net/js/src/socket.js
new file mode 100644
index 000000000..25e7050cc
--- /dev/null
+++ b/net/js/src/socket.js
@@ -0,0 +1,160 @@
+const net = require('net');
+const ws = require('ws');
+const urijs = require('uri-js');
+
+function Socket(uri) {
+	let t = typeof uri;
+	
+	this.handlers_ = {
+		'open': [],
+		'data': [],
+		'error': [],
+		'close': []
+	};
+	
+	this.buffer_ = new Buffer(0);
+
+	if (t == "string") {
+		this._fromURI(uri);
+	} else if (t == "object") {
+		this._fromObject(uri);
+	}
+	
+	// Attach handler depending on socket type
+	if (this.scheme_ = "websocket") {
+		if (this.socket_.addEventHandler) {
+			this.socket_.addEventHandler('message', event => {
+				dataHandler(event.data);
+			});	
+		} else {
+			this.socket_.on('message', dataHandler);
+		}
+	} else {
+		this.socket_.on('data', dataHandler);
+	}
+}
+
+Socket.prototype._fromURI = function(uri) {
+	let uriobj = urijs.parse(uri);
+	
+	this.uri_ = uri;
+	this.scheme_ = uriobj.scheme;
+	
+	if (this.scheme_ == "ws") {
+		if (typeof WebSocket == "undefined") {
+			this.socket_ = new ws(uri);
+		} else {
+			this.socket_ = new WebSocket(uri);
+		}
+		this._initWebsocket();
+	} else if (this.scheme_ == "tcp") {
+		this.socket_ = new net.Socket(uriobj.port, uriobj.host);
+		this._initTCPSocket();
+	} else {
+		console.error("Invalid URI scheme for socket: ", this.scheme_);
+	}
+}
+
+Socket.prototype._fromObject = function(sock) {
+	this.socket_ = sock;
+	
+	if (typeof WebSocket == "undefined") {
+		if (sock instanceof ws) this.scheme_ = "ws";
+		else if (sock instanceof net.Socket) this.scheme_ = "tcp";
+		else this.scheme_ = null;
+	} else {
+		if (sock instanceof WebSocket) this.scheme_ = "ws";
+		else this.scheme_ = null;
+	}
+	
+	if (this.scheme_ == "ws") this._initWebsocket();
+	else if (this.scheme_ == "tcp") this._initTCPSocket();
+}
+
+Socket.prototype._initWebsocket = function() {
+	let dataHandler = (data) => {
+		let size = binary.readUInt32LE(data, 0);
+		let service = binary.readUInt32LE(data, 4);
+		
+		console.log("Message", service);
+		if (this.handlers_.hasOwnProperty(service)) {
+			this.handlers_[service](binary.subarray(data, 8));
+		} else {
+			console.error("No handler for service "+service);
+		}
+	};
+
+	if (this.socket_.addEventHandler) {
+		this.socket_.addEventHandler('message', event => {
+			dataHandler(event.data);
+		});	
+	} else {
+		this.socket_.on('message', dataHandler);
+	}
+}
+
+Socket.prototype._initTCPSocket = function() {
+	let dataHandler = (data) => {
+		/*console.log('Received: ' + data);
+		this.buffer_ = Buffer.concat([this.buffer_, data]);
+		
+		if (this.buffer_.length >= 8) {
+			this.header_._setBuff(this.buffer_);
+			let size = this.header_.get('size');
+			
+			console.log("Message: " + this.header_.get('service'));
+			
+			// Do we have a complete message yet?
+			if (this.buffer_.length-4 >= size) {
+				// Yes, so dispatch
+				console.log("Complete message found");
+			} else {
+				console.log("Incomplete message");
+			}
+		}*/
+	};
+	this.socket_.on('data', dataHandler);
+}
+
+Socket.prototype.isConnected = function() {
+	return this.connected_;
+}
+
+Socket.prototype.on = function(name, f) {
+	if (typeof name == "string") {
+		if (this.handlers.hasOwnProperty(name)) {
+			this.handlers_[name].push(f);
+		} else {
+			console.error("Unrecognised handler: ", name);
+		}
+	} else if (typeof name == "number") {
+		if (this.handlers_[name] === undefined) this.handlers_[name] = [];
+		this.handlers_[name].push(f);
+	} else {
+		console.error("Invalid handler: ", name);
+	}
+}
+
+Socket.prototype.close = function() {
+	this.socket_.destroy();
+	this.socket_ = null;
+}
+
+Socket.prototype._socket = function() {
+	return this.socket_;
+}
+
+Socket.prototype.getURI = function() {
+	return this.uri_;
+}
+
+Socket.prototype.asyncCall = function(name, cb /*, ...*/) {
+
+}
+
+Socket.prototype.send = function(id /*, ...*/) {
+	//this.socket_.write(
+}
+
+module.exports = Socket;
+
diff --git a/net/js/test/socket_unit.js b/net/js/test/socket_unit.js
new file mode 100644
index 000000000..21a68624b
--- /dev/null
+++ b/net/js/test/socket_unit.js
@@ -0,0 +1,44 @@
+const Socket = require('../src/socket.js');
+const assert = require('assert');
+const net = require('net');
+
+describe("Constructing a socket", function() {
+	
+	let server = net.createServer(socket => {
+		console.log("Client connected");
+	});
+	server.listen(9000, 'localhost');
+	
+	it("Connects to a valid tcp uri", function(done) {
+		let sock = new Socket("tcp://localhost:9000");
+		sock.on('connect', () => {
+			assert.equal(sock.isConnected(),true);
+			sock.close();
+			done();
+		});
+	});
+	
+	server.close(() => { console.log("Closed"); });
+});
+
+describe("Receiving messages on a socket", function() {
+	
+	let server = net.createServer(socket => {
+		console.log("Client connected");
+		server.write(Buffer.from('helloworld'));
+	});
+	server.listen(9000, 'localhost');
+	
+	it("Connects to a valid tcp uri", function(done) {
+		let sock = new Socket("tcp://localhost:9000");
+		sock.on(44, (data) => {
+			// TODO Parse the data...
+			console.log("Received data....");
+			done();
+			sock.close();
+		});
+	});
+	
+	server.close(() => { console.log("Closed"); });
+});
+
diff --git a/web-service/package.json b/web-service/package.json
new file mode 100644
index 000000000..ea4d7e8fc
--- /dev/null
+++ b/web-service/package.json
@@ -0,0 +1,19 @@
+{
+  "name": "@ftl/web-service",
+  "version": "0.0.1",
+  "description": "Web-service backend for FT-Lab systems",
+  "main": "src/index.js",
+  "directories": {
+    "test": "test"
+  },
+  "scripts": {
+    "start": "node src/index.js",
+    "test": "mocha test"
+  },
+  "author": "Nicolas Pope",
+  "license": "ISC",
+  "dependencies": {
+    "express": "^4.16.4",
+    "express-ws": "^4.0.0"
+  }
+}
diff --git a/web-service/src/index.js b/web-service/src/index.js
new file mode 100644
index 000000000..42d7cfefb
--- /dev/null
+++ b/web-service/src/index.js
@@ -0,0 +1,25 @@
+const express = require('express');
+const app = express();
+const expressWs = require('express-ws')(app);
+
+app.get('/', (req, res) => {
+	res.end();
+});
+
+app.ws('/', (ws, req) => {
+	console.log("New web socket request");
+	// SEND Handshake
+	ws.on('message', (msg) => {
+		console.log("Message", msg);
+	});
+	ws.on('error', () => {
+		console.log("Error");
+	});
+	ws.on('close', () => {
+		console.log("Close");
+	});
+});
+
+console.log("Listening or port 3000");
+app.listen(3000);
+
-- 
GitLab