libresilient/plugins/redirect/index.js

137 wiersze
5.4 KiB
JavaScript

/* ========================================================================= *\
|* === HTTP(S) fetch() from alternative endpoints === *|
\* ========================================================================= */
/**
* this plugin does not implement any push method
*
* use this plugin to redirect all requests to a location based on a particular URL
*
* for example, if the original website is `https://some.example.org/`, and the `redirectTo`
* config field is set to `https://other.example.com/subdir/`, the redirect location will
* be the request URL with `https://some.example.org/` replaced with `https://other.example.com/subdir/`:
*
* - https://some.example.org/ redirects to https://other.example.com/subdir/
* - https://some.example.org/favicon.ico redirects to https://other.example.com/subdir/favicon.ico
* - https://some.example.org/api/test.json redirects to https://other.example.com/subdir/api/test.json
* ...and so on
*/
// no polluting of the global namespace please
(function(LRPC){
// this never changes
const pluginName = "redirect"
LRPC.set(pluginName, (LR, init={})=>{
/*
* plugin config settings
*/
// sane defaults
let defaultConfig = {
/*
* URL to base the redirect target on
*
* all requests will be redirected to <redirectTo>/<URL>
*
* if not a string, the plugin returns an error, thus allowing
* plugins configured later in the chain to handle requests
*
* don't forget the ending '/'
*/
redirectTo: null,
/*
* the HTTP code and status to use while redirecting
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
*
* if redirectStatus is not a number, the plugin returns an error,
* thus allowing plugins configured later in the chain to handle requests
*
* by default using 302 Found
*/
redirectStatus: 302,
redirectStatusText: "Found",
/*
* is the plugin enabled?
*
* if this is set to false:
* - the code will *not* check if the plugin is misconfigured;
* - when any handler is called, it will simply return false.
*
* mainly used to load a plugin in a service worker, such that a config.json
* update later can enable it even when the original domain is unavailable.
*/
enabled: true
}
// merge the defaults with settings from the init var
let config = {...defaultConfig, ...init}
// reality check: redirectTo, redirectStatus, redirectStatusText need to be sane
// but only if the plugin is enabled
if (config.enabled) {
if ( typeof config.redirectTo != "string" ) {
let err = new Error("redirectTo should be a string")
console.error(err)
throw err
} else if (config.redirectTo.charAt(config.redirectTo.length - 1) != '/') {
// sanity reminder
LR.log(pluginName, 'warning: redirectTo does not end in "/", this might lead to unexpected results!')
}
if ( typeof config.redirectStatus != "number" ) {
let err = new Error("redirectStatus should be a number")
console.error(err)
throw err
}
if ( typeof config.redirectStatusText != "string" ) {
let err = new Error("redirectStatusText should be a string")
console.error(err)
throw err
}
}
/**
* getting content using regular HTTP(S) fetch()
*/
let redirectInsteadOfFetch = (url, init={}) => {
// TODO: check for obvious redirect loops
// 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 redirectTarget = config.redirectTo + url.replace(/https?:\/\/[^/]+\//, '')
// debug log
LR.log(pluginName, `redirecting:\n - from: ${url}\n - to: ${redirectTarget}\n - status: ${config.redirectStatus} ${config.redirectStatusText}`)
// we need to create a new Response object
// with all the headers added explicitly,
// since response.headers is immutable
var responseInit = {
status: config.redirectStatus,
statusText: config.redirectStatusText,
headers: {
Location: redirectTarget
},
url: url
};
// return a new Response object for this
return new Response(null, responseInit)
}
// return the plugin data structure
return {
name: pluginName,
description: 'HTTP Redirect based on a configured target location',
version: 'COMMIT_UNKNOWN',
fetch: redirectInsteadOfFetch
}
})
// done with not polluting the global namespace
})(LibResilientPluginConstructors)