From 5380a519bdb63ec8bc57196fa93a2069fcddc60c Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 20 Aug 2018 19:45:13 +0000 Subject: [PATCH] treat devices more like devices and less like domains --- .jshintrc | 2 +- lib/device-tracker.js | 91 ++++++++++++++++++++++++++++++++++++++----- lib/relay.js | 7 +++- 3 files changed, 89 insertions(+), 11 deletions(-) diff --git a/.jshintrc b/.jshintrc index 63801ce..2d356f4 100644 --- a/.jshintrc +++ b/.jshintrc @@ -10,7 +10,7 @@ , "immed": true , "undef": true , "unused": true -, "latedef": true +, "latedef": "nofunc" , "curly": true , "trailing": true } diff --git a/lib/device-tracker.js b/lib/device-tracker.js index 7194cdf..17fc499 100644 --- a/lib/device-tracker.js +++ b/lib/device-tracker.js @@ -2,42 +2,115 @@ var Devices = module.exports; Devices.add = function (store, servername, newDevice) { - var devices = store[servername] || []; + if (!store._devices) { store._devices = {}; } + if (!store._domains) { store._domains = {}; } + if (!store._domains[servername]) { + store._domains[servername] = []; + } + var devices = store._domains[servername]; devices.push(newDevice); - store[servername] = devices; + + // TODO only use a device id + var devId = newDevice.id || servername; + if (!store._devices[devId]) { + store._devices[devId] = newDevice; + if (!store._devices[devId].domains) { + store._devices[devId].domains = {}; + } + if (!store._devices[devId].domains[servername]) { + store._devices[devId].domains[servername] = true; + } + } +}; +Devices.alias = function (store, servername, alias) { + if (!store._domains[servername]) { + store._domains[servername] = []; + } + if (!store._domains[servername]._primary) { + store._domains[servername]._primary = servername; + } + if (!store._domains[servername].aliases) { + store._domains[servername].aliases = {}; + } + store._domains[alias] = store._domains[servername]; + store._domains[servername].aliases[alias] = true; }; Devices.remove = function (store, servername, device) { - var devices = store[servername] || []; + // Check if this domain has an active device + var devices = store._domains[servername] || []; var index = devices.indexOf(device); if (index < 0) { console.warn('attempted to remove non-present device', device.deviceId, 'from', servername); return null; } + + // unlink this domain from this device + var domains = store._devices[devices[index].id || servername].domains; + delete domains[servername]; + /* + // remove device if no domains remain + // nevermind, a device can hang around in limbo for a bit + if (!Object.keys(domains).length) { + delete store._devices[devices[index].id || servername]; + } + */ + + // unlink this device from this domain return devices.splice(index, 1)[0]; }; +Devices.close = function (store, device) { + var dev = store._devices[device.id]; + var id = device.id; + // because we're actually using names rather than don't have reliable deviceIds yet + if (!dev) { + Object.keys(store._devices[device.id]).some(function (key) { + if (store._devices[key].socketId === device.socketId) { + id = key; + delete store._devices[key]; + return true; + } + }); + } + + // TODO double check that all domains are removed + if (id) { delete store._devices[id]; } +}; Devices.list = function (store, servername) { - if (store[servername] && store[servername].length) { - return store[servername]; + // efficient lookup first + if (store._domains[servername] && store._domains[servername].length) { + return store._domains[servername]._primary && store._domains[store._domains[servername]._primary] || store._domains[servername]; } // There wasn't an exact match so check any of the wildcard domains, sorted longest // first so the one with the biggest natural match with be found first. var deviceList = []; - Object.keys(store).filter(function (pattern) { - return pattern[0] === '*' && store[pattern].length; + Object.keys(store._domains).filter(function (pattern) { + return pattern[0] === '*' && store._domains[pattern].length; }).sort(function (a, b) { return b.length - a.length; }).some(function (pattern) { var subPiece = pattern.slice(1); if (subPiece === servername.slice(-subPiece.length)) { - console.log('"'+servername+'" matches "'+pattern+'"'); - deviceList = store[pattern]; + console.log('[Devices.list] "'+servername+'" matches "'+pattern+'"'); + deviceList = store._domains[pattern]; + + // Devices.alias(store, '*.example.com', 'sub.example.com' + // '*.example.com' retrieves a reference to 'example.com' + // and this reference then also referenced by 'sub.example.com' + // Hence this O(n) check is replaced with the O(1) check above + Devices.alias(store, pattern, servername); return true; } }); return deviceList; }; +/* +Devices.active = function (store, id) { + var dev = store._devices[id]; + return !!dev; +}; +*/ Devices.exist = function (store, servername) { return !!(Devices.list(store, servername).length); }; diff --git a/lib/relay.js b/lib/relay.js index 9bb810b..17dae1c 100644 --- a/lib/relay.js +++ b/lib/relay.js @@ -211,10 +211,13 @@ var Server = { function hangup() { clearTimeout(srv.timeoutId); console.log('[ws] device hangup', Server.logName(state, srv), 'connection closing'); + // remove the allowed domains from the list (but leave the socket) Object.keys(srv.grants).forEach(function (jwtoken) { Server.removeToken(state, srv, jwtoken); }); srv.ws.terminate(); + // remove the socket from the list, period + Devices.close(state.deviceLists, srv); } srv.lastActivity = Date.now(); @@ -550,7 +553,7 @@ var Server = { module.exports.store = { Devices: Devices }; module.exports.create = function (state) { - state.deviceLists = {}; + state.deviceLists = { _domains: {}, _devices: {} }; state.deviceCallbacks = {}; state.srvs = {}; @@ -577,6 +580,8 @@ module.exports.create = function (state) { var initToken; srv.ws = _ws; srv.upgradeReq = _upgradeReq; + // TODO use device's ECDSA thumbprint as device id + srv.id = null; srv.socketId = Packer.socketToId(srv.upgradeReq.socket); srv.grants = {}; srv.clients = {};