From 876e7d118497ba22b048dc3a681927a50c423523 Mon Sep 17 00:00:00 2001 From: Nicolas Pope <nwpope@utu.fi> Date: Fri, 5 Apr 2019 09:56:05 +0300 Subject: [PATCH] JS Socket passes initial connection tests --- net/js/package.json | 3 +- net/js/src/index.js | 2 + net/js/src/socket.js | 133 ++++++++++++++++++++++++------------- net/js/test/socket_unit.js | 17 +++-- 4 files changed, 99 insertions(+), 56 deletions(-) diff --git a/net/js/package.json b/net/js/package.json index 52bbcc4bd..b379f3846 100644 --- a/net/js/package.json +++ b/net/js/package.json @@ -1,6 +1,6 @@ { "name": "@ftl/net", - "version": "0.0.1", + "version": "0.0.2", "description": "P2P Network protocol for FT-Lab", "main": "src/index.js", "scripts": { @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "bops": "^1.0.0", + "detect-browser": "^4.2.0", "struct": "0.0.12", "uri-js": "^4.2.2", "ws": "^6.2.1" diff --git a/net/js/src/index.js b/net/js/src/index.js index e69de29bb..bddfe8142 100644 --- a/net/js/src/index.js +++ b/net/js/src/index.js @@ -0,0 +1,2 @@ +export.Socket = require('./socket.js'); + diff --git a/net/js/src/socket.js b/net/js/src/socket.js index 22b70d44c..48c3725a0 100644 --- a/net/js/src/socket.js +++ b/net/js/src/socket.js @@ -2,6 +2,8 @@ const net = require('net'); const ws = require('ws'); const urijs = require('uri-js'); const binary = require('bops'); +const browser = require('detect-browser').detect(); +const isbrowser = !browser || browser.name != "node"; function Socket(uri) { let t = typeof uri; @@ -17,7 +19,11 @@ function Socket(uri) { this.lasterr_ = null; this.connected_ = false; this.valid_ = false; - this.buffer_ = new Buffer(0); + this.uuid_ = binary.create(16); + + if (!isbrowser) { + this.buffer_ = new Buffer(0); // Only in nodejs + } if (t == "string") { this._fromURI(uri); @@ -26,10 +32,17 @@ function Socket(uri) { } } -Socket.ERROR_BADPROTOCOL = 0x01; -Socket.ERROR_BADHOST = 0x02; -Socket.ERROR_BADHANDSHAKE = 0x03; -Socket.ERROR_MALFORMEDURI = 0x04; +Socket.ERROR_BADPROTOCOL = "Bad Protocol"; +Socket.ERROR_BADHOST = "Unknown host"; +Socket.ERROR_BADHANDSHAKE = "Invalid Handshake"; +Socket.ERROR_MALFORMEDURI = "Malformed URI"; +Socket.ERROR_TCPINBROWSER = "TCP invalid in browser"; +Socket.ERROR_LARGEMESSAGE = "Network message too large"; + +Socket.prototype.error = function(errno) { + this.lasterr_ = errno; + this.dispatch('error', [errno]); +} Socket.prototype.isValid = function() { return this.valid_; @@ -45,8 +58,7 @@ Socket.prototype._fromURI = function(uri) { // Could not parse uri so report error if (uriobj.scheme === undefined || uriobj.host === undefined) { - this.lasterr_ = Socket.ERROR_MALFORMEDURI; - this.dispatch('error', [this.lasterr_]); + this.error(Socket.ERROR_MALFORMEDURI); return; } @@ -63,12 +75,15 @@ Socket.prototype._fromURI = function(uri) { this._initWebsocket(); // TCP } else if (this.scheme_ == "tcp") { - this.socket_ = net.connect(uriobj.port, uriobj.host); - this._initTCPSocket(); + if (!isbrowser) { + this.socket_ = net.connect(uriobj.port, uriobj.host); + this._initTCPSocket(); + } else { + this.error(Socket.ERROR_TCPINBROWSER); + } // Unrecognised protocol } else { - this.lasterr_ = Socket.ERROR_BADPROTOCOL; - this.dispatch('error', [this.lasterr_]); + this.error(Socket.ERROR_BADPROTOCOL) } } @@ -95,15 +110,7 @@ Socket.prototype._initWebsocket = function() { this.valid_ = true; 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); - } + this.processMessage(data); }; if (this.socket_.addEventHandler) { @@ -114,8 +121,8 @@ Socket.prototype._initWebsocket = function() { this.socket_.on('message', dataHandler); } this.socket_.on('open', () => { - this.connected_ = true; - this.dispatch('open', []); + //this.connected_ = true; + //this.dispatch('open', []); }); this.socket_.on('error', (err) => { this.connected_ = false; @@ -131,46 +138,78 @@ Socket.prototype._initWebsocket = function() { }); } +function checkMagic(buffer) { + if (buffer.length < 8) return false; + let lo_magic = binary.readUInt32LE(buffer,0); + let hi_magic = binary.readUInt32LE(buffer,4); + return (lo_magic == 0x53640912 && hi_magic == 0x10993400) +} + +Socket.prototype.processMessage = function(buffer) { + if (!this.handshake_) { + // Check handshake + if (!checkMagic(buffer)) { + this.close(); + this.error(Socket.ERROR_BADHANDSHAKE); + return 0; + } + + binary.copy(buffer, this.uuid_, 0, 8, 16); + let proto_size = binary.readUInt32LE(buffer,24); + + this.handshake_ = true; + this.connected_ = true; + this.dispatch('open', []); + + return 28 + proto_size; + } else { + let size = binary.readUInt32LE(buffer,0); + let service = binary.readUInt32LE(buffer,4); + + console.log("Message: " + service + "(size="+size+")"); + + // Do we have a complete message yet? + if (size > 1024*1024*100) { + this.error(Socket.ERROR_LARGEMESSAGE); + this.close(); + return 0; + } else if (buffer.length-4 >= size) { + // Yes, so dispatch + this.dispatch(service, [size, binary.subarray(buffer,8)]); + return size+4; + } else { + return 0; + } + } +} + +/** + * Setup TCP socket handlers and message buffering mechanism. + */ Socket.prototype._initTCPSocket = function() { this.valid_ = true; let dataHandler = (data) => { - console.log('Received: ' + data); this.buffer_ = Buffer.concat([this.buffer_, data]); - if (this.buffer_.length >= 8) { - let size = binary.readUInt32LE(this.buffer_,0); - let service = binary.readUInt32LE(this.buffer_,4); - - console.log("Message: " + service); - - // Do we have a complete message yet? - if (size > 1024*1024*100) { - this.dispatch('error', ["invalid message size"]); - console.log("Message too big"); - } else if (this.buffer_.length-4 >= size) { - // Yes, so dispatch - console.log("Complete message found"); - this.dispatch(service, [size, binary.subarray(this.buffer_,8)]); - } else { - console.log("Incomplete message"); - } + while (this.buffer_.length >= 8) { + let s = this.processMessage(this.buffer_); + if (s == 0) break; + this.buffer_ = binary.subarray(this.buffer_,s); } }; this.socket_.on('data', dataHandler); this.socket_.on('connect', () => { - this.connected_ = true; - this.dispatch('open', []); + //this.connected_ = true; + //this.dispatch('open', []); }); this.socket_.on('error', (err) => { this.connected_ = false; this.valid_ = false; - console.log("Sock error: ", err); switch (err.errno) { - case 'ENOTFOUND' : this.lasterr_ = Socket.ERROR_BADHOST; break; - default : this.lasterr_ = err.errno; + case 'ENOTFOUND' : this.error(Socket.ERROR_BADHOST); break; + default : this.error(err.errno); } - this.dispatch('error', [this.lasterr_]); }); this.socket_.on('close', () => { this.dispatch('close', []); @@ -216,6 +255,8 @@ Socket.prototype.dispatch = function(h, args) { } Socket.prototype.close = function() { + if (this.socket_ == null) return; + if (this.scheme_ == "ws") { this.socket_.close(); } else { diff --git a/net/js/test/socket_unit.js b/net/js/test/socket_unit.js index a1cc4f78c..c5fd8fc7e 100644 --- a/net/js/test/socket_unit.js +++ b/net/js/test/socket_unit.js @@ -16,7 +16,7 @@ describe("Socket()", function() { if (dobadhandshake) { socket.write(Buffer.from([44,55,33,22,23,44,87])); } else { - socket.write(Buffer.from([0x12,0x09,0x64,0x53,0x00,0x34,0x99,0x10,3,0,0,0,3,0,0,0,67,67,67,67,67,67])); + socket.write(Buffer.from([0x12,0x09,0x64,0x53,0x00,0x34,0x99,0x10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,67,67,67])); } }); server.listen(9000, 'localhost'); @@ -27,7 +27,7 @@ describe("Socket()", function() { if (dobadhandshake) { ws.send(Buffer.from([44,55,33,22,23,44,87])); } else { - ws.send(Buffer.from([0x12,0x09,0x64,0x53,0x00,0x34,0x99,0x10,3,0,0,0,3,0,0,0,67,67,67,67,67,67])); + ws.send(Buffer.from([0x12,0x09,0x64,0x53,0x00,0x34,0x99,0x10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,67,67,67])); } }); }); @@ -52,18 +52,16 @@ describe("Socket()", function() { }); }); - context("with a valid uri but bad handshake", () => { + context("with a valid uri but bad handshake", (done) => { it("should reject the connection", () => { - let diderror = false; dobadhandshake = true; let sock = new Socket("tcp://localhost:9000"); sock.on('error', (errno) => { - diderror = true; assert.equal(errno, Socket.ERROR_BADHANDSHAKE); assert.isOk(sock.isValid()); assert.isNotOk(sock.isConnected()); + done(); }); - assert.isOk(diderror); }); }); @@ -101,7 +99,7 @@ describe("Socket()", function() { }); afterEach(() => { - server.close(() => { console.log("Closed"); }); + server.close(); server.unref(); wss.close(); @@ -113,6 +111,8 @@ describe("Receiving messages on a tcp socket", function() { beforeEach(() => { server = net.createServer(socket => { + // Handshake first + socket.write(Buffer.from([0x12,0x09,0x64,0x53,0x00,0x34,0x99,0x10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,67,67,67])); socket.write(Buffer.from([8,0,0,0,44,0,0,0,23,0,0,0])); }); server.listen(9001, 'localhost'); @@ -121,7 +121,6 @@ describe("Receiving messages on a tcp socket", function() { it("receives valid short message", function(done) { let sock = new Socket("tcp://localhost:9001"); sock.on(44, (size, data) => { - // TODO Parse the data... assert.equal(binary.readInt32LE(data,0), 23); console.log("Received data...."); sock.close(); @@ -130,7 +129,7 @@ describe("Receiving messages on a tcp socket", function() { }); afterEach(() => { - server.close(() => { console.log("Closed"); }); + server.close(); server.unref(); }); }); -- GitLab