treat devices more like devices and less like domains

This commit is contained in:
AJ ONeal 2018-08-20 19:45:13 +00:00
parent bb018c538d
commit 5380a519bd
3 changed files with 89 additions and 11 deletions

View File

@ -10,7 +10,7 @@
, "immed": true , "immed": true
, "undef": true , "undef": true
, "unused": true , "unused": true
, "latedef": true , "latedef": "nofunc"
, "curly": true , "curly": true
, "trailing": true , "trailing": true
} }

View File

@ -2,42 +2,115 @@
var Devices = module.exports; var Devices = module.exports;
Devices.add = function (store, servername, newDevice) { 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); 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) { 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); var index = devices.indexOf(device);
if (index < 0) { if (index < 0) {
console.warn('attempted to remove non-present device', device.deviceId, 'from', servername); console.warn('attempted to remove non-present device', device.deviceId, 'from', servername);
return null; 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]; 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) { Devices.list = function (store, servername) {
if (store[servername] && store[servername].length) { // efficient lookup first
return 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 // 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. // first so the one with the biggest natural match with be found first.
var deviceList = []; var deviceList = [];
Object.keys(store).filter(function (pattern) { Object.keys(store._domains).filter(function (pattern) {
return pattern[0] === '*' && store[pattern].length; return pattern[0] === '*' && store._domains[pattern].length;
}).sort(function (a, b) { }).sort(function (a, b) {
return b.length - a.length; return b.length - a.length;
}).some(function (pattern) { }).some(function (pattern) {
var subPiece = pattern.slice(1); var subPiece = pattern.slice(1);
if (subPiece === servername.slice(-subPiece.length)) { if (subPiece === servername.slice(-subPiece.length)) {
console.log('"'+servername+'" matches "'+pattern+'"'); 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'
// 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 true;
} }
}); });
return deviceList; return deviceList;
}; };
/*
Devices.active = function (store, id) {
var dev = store._devices[id];
return !!dev;
};
*/
Devices.exist = function (store, servername) { Devices.exist = function (store, servername) {
return !!(Devices.list(store, servername).length); return !!(Devices.list(store, servername).length);
}; };

View File

@ -211,10 +211,13 @@ var Server = {
function hangup() { function hangup() {
clearTimeout(srv.timeoutId); clearTimeout(srv.timeoutId);
console.log('[ws] device hangup', Server.logName(state, srv), 'connection closing'); 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) { Object.keys(srv.grants).forEach(function (jwtoken) {
Server.removeToken(state, srv, jwtoken); Server.removeToken(state, srv, jwtoken);
}); });
srv.ws.terminate(); srv.ws.terminate();
// remove the socket from the list, period
Devices.close(state.deviceLists, srv);
} }
srv.lastActivity = Date.now(); srv.lastActivity = Date.now();
@ -550,7 +553,7 @@ var Server = {
module.exports.store = { Devices: Devices }; module.exports.store = { Devices: Devices };
module.exports.create = function (state) { module.exports.create = function (state) {
state.deviceLists = {}; state.deviceLists = { _domains: {}, _devices: {} };
state.deviceCallbacks = {}; state.deviceCallbacks = {};
state.srvs = {}; state.srvs = {};
@ -577,6 +580,8 @@ module.exports.create = function (state) {
var initToken; var initToken;
srv.ws = _ws; srv.ws = _ws;
srv.upgradeReq = _upgradeReq; srv.upgradeReq = _upgradeReq;
// TODO use device's ECDSA thumbprint as device id
srv.id = null;
srv.socketId = Packer.socketToId(srv.upgradeReq.socket); srv.socketId = Packer.socketToId(srv.upgradeReq.socket);
srv.grants = {}; srv.grants = {};
srv.clients = {}; srv.clients = {};