domotz-remote-pawn
Version:
Domotz Agent
162 lines (144 loc) • 5.65 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2016 Domotz Ltd
*
* Domotz Agent is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Domotz Agent is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
*
* Created by Andrea Azzarà <a.azzara@domotz.com> on 04/07/18.
*/
function httpOutCache(resourceLocator) {
var defaultExpiration = resourceLocator.settingsManager
? resourceLocator.settingsManager.getSetting('configuration.settings.http_out_cache_default_expiration', 168)
: 168; //7 days
var maxCacheEntries = resourceLocator.settingsManager
? resourceLocator.settingsManager.getSetting('configuration.settings.http_out_cache_max_entries', 30000)
: 30000;
var stringify = resourceLocator.stringify;
var crypto = resourceLocator.crypto;
var _ = resourceLocator.lodash;
var os = resourceLocator.os;
var outboundCache = {};
var fileUtils = resourceLocator.utils.file;
var path = resourceLocator.path;
var fsCachePath = path.join(os.tmpdir(), 'domotz_http_cache.json');
var changed = false;
var myConsole = resourceLocator.log.decorateLogs();
var lazyLoaded = false;
function _getKey(method, resource) {
return method + '+' + resource;
}
function _createHash(body) {
// JSON normalization so that it is deterministic in sending to engine and the md5 has reason to exists
var normalisedStrPayload = stringify(body);
return crypto.createHash('md5').update(normalisedStrPayload).digest('hex');
}
/**
* @param resource (http resource)
* @param method (http method)
* @param body
* @param expiration (unix timestamp with ms resolution e.g., new Date().getTime()).
* '0' or 'null' mean no expiration.
*/
function storeCallHash(resource, method, body, expiration) {
if (Object.keys(outboundCache).length > maxCacheEntries) {
myConsole.warn('Cache is full');
return;
}
if (expiration === null || expiration === 0) {
expiration = null;
} else if (typeof expiration === 'undefined') {
expiration = expirationInNHours(defaultExpiration);
}
var key = _getKey(method, resource);
myConsole.debug(
'Storing entry for %s with expiration %s hours',
key,
expiration ? Math.round((expiration - new Date().getTime()) / (1000 * 3600)) : '-'
);
outboundCache[key] = {
hash: _createHash(body),
expiration: expiration,
};
changed = true;
}
function checkCallHash(resource, method, body) {
if (!lazyLoaded) {
loadCache();
}
var hash = _createHash(body);
var key = _getKey(method, resource);
if (key in outboundCache && hash === outboundCache[key].hash) {
if (outboundCache[key].expiration) {
var now = new Date().getTime();
if (outboundCache[key].expiration < now) {
myConsole.debug('Deleting expired entry for %s with expiration %s', key);
delete outboundCache[key];
return false;
}
}
myConsole.debug('Cache hit for %s', key);
return true;
}
myConsole.debug('Cache miss for %s', key);
return false;
}
function getTable() {
myConsole.verbose('Cache content %s', JSON.stringify(outboundCache));
return outboundCache;
}
function resetCache() {
outboundCache = {};
fileUtils.deleteFile(fsCachePath);
}
function cleanup() {
var now = new Date().getTime();
_.forOwn(outboundCache, function (value, key) {
if (value.expiration && value.expiration < now) {
myConsole.debug('Removing expired key from cache %s', key);
delete outboundCache[key];
}
});
}
function expirationInNHours(n) {
return new Date().getTime() + n * 3600 * 1000;
}
function loadCache() {
myConsole.info('reading table from fs');
var content = fileUtils.readFile(fsCachePath, JSON.parse);
lazyLoaded = true;
if (!content) {
return;
}
outboundCache = content;
cleanup();
myConsole.info('Loaded %s entries from cache ', Object.keys(outboundCache).length);
myConsole.verbose('Cache content: %s', JSON.stringify(outboundCache));
}
function storeCache() {
if (Object.keys(outboundCache).length > 0 && changed) {
myConsole.info('Storing cache to fs');
fileUtils.writeFileSafe(fsCachePath, JSON.stringify(outboundCache));
}
}
return {
checkCallHash: checkCallHash,
storeCallHash: storeCallHash,
getTable: getTable,
resetCache: resetCache,
cleanup: cleanup,
expirationInNHours: expirationInNHours,
loadCache: loadCache,
storeCache: storeCache,
};
}
module.exports.httpOutCache = httpOutCache;