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 f5ed895..e06a382 100644 --- a/lib/device-tracker.js +++ b/lib/device-tracker.js @@ -2,46 +2,91 @@ var Devices = module.exports; Devices.add = function (store, servername, newDevice) { - if (!store[servername]) { - store[servername] = []; + if (!store._devices) { store._devices = {}; } + if (!store._domains) { store._domains = {}; } + if (!store._domains[servername]) { + store._domains[servername] = []; } - var devices = store[servername]; + var devices = store._domains[servername]; devices.push(newDevice); + + // 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[servername]) { - store[servername] = []; + if (!store._domains[servername]) { + store._domains[servername] = []; } - if (!store[servername]._primary) { - store[servername]._primary = servername; + if (!store._domains[servername]._primary) { + store._domains[servername]._primary = servername; } - if (!store[servername].aliases) { - store[servername].aliases = {}; + if (!store._domains[servername].aliases) { + store._domains[servername].aliases = {}; } - store[alias] = store[servername]; - store[servername].aliases[alias] = true; + 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) { // efficient lookup first - if (store[servername] && store[servername].length) { - return store[servername]._primary && store[store[servername]._primary] || store[servername]; + 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) { @@ -51,7 +96,7 @@ Devices.list = function (store, servername) { // '.example.com' = 'sub.example.com'.slice(-12) if (subPiece === servername.slice(-subPiece.length)) { console.log('[Devices.list] "'+servername+'" matches "'+pattern+'"'); - deviceList = store[pattern]; + deviceList = store._domains[pattern]; // Devices.alias(store, '*.example.com', 'sub.example.com' // '*.example.com' retrieves a reference to 'example.com' @@ -64,6 +109,12 @@ Devices.list = function (store, servername) { 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 6bd9d91..d86bda1 100644 --- a/lib/relay.js +++ b/lib/relay.js @@ -216,10 +216,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(); @@ -574,7 +577,7 @@ var Server = { module.exports.store = { Devices: Devices }; module.exports.create = function (state) { - state.deviceLists = {}; + state.deviceLists = { _domains: {}, _devices: {} }; state.deviceCallbacks = {}; state.srvs = {}; @@ -601,6 +604,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 = {};