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
|
* cdata - config data to verify
|
||||||
*/
|
*/
|
||||||
let verifyConfigData = (cdata) => {
|
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
|
// basic check for the plugins field
|
||||||
if ( !("plugins" in cdata) || ! Array.isArray(cdata.plugins) ) {
|
if ( !("plugins" in cdata) || ! Array.isArray(cdata.plugins) ) {
|
||||||
self.log('service-worker', 'fetched config does not contain a valid "plugins" field')
|
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
|
// 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)
|
// in case we need it later (for example, when re-loading the config)
|
||||||
let pluginsConfig = [...config.plugins]
|
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
|
// only now load the plugins (config.json could have changed the defaults)
|
||||||
try {
|
while (pluginsConfig.length > 0) {
|
||||||
|
|
||||||
// this is the stash for plugins that need dependencies instantiated first
|
// get the first plugin config from the array
|
||||||
let dependentPlugins = new 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)
|
// load the relevant plugin script (if not yet loaded)
|
||||||
while (pluginsConfig.length > 0) {
|
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
|
// move the dependency plugin configs to LibResilientConfig to be worked on next
|
||||||
let pluginConfig = pluginsConfig.shift()
|
for (let i=(pluginConfig.uses.length); i--; i>=0) {
|
||||||
self.log('service-worker', `handling plugin type: ${pluginConfig.name}`)
|
self.log('service-worker', `${pluginConfig.name}: dependency found: ${pluginConfig.uses[i].name}`)
|
||||||
|
// put the plugin config in front of the plugin configs array
|
||||||
// load the relevant plugin script (if not yet loaded)
|
pluginsConfig.unshift(pluginConfig.uses[i])
|
||||||
if (!LibResilientPluginConstructors.has(pluginConfig.name)) {
|
// set each dependency plugin config to false so that we can keep track
|
||||||
self.log('service-worker', `${pluginConfig.name}: loading plugin's source`)
|
// as we fill those gaps later with instantiated dependency plugins
|
||||||
self.importScripts(`./plugins/${pluginConfig.name}/index.js`)
|
pluginConfig.uses[i] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// do we have any dependencies we should handle first?
|
// stash the plugin config until we have all the dependencies handled
|
||||||
if (typeof pluginConfig.uses !== "undefined") {
|
self.log('service-worker', `${pluginConfig.name}: not instantiating until dependencies are ready`)
|
||||||
self.log('service-worker', `${pluginConfig.name}: ${pluginConfig.uses.length} dependencies found`)
|
dependentPlugins.push(pluginConfig)
|
||||||
|
|
||||||
// move the dependency plugin configs to LibResilientConfig to be worked on next
|
// move on to the next plugin config, which at this point will be
|
||||||
for (let i=(pluginConfig.uses.length); i--; i>=0) {
|
// the first of dependencies for the plugin whose config got stashed
|
||||||
self.log('service-worker', `${pluginConfig.name}: dependency found: ${pluginConfig.uses[i].name}`)
|
continue;
|
||||||
// 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
|
do {
|
||||||
// as we fill those gaps later with instantiated dependency plugins
|
|
||||||
pluginConfig.uses[i] = false
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
// instantiate the plugin
|
||||||
|
let plugin = LibResilientPluginConstructors.get(pluginConfig.name)(self, pluginConfig)
|
||||||
// if the plugin is not enabled, no instantiation for it nor for its dependencies
|
self.log('service-worker', `${pluginConfig.name}: instantiated`)
|
||||||
// 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
|
|
||||||
|
|
||||||
// if pluginConfig is not false, rinse-repeat the plugin instantiation steps
|
// do we have a stashed plugin that requires dependencies?
|
||||||
// since we are dealing with the last element of dependentPlugins
|
if (dependentPlugins.length === 0) {
|
||||||
} while ( (pluginConfig !== false) && (pluginConfig !== undefined) )
|
// 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
|
||||||
// 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
|
// in that case let's find the first empty spot for a dependency
|
||||||
// and not need to re-set it back in case anything fails
|
let didx = dependentPlugins[dependentPlugins.length - 1].uses.indexOf(false)
|
||||||
if (config.useMimeSniffingLibrary === true) {
|
// assign the freshly instantiated plugin as that dependency
|
||||||
// we do not want to hit a NetworkError and end up using the default config
|
dependentPlugins[dependentPlugins.length - 1].uses[didx] = plugin
|
||||||
// much better to end up not using the fancy MIME type detection in such a case
|
self.log('service-worker', `${pluginConfig.name}: assigning as dependency (#${didx}) to ${dependentPlugins[dependentPlugins.length - 1].name}`)
|
||||||
try {
|
|
||||||
// we do! load the external lib
|
// was this the last one?
|
||||||
self.importScripts(`./lib/file-type.js`)
|
if (didx >= dependentPlugins[dependentPlugins.length - 1].uses.length - 1) {
|
||||||
} catch (e) {
|
// yup, last one!
|
||||||
self.log('service-worker', `error when fetching external MIME sniffing library: ${e.message}`)
|
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
|
// it is not the last one, so there should be more dependency plugins to instantiate first
|
||||||
self.log('service-worker', 'loaded external MIME sniffing library')
|
// before we can instantiate the last of element of dependentPlugins
|
||||||
} else {
|
// but that requires the full treatment, including checing the `uses` field for their configs
|
||||||
self.log('service-worker', 'failed to load external MIME sniffing library!')
|
self.log('service-worker', `${pluginConfig.name}: not yet the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
|
||||||
}
|
pluginConfig = false
|
||||||
}
|
|
||||||
|
|
||||||
// we're good!
|
// if pluginConfig is not false, rinse-repeat the plugin instantiation steps
|
||||||
return true;
|
// 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}
|
config = {...self.LibResilientConfig, ...cdata}
|
||||||
|
|
||||||
// try executing the config
|
// 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
|
// if we're using the defaults, and yet loading of the config failed
|
||||||
// something is massively wrong
|
// something is massively wrong
|
||||||
|
@ -552,15 +556,22 @@ let initServiceWorker = async () => {
|
||||||
// that is, if it comes from the "v1" cache...
|
// that is, if it comes from the "v1" cache...
|
||||||
if (use_cache === "v1") {
|
if (use_cache === "v1") {
|
||||||
self.log('service-worker', `successfully loaded config.json; caching in cache: v1:verified`)
|
self.log('service-worker', `successfully loaded config.json; caching in cache: v1:verified`)
|
||||||
cacheConfigJSON(configURL, cresponse, 'v1:verified')
|
await cacheConfigJSON(configURL, cresponse, 'v1:verified')
|
||||||
// or, was fetch()ed and valid (no caching if we're going with defaults, obviously)
|
|
||||||
|
// 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) ) {
|
} else if ( (use_cache === undefined) && (cresponse !== false) ) {
|
||||||
self.log('service-worker', `successfully loaded config.json; caching in cache: v1, v1:verified`)
|
self.log('service-worker', `successfully loaded config.json; caching in cache: v1, v1:verified`)
|
||||||
// we want to cache to both, so that:
|
// 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
|
// 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
|
// 2. but we get the verified config already in the v1:verified cache for later
|
||||||
cacheConfigJSON(configURL, await cresponse.clone(), 'v1')
|
await cacheConfigJSON(configURL, await cresponse.clone(), 'v1')
|
||||||
cacheConfigJSON(configURL, cresponse, 'v1:verified')
|
await cacheConfigJSON(configURL, cresponse, 'v1:verified')
|
||||||
}
|
}
|
||||||
|
|
||||||
// inform
|
// inform
|
||||||
|
|
Ładowanie…
Reference in New Issue