From b159976478ab04c99d26b9bc49d817b318d13f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=27rysiek=27=20Wo=C5=BAniak?= Date: Sun, 28 Jan 2024 22:56:25 +0000 Subject: [PATCH] service-worker: checking for plugin constructors availability after retrieving config.json using non-fetch means now also dives into dependencies (ref. #48) --- service-worker.js | 72 +++++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/service-worker.js b/service-worker.js index b19a8fb..17ec8b5 100644 --- a/service-worker.js +++ b/service-worker.js @@ -342,8 +342,14 @@ let initServiceWorker = async () => { // first let's deal with the easy part -- do we want to use MIME type guessing based on content? if (self.LibResilientConfig.useMimeSniffingLibrary === true) { - // we do! load the external lib - self.importScripts(`./lib/file-type.js`) + // 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') @@ -479,30 +485,46 @@ let initServiceWorker = async () => { // if we got the new config.json via a method *other* than plain old fetch(), // we will not be able to use importScripts() to load any pugins that have not been loaded already + // + // NOTICE: this *only* checks if we have all the necessary plugin constructors already available + // which signifies that relevant code has been successfully loaded; but there are other failure modes! if (cresponse.headers.get('x-libresilient-method') != 'fetch') { - // go through the plugins in the new config and check if we already have their constructors - // i.e. if the plugin scripts have already been loaded - // FIXME: this does not currently dive into dependencies! - // FIXME: https://gitlab.com/rysiekpl/libresilient/-/issues/48 - for (let p in cdata.plugins) { - var pname = cdata.plugins[p].name - - // plugin constructor not available, meaning: we'd have to importScripts() it - // but we can't since this was not retrieved via fetch(), so we cannot assume - // that the main domain of the website is up and available - // - // if we cache this newly retrieved config.json, next time the service worker gets restarted - // we will end up with an error while trying to run importScripts() for this plugin - // which in turn would lead to the service worker being unregistered - // - // if the main domain is not available, this would mean the website stops working - // even though we *were* able to retrieve the new config.json via plugins! - // so, ignoring this new config.json. - if (!LibResilientPluginConstructors.has(pname)) { - self.log('service-worker', `config.json was retrieved through plugins other than fetch, but specifies additional plugins (${pname}); ignoring.`) - return false; - } + try { + + // go through the plugins in the new config and check if we already have their constructors + // i.e. if the plugin scripts have already been loaded + let plugins = [...cdata.plugins] + let currentPlugin = plugins.shift() + do { + // plugin constructor not available, meaning: we'd have to importScripts() it + // but we can't since this was not retrieved via fetch(), so we cannot assume + // that the main domain of the website is up and available + // + // if we cache this newly retrieved config.json, next time the service worker gets restarted + // we will end up with an error while trying to run importScripts() for this plugin + // which in turn would lead to the service worker being unregistered + // + // if the main domain is not available, this would mean the website stops working + // even though we *were* able to retrieve the new config.json via plugins! + // so, ignoring this new config.json. + if (!LibResilientPluginConstructors.has(currentPlugin.name)) { + throw new Error(`config.json loaded through transport other than fetch, but specifies not previously loaded plugin: "${currentPlugin.name}"`) + } + // push any dependencies into the array, at the very front + // thus gradually flattening the config + if ("uses" in currentPlugin) { + plugins.unshift(...currentPlugin.uses) + } + // get the next plugin to check + currentPlugin = plugins.shift() + } while ( (currentPlugin !== false) && (currentPlugin !== undefined) ) + + } catch(e) { + self.log( + 'service-worker', + `warning: ${e.message}\nignoring the whole config.json.`) + return false; } } @@ -517,7 +539,7 @@ let initServiceWorker = async () => { // we only get a cryptic "Error while registering a service worker" // unless we explicitly print the errors out in the console console.error(e) - // we got an error while initializing the plugins + // we got an error while initializing the service worker! // better play it safe! self.registration.unregister() throw e