kopia lustrzana https://gitlab.com/rysiekpl/libresilient
improved config.json error handling, including additional tests (ref. #48)
rodzic
28c79a35e4
commit
b50b76d0d5
Plik diff jest za duży
Load Diff
|
@ -195,6 +195,11 @@ self.guessMimeType = async function(ext, content) {
|
|||
* cdata - config data to verify
|
||||
*/
|
||||
let verifyConfigData = (cdata) => {
|
||||
// cdata needs to be an object
|
||||
if ( typeof cdata !== "object" || cdata === null ) {
|
||||
self.log('service-worker', 'fetched config does not contain a valid JSON object')
|
||||
return false;
|
||||
}
|
||||
// basic check for the plugins field
|
||||
if ( !("plugins" in cdata) || ! Array.isArray(cdata.plugins) ) {
|
||||
self.log('service-worker', 'fetched config does not contain a valid "plugins" field')
|
||||
|
@ -283,140 +288,126 @@ let executeConfig = (config) => {
|
|||
// working on a copy of the plugins config so that config.plugins remains unmodified
|
||||
// in case we need it later (for example, when re-loading the config)
|
||||
let pluginsConfig = [...config.plugins]
|
||||
|
||||
// this is the stash for plugins that need dependencies instantiated first
|
||||
let dependentPlugins = new Array()
|
||||
|
||||
// we want to catch any and all possible errors here
|
||||
try {
|
||||
// only now load the plugins (config.json could have changed the defaults)
|
||||
while (pluginsConfig.length > 0) {
|
||||
|
||||
// this is the stash for plugins that need dependencies instantiated first
|
||||
let dependentPlugins = new Array()
|
||||
// get the first plugin config from the array
|
||||
let pluginConfig = pluginsConfig.shift()
|
||||
self.log('service-worker', `handling plugin type: ${pluginConfig.name}`)
|
||||
|
||||
// only now load the plugins (config.json could have changed the defaults)
|
||||
while (pluginsConfig.length > 0) {
|
||||
// load the relevant plugin script (if not yet loaded)
|
||||
if (!LibResilientPluginConstructors.has(pluginConfig.name)) {
|
||||
self.log('service-worker', `${pluginConfig.name}: loading plugin's source`)
|
||||
self.importScripts(`./plugins/${pluginConfig.name}/index.js`)
|
||||
}
|
||||
|
||||
// do we have any dependencies we should handle first?
|
||||
if (typeof pluginConfig.uses !== "undefined") {
|
||||
self.log('service-worker', `${pluginConfig.name}: ${pluginConfig.uses.length} dependencies found`)
|
||||
|
||||
// get the first plugin config from the array
|
||||
let pluginConfig = pluginsConfig.shift()
|
||||
self.log('service-worker', `handling plugin type: ${pluginConfig.name}`)
|
||||
|
||||
// load the relevant plugin script (if not yet loaded)
|
||||
if (!LibResilientPluginConstructors.has(pluginConfig.name)) {
|
||||
self.log('service-worker', `${pluginConfig.name}: loading plugin's source`)
|
||||
self.importScripts(`./plugins/${pluginConfig.name}/index.js`)
|
||||
// move the dependency plugin configs to LibResilientConfig to be worked on next
|
||||
for (let i=(pluginConfig.uses.length); i--; i>=0) {
|
||||
self.log('service-worker', `${pluginConfig.name}: dependency found: ${pluginConfig.uses[i].name}`)
|
||||
// put the plugin config in front of the plugin configs array
|
||||
pluginsConfig.unshift(pluginConfig.uses[i])
|
||||
// set each dependency plugin config to false so that we can keep track
|
||||
// as we fill those gaps later with instantiated dependency plugins
|
||||
pluginConfig.uses[i] = false
|
||||
}
|
||||
|
||||
// do we have any dependencies we should handle first?
|
||||
if (typeof pluginConfig.uses !== "undefined") {
|
||||
self.log('service-worker', `${pluginConfig.name}: ${pluginConfig.uses.length} dependencies found`)
|
||||
|
||||
// move the dependency plugin configs to LibResilientConfig to be worked on next
|
||||
for (let i=(pluginConfig.uses.length); i--; i>=0) {
|
||||
self.log('service-worker', `${pluginConfig.name}: dependency found: ${pluginConfig.uses[i].name}`)
|
||||
// put the plugin config in front of the plugin configs array
|
||||
pluginsConfig.unshift(pluginConfig.uses[i])
|
||||
// set each dependency plugin config to false so that we can keep track
|
||||
// as we fill those gaps later with instantiated dependency plugins
|
||||
pluginConfig.uses[i] = false
|
||||
// stash the plugin config until we have all the dependencies handled
|
||||
self.log('service-worker', `${pluginConfig.name}: not instantiating until dependencies are ready`)
|
||||
dependentPlugins.push(pluginConfig)
|
||||
|
||||
// move on to the next plugin config, which at this point will be
|
||||
// the first of dependencies for the plugin whose config got stashed
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
// if the plugin is not enabled, no instantiation for it nor for its dependencies
|
||||
// if the pluginConfig does not have an "enabled" field, it should be assumed to be "true"
|
||||
if ( ( "enabled" in pluginConfig ) && ( pluginConfig.enabled != true ) ) {
|
||||
self.log('service-worker', `skipping ${pluginConfig.name} instantiation: plugin not enabled (dependencies will also not be instantiated)`)
|
||||
pluginConfig = dependentPlugins.pop()
|
||||
if (pluginConfig !== undefined) {
|
||||
let didx = pluginConfig.uses.indexOf(false)
|
||||
pluginConfig.uses.splice(didx, 1)
|
||||
}
|
||||
|
||||
// stash the plugin config until we have all the dependencies handled
|
||||
self.log('service-worker', `${pluginConfig.name}: not instantiating until dependencies are ready`)
|
||||
dependentPlugins.push(pluginConfig)
|
||||
|
||||
// move on to the next plugin config, which at this point will be
|
||||
// the first of dependencies for the plugin whose config got stashed
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
// if the plugin is not enabled, no instantiation for it nor for its dependencies
|
||||
// if the pluginConfig does not have an "enabled" field, it should be assumed to be "true"
|
||||
if ( ( "enabled" in pluginConfig ) && ( pluginConfig.enabled != true ) ) {
|
||||
self.log('service-worker', `skipping ${pluginConfig.name} instantiation: plugin not enabled (dependencies will also not be instantiated)`)
|
||||
pluginConfig = dependentPlugins.pop()
|
||||
if (pluginConfig !== undefined) {
|
||||
let didx = pluginConfig.uses.indexOf(false)
|
||||
pluginConfig.uses.splice(didx, 1)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// instantiate the plugin
|
||||
let plugin = LibResilientPluginConstructors.get(pluginConfig.name)(self, pluginConfig)
|
||||
self.log('service-worker', `${pluginConfig.name}: instantiated`)
|
||||
|
||||
// do we have a stashed plugin that requires dependencies?
|
||||
if (dependentPlugins.length === 0) {
|
||||
// no we don't; so, this plugin goes directly to the plugin list
|
||||
self.LibResilientPlugins.push(plugin)
|
||||
// we're done here
|
||||
self.log('service-worker', `${pluginConfig.name}: no dependent plugins, pushing directly to LibResilientPlugins`)
|
||||
break;
|
||||
}
|
||||
|
||||
// at this point clearly there is at least one element in dependentPlugins
|
||||
// so we can safely assume that the freshly instantiated plugin is a dependency
|
||||
//
|
||||
// in that case let's find the first empty spot for a dependency
|
||||
let didx = dependentPlugins[dependentPlugins.length - 1].uses.indexOf(false)
|
||||
// assign the freshly instantiated plugin as that dependency
|
||||
dependentPlugins[dependentPlugins.length - 1].uses[didx] = plugin
|
||||
self.log('service-worker', `${pluginConfig.name}: assigning as dependency (#${didx}) to ${dependentPlugins[dependentPlugins.length - 1].name}`)
|
||||
|
||||
// was this the last one?
|
||||
if (didx >= dependentPlugins[dependentPlugins.length - 1].uses.length - 1) {
|
||||
// yup, last one!
|
||||
self.log('service-worker', `${pluginConfig.name}: this was the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
|
||||
// we can now proceed to instantiate the last element of dependentPlugins
|
||||
pluginConfig = dependentPlugins.pop()
|
||||
continue
|
||||
}
|
||||
|
||||
// it is not the last one, so there should be more dependency plugins to instantiate first
|
||||
// before we can instantiate the last of element of dependentPlugins
|
||||
// but that requires the full treatment, including checing the `uses` field for their configs
|
||||
self.log('service-worker', `${pluginConfig.name}: not yet the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
|
||||
pluginConfig = false
|
||||
// instantiate the plugin
|
||||
let plugin = LibResilientPluginConstructors.get(pluginConfig.name)(self, pluginConfig)
|
||||
self.log('service-worker', `${pluginConfig.name}: instantiated`)
|
||||
|
||||
// if pluginConfig is not false, rinse-repeat the plugin instantiation steps
|
||||
// since we are dealing with the last element of dependentPlugins
|
||||
} while ( (pluginConfig !== false) && (pluginConfig !== undefined) )
|
||||
// do we have a stashed plugin that requires dependencies?
|
||||
if (dependentPlugins.length === 0) {
|
||||
// no we don't; so, this plugin goes directly to the plugin list
|
||||
self.LibResilientPlugins.push(plugin)
|
||||
// we're done here
|
||||
self.log('service-worker', `${pluginConfig.name}: no dependent plugins, pushing directly to LibResilientPlugins`)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// finally -- do we want to use MIME type guessing based on content?
|
||||
// dealing with this at the very end so that we know we can safely set detectMimeFromBuffer
|
||||
// and not need to re-set it back in case anything fails
|
||||
if (config.useMimeSniffingLibrary === true) {
|
||||
// we do not want to hit a NetworkError and end up using the default config
|
||||
// much better to end up not using the fancy MIME type detection in such a case
|
||||
try {
|
||||
// we do! load the external lib
|
||||
self.importScripts(`./lib/file-type.js`)
|
||||
} catch (e) {
|
||||
self.log('service-worker', `error when fetching external MIME sniffing library: ${e.message}`)
|
||||
// at this point clearly there is at least one element in dependentPlugins
|
||||
// so we can safely assume that the freshly instantiated plugin is a dependency
|
||||
//
|
||||
// in that case let's find the first empty spot for a dependency
|
||||
let didx = dependentPlugins[dependentPlugins.length - 1].uses.indexOf(false)
|
||||
// assign the freshly instantiated plugin as that dependency
|
||||
dependentPlugins[dependentPlugins.length - 1].uses[didx] = plugin
|
||||
self.log('service-worker', `${pluginConfig.name}: assigning as dependency (#${didx}) to ${dependentPlugins[dependentPlugins.length - 1].name}`)
|
||||
|
||||
// was this the last one?
|
||||
if (didx >= dependentPlugins[dependentPlugins.length - 1].uses.length - 1) {
|
||||
// yup, last one!
|
||||
self.log('service-worker', `${pluginConfig.name}: this was the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
|
||||
// we can now proceed to instantiate the last element of dependentPlugins
|
||||
pluginConfig = dependentPlugins.pop()
|
||||
continue
|
||||
}
|
||||
if (typeof fileType !== 'undefined' && "fileTypeFromBuffer" in fileType) {
|
||||
detectMimeFromBuffer = fileType.fileTypeFromBuffer
|
||||
self.log('service-worker', 'loaded external MIME sniffing library')
|
||||
} else {
|
||||
self.log('service-worker', 'failed to load external MIME sniffing library!')
|
||||
}
|
||||
}
|
||||
|
||||
// it is not the last one, so there should be more dependency plugins to instantiate first
|
||||
// before we can instantiate the last of element of dependentPlugins
|
||||
// but that requires the full treatment, including checing the `uses` field for their configs
|
||||
self.log('service-worker', `${pluginConfig.name}: not yet the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
|
||||
pluginConfig = false
|
||||
|
||||
// we're good!
|
||||
return true;
|
||||
// if pluginConfig is not false, rinse-repeat the plugin instantiation steps
|
||||
// since we are dealing with the last element of dependentPlugins
|
||||
} while ( (pluginConfig !== false) && (pluginConfig !== undefined) )
|
||||
|
||||
// exception? no bueno
|
||||
} catch (e) {
|
||||
// inform
|
||||
self.log('service-worker', `error while executing config: ${e.message}`)
|
||||
// cleanup after a failed config execution
|
||||
self.LibResilientPluginConstructors = new Map(lrpcBackup)
|
||||
self.LibResilientPlugins = new Array()
|
||||
// we are not good
|
||||
return false;
|
||||
}
|
||||
|
||||
// finally -- do we want to use MIME type guessing based on content?
|
||||
// dealing with this at the very end so that we know we can safely set detectMimeFromBuffer
|
||||
// and not need to re-set it back in case anything fails
|
||||
if (config.useMimeSniffingLibrary === true) {
|
||||
// we do not want to hit a NetworkError and end up using the default config
|
||||
// much better to end up not using the fancy MIME type detection in such a case
|
||||
try {
|
||||
// we do! load the external lib
|
||||
self.importScripts(`./lib/file-type.js`)
|
||||
} catch (e) {
|
||||
self.log('service-worker', `error when fetching external MIME sniffing library: ${e.message}`)
|
||||
}
|
||||
if (typeof fileType !== 'undefined' && "fileTypeFromBuffer" in fileType) {
|
||||
detectMimeFromBuffer = fileType.fileTypeFromBuffer
|
||||
self.log('service-worker', 'loaded external MIME sniffing library')
|
||||
} else {
|
||||
self.log('service-worker', 'failed to load external MIME sniffing library!')
|
||||
}
|
||||
}
|
||||
|
||||
// we're good!
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -530,7 +521,20 @@ let initServiceWorker = async () => {
|
|||
config = {...self.LibResilientConfig, ...cdata}
|
||||
|
||||
// try executing the config
|
||||
config_executed = executeConfig(config)
|
||||
// we want to catch any and all possible errors here
|
||||
try {
|
||||
config_executed = executeConfig(config)
|
||||
|
||||
// exception? no bueno
|
||||
} catch (e) {
|
||||
// inform
|
||||
self.log('service-worker', `error while executing config: ${e.message}`)
|
||||
// cleanup after a failed config execution
|
||||
self.LibResilientPluginConstructors = new Map(lrpcBackup)
|
||||
self.LibResilientPlugins = new Array()
|
||||
// we are not good
|
||||
config_executed = false;
|
||||
}
|
||||
|
||||
// if we're using the defaults, and yet loading of the config failed
|
||||
// something is massively wrong
|
||||
|
@ -552,15 +556,22 @@ let initServiceWorker = async () => {
|
|||
// that is, if it comes from the "v1" cache...
|
||||
if (use_cache === "v1") {
|
||||
self.log('service-worker', `successfully loaded config.json; caching in cache: v1:verified`)
|
||||
cacheConfigJSON(configURL, cresponse, 'v1:verified')
|
||||
// or, was fetch()ed and valid (no caching if we're going with defaults, obviously)
|
||||
await cacheConfigJSON(configURL, cresponse, 'v1:verified')
|
||||
|
||||
// we used the v1:verified cache; we should cache config.json into the v1 cache
|
||||
// as that will speed things up a bit next time we need to load the service worker
|
||||
} else if (use_cache === "v1:verified") {
|
||||
self.log('service-worker', `successfully loaded config.json; caching in cache: v1`)
|
||||
await cacheConfigJSON(configURL, cresponse, 'v1')
|
||||
|
||||
// or, was fetch()-ed and valid (no caching if we're going with defaults, obviously)
|
||||
} else if ( (use_cache === undefined) && (cresponse !== false) ) {
|
||||
self.log('service-worker', `successfully loaded config.json; caching in cache: v1, v1:verified`)
|
||||
// we want to cache to both, so that:
|
||||
// 1. we get the extra bit of performance from using the v1 cache that is checked first
|
||||
// 2. but we get the verified config already in the v1:verified cache for later
|
||||
cacheConfigJSON(configURL, await cresponse.clone(), 'v1')
|
||||
cacheConfigJSON(configURL, cresponse, 'v1:verified')
|
||||
await cacheConfigJSON(configURL, await cresponse.clone(), 'v1')
|
||||
await cacheConfigJSON(configURL, cresponse, 'v1:verified')
|
||||
}
|
||||
|
||||
// inform
|
||||
|
|
Ładowanie…
Reference in New Issue