If a website is experiencing an outage, but LibResilient is deployed on it and is working correctly for the visitors whose browsers were able to install and activate it before the disruption started, it is possible to update LibResilient configuration using any alternative transport plugins that were configured originally.
It is not, however, possible to load new plugins that have not been previously loaded based on the previous config.json
, as this would require a call to importScripts()
and thus a direct HTTPS request to the original domain.
When loading the configuration file that has been retrieved using alternative transports, LibResilient will reject it if it specifies plugins which have not been loaded previously.
importScripts()
The Service Worker API allows for updating the service worker script itself and any imported scripts (via importScripts()
) only via a direct HTTPS request to the original domain.
LibResilient expects configuration in a config.json
file, not in a script, so that it can be retrieved and used without a call to importScripts()
. This allows updating the configuration (but not code) during disruptions. The importScripts()
limitation still affects plugin scripts, however: it is not possible to load new plugin scripts while the original website is experiencing disruption or outage.
LibResilient’s Service Worker needs the config.json
configuration script every time it is installed or re-started.
Service Worker installation happens upon loading the website for the first time. Re-start of the Service Worker can happen at any time — it’s up to the browser to decide when to stop a Service Worker. This usually happens after all relevant tabs are closed, or some time after the last request even if the tabs are open. A previously installed Service Worker is started whenever there is an event for it to handle; in case of LibResilient that will almost always be a fetch
event.
When a Service Worker is stopped, all context (variables, state, etc) is lost. Only the Service Worker script (in our case, service-worker.js
) and any scripts imported in it using importScripts()
remain cached, along with other contents of the cache (and other WebAPIs that are not relevant here).
For LibResilient this means that when an already installed, but stopped, Service Worker gets started again, all plugin initialization needs to happen from scratch. And for that we have to have the config.json
available.
When installing or handling any kind of event, the LibResilient Service Worker script first tries to retrieve the config.json
file. The first place to look is the cache. If there is a valid config.json
available there, it is used to configure and initialize the plugins.
If there is no config.json
available in cache, the only way to get it at this stage (as plugins are not configured yet) is a regular HTTPS fetch()
request to the original domain. If that works, the retrieved config.json
is cached for future use (that is, next time when the Service Worker has been stopped and is being started again) and used to configure and initialize the plugins.
If there is no config.json
available in cache, and the fetch()
request does not produce a valid config.json
either, LibResilient falls back to the default config (that is, a pretty trivial set-up using fetch
and cache
). At this stage LibResilient Service Worker can start handling requests generated by any open tabs in visitor’s browser.
After configuring and initializing the plugins, LibResilient checks if the cached config.json
(if available) is older than 24h, based on the Date:
header in the response. If it is, it is considered stale, and a new background request for config.json
is started, this time using the now available already-initialized plugin infrastructure. At this stage, if alternative transport plugins are configured, updated config.json
can be retrieved even if the original website is experiencing disruptions or downtime.
However, due to the importScripts()
limitation, if a config.json
file is retrieved using alternative transport plugins (not via the fetch
plugin), it is expected not to introduce plugins that have not been used before — only to change the configuration of already-loaded plugins. If a plugin that has not been loaded beforehand is present, that config.json
file is ignored and discarded.
The reasoning behind this is that at this stage we do have a working configuration, as we were able to retrieve config.json
even though the original website is not available. So it’s better to keep using that proven configuration, rather than risk having to call importScripts()
for a new plugin, which might fail due to original website apparently not being available.
Plugins can be configured in the config.json
file as enabled: false
. For example:
{
"plugins": [{
"name": "fetch"
},{
"name": "alt-fetch",
"endpoints": [
"https://example.com/"
]
},{
"name": "redirect",
"enabled": "false"
}],
"loggedComponents": ["service-worker", "fetch", "alt-fetch", "redirect"],
"defaultPluginTimeout": 1000
}
This is a pretty simple config.json
that configures LibResilient to retrieve any website content using regular fetch()
, but with a fall-back to an alt-fetch
alternative transport with one endpoint in case the original website is inaccessible.
Note that the redirect
plugin is configured as disabled. What this means is that its code of this plugin will be loaded (using a call to importScripts()
) into the service worker (as long as the original website is available), but it will not be instantiated nor used for handling any requests.
If the website becomes unavailable for whatever reason, the alt-fetch
plugin will allow the service worker to pull an updated config.json
file from and alternative endpoint. That config.json
file can then activate the (already loaded, but inactive) redirect
plugin to redirect any and all requests to a completely new domain.