kopia lustrzana https://gitlab.com/rysiekpl/libresilient
modularize alt fetch plugin as an example
rodzic
d96022f870
commit
b8acb3929e
|
@ -10,16 +10,10 @@
|
|||
* the polyfill is implemented in LibResilient's service-worker.js
|
||||
*/
|
||||
|
||||
// no polluting of the global namespace please
|
||||
(function(LRPC){
|
||||
// this never changes
|
||||
const pluginName = "alt-fetch"
|
||||
LRPC.set(pluginName, (LR, init={})=>{
|
||||
|
||||
/*
|
||||
* plugin config settings
|
||||
*/
|
||||
|
||||
const PLUGIN_NAME = 'alt-fetch';
|
||||
export function altFetchPlugin (LRPC) {
|
||||
let config = {};
|
||||
LRPC.set(PLUGIN_NAME, (LR, init={})=> {
|
||||
// sane defaults
|
||||
let defaultConfig = {
|
||||
// endpoints to use
|
||||
|
@ -46,116 +40,106 @@
|
|||
//
|
||||
// 3 seems to be a reasonable default
|
||||
concurrency: 3
|
||||
}
|
||||
|
||||
// merge the defaults with settings from the init var
|
||||
let config = {...defaultConfig, ...init}
|
||||
};
|
||||
|
||||
config = { ...defaultConfig, ...init };
|
||||
// reality check: endpoints need to be set to an array of non-empty strings
|
||||
if (typeof(config.endpoints) !== "object" || !Array.isArray(config.endpoints)) {
|
||||
let err = new Error("endpoints not confgured")
|
||||
console.error(err)
|
||||
throw err
|
||||
if (typeof (config.endpoints) !== 'object' || !Array.isArray(config.endpoints)) {
|
||||
let err = new Error('endpoints not confgured');
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
name: PLUGIN_NAME,
|
||||
description: 'HTTP(S) fetch() using preconfigured alternative endpoints',
|
||||
version: 'COMMIT_UNKNOWN',
|
||||
fetch: (url, init) => fetchContentFromAlternativeEndpoints(url, init, config)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getting content using regular HTTP(S) fetch()
|
||||
*/
|
||||
let fetchContentFromAlternativeEndpoints = (url, init={}) => {
|
||||
export function fetchContentFromAlternativeEndpoints (url, init = {}, config) {
|
||||
// remove the https://original.domain/ bit to get the relative path
|
||||
// TODO: this assumes that URLs we handle are always relative to the root
|
||||
// TODO: of the original domain, this needs to be documented
|
||||
var path = url.replace(/https?:\/\/[^/]+\//, '');
|
||||
|
||||
// remove the https://original.domain/ bit to get the relative path
|
||||
// TODO: this assumes that URLs we handle are always relative to the root
|
||||
// TODO: of the original domain, this needs to be documented
|
||||
var path = url.replace(/https?:\/\/[^/]+\//, '')
|
||||
// we really want to make fetch happen, Regina!
|
||||
// TODO: this change should *probably* be handled on the Service Worker level
|
||||
// init.cache = 'reload';
|
||||
|
||||
// we really want to make fetch happen, Regina!
|
||||
// TODO: this change should *probably* be handled on the Service Worker level
|
||||
init.cache = 'reload'
|
||||
// we don't want to modify the original endpoints array
|
||||
var sourceEndpoints = [...config.endpoints];
|
||||
|
||||
// we don't want to modify the original endpoints array
|
||||
var sourceEndpoints = [...config.endpoints]
|
||||
let useEndpoints = [];
|
||||
// if we have fewer than the configured concurrency or just as many, use all of them
|
||||
if (sourceEndpoints.length <= config.concurrency) {
|
||||
useEndpoints = sourceEndpoints;
|
||||
|
||||
// if we have fewer than the configured concurrency or just as many, use all of them
|
||||
if (sourceEndpoints.length <= config.concurrency) {
|
||||
var useEndpoints = sourceEndpoints
|
||||
// otherwise get `config.concurrency` endpoints at random
|
||||
} else {
|
||||
while (useEndpoints.length < config.concurrency) {
|
||||
useEndpoints.push(
|
||||
sourceEndpoints
|
||||
.splice(Math.floor(Math.random() * sourceEndpoints.length), 1)[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise get `config.concurrency` endpoints at random
|
||||
} else {
|
||||
var useEndpoints = new Array()
|
||||
while (useEndpoints.length < config.concurrency) {
|
||||
useEndpoints.push(
|
||||
sourceEndpoints
|
||||
.splice(Math.floor(Math.random() * sourceEndpoints.length), 1)[0]
|
||||
)
|
||||
}
|
||||
}
|
||||
// add the rest of the path to each endpoint
|
||||
useEndpoints.forEach((endpoint, index) => {
|
||||
useEndpoints[index] = endpoint + path;
|
||||
});
|
||||
|
||||
// add the rest of the path to each endpoint
|
||||
useEndpoints.forEach((endpoint, index) => {
|
||||
useEndpoints[index] = endpoint + path;
|
||||
// debug log
|
||||
console.log(PLUGIN_NAME, `fetching from alternative endpoints:\n ${useEndpoints.join('\n ')}`);
|
||||
|
||||
return Promise.any(
|
||||
useEndpoints.map(
|
||||
u=>fetch(u, init)
|
||||
))
|
||||
.then((response) => {
|
||||
// 4xx? 5xx? that's a paddlin'
|
||||
if (response.status >= 400) {
|
||||
// throw an Error to fall back to other plugins:
|
||||
throw new Error('HTTP Error: ' + response.status + ' ' + response.statusText);
|
||||
}
|
||||
// all good, it seems
|
||||
console.log(PLUGIN_NAME, 'fetched:', response.url);
|
||||
|
||||
// we need to create a new Response object
|
||||
// with all the headers added explicitly,
|
||||
// since response.headers is immutable
|
||||
var responseInit = {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: {},
|
||||
url: url
|
||||
};
|
||||
response.headers.forEach(function (val, header){
|
||||
responseInit.headers[header] = val;
|
||||
});
|
||||
|
||||
// add the X-LibResilient-* headers to the mix
|
||||
responseInit.headers['X-LibResilient-Method'] = PLUGIN_NAME;
|
||||
|
||||
// we will not have it most of the time, due to CORS rules:
|
||||
// https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header
|
||||
responseInit.headers['X-LibResilient-ETag'] = response.headers.get('ETag');
|
||||
if (responseInit.headers['X-LibResilient-ETag'] === null) {
|
||||
// far from perfect, but what are we going to do, eh?
|
||||
responseInit.headers['X-LibResilient-ETag'] = response.headers.get('last-modified');
|
||||
}
|
||||
|
||||
// return the new response, using the Blob from the original one
|
||||
return response
|
||||
.blob()
|
||||
.then((blob) => {
|
||||
return new Response(
|
||||
blob,
|
||||
responseInit
|
||||
);
|
||||
});
|
||||
|
||||
// debug log
|
||||
LR.log(pluginName, `fetching from alternative endpoints:\n ${useEndpoints.join('\n ')}`)
|
||||
|
||||
return Promise.any(
|
||||
useEndpoints.map(
|
||||
u=>fetch(u, init)
|
||||
))
|
||||
.then((response) => {
|
||||
// 4xx? 5xx? that's a paddlin'
|
||||
if (response.status >= 400) {
|
||||
// throw an Error to fall back to other plugins:
|
||||
throw new Error('HTTP Error: ' + response.status + ' ' + response.statusText);
|
||||
}
|
||||
// all good, it seems
|
||||
LR.log(pluginName, "fetched:", response.url);
|
||||
|
||||
// we need to create a new Response object
|
||||
// with all the headers added explicitly,
|
||||
// since response.headers is immutable
|
||||
var responseInit = {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: {},
|
||||
url: url
|
||||
};
|
||||
response.headers.forEach(function(val, header){
|
||||
responseInit.headers[header] = val;
|
||||
});
|
||||
|
||||
// add the X-LibResilient-* headers to the mix
|
||||
responseInit.headers['X-LibResilient-Method'] = pluginName
|
||||
|
||||
// we will not have it most of the time, due to CORS rules:
|
||||
// https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header
|
||||
responseInit.headers['X-LibResilient-ETag'] = response.headers.get('ETag')
|
||||
if (responseInit.headers['X-LibResilient-ETag'] === null) {
|
||||
// far from perfect, but what are we going to do, eh?
|
||||
responseInit.headers['X-LibResilient-ETag'] = response.headers.get('last-modified')
|
||||
}
|
||||
|
||||
// return the new response, using the Blob from the original one
|
||||
return response
|
||||
.blob()
|
||||
.then((blob) => {
|
||||
return new Response(
|
||||
blob,
|
||||
responseInit
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// return the plugin data structure
|
||||
return {
|
||||
name: pluginName,
|
||||
description: 'HTTP(S) fetch() using preconfigured alternative endpoints',
|
||||
version: 'COMMIT_UNKNOWN',
|
||||
fetch: fetchContentFromAlternativeEndpoints
|
||||
}
|
||||
|
||||
})
|
||||
// done with not polluting the global namespace
|
||||
})(LibResilientPluginConstructors)
|
||||
});
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue