kopia lustrzana https://gitlab.com/rysiekpl/libresilient
redirect plugin implemented, along with tests (ref. #37)
rodzic
50c7053764
commit
4212bb1c3b
|
@ -0,0 +1,102 @@
|
|||
const makeServiceWorkerEnv = require('service-worker-mock');
|
||||
|
||||
global.fetch = require('node-fetch');
|
||||
jest.mock('node-fetch')
|
||||
|
||||
describe("plugin: redirect", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
Object.assign(global, makeServiceWorkerEnv());
|
||||
jest.resetModules();
|
||||
global.LibResilientPluginConstructors = new Map()
|
||||
init = {
|
||||
name: 'redirect',
|
||||
redirectStatus: 302,
|
||||
redirectStatusText: "Found",
|
||||
redirectTo: "https://redirected.example.org/subdir/"
|
||||
}
|
||||
LR = {
|
||||
log: (component, ...items)=>{
|
||||
console.debug(component + ' :: ', ...items)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("it should register in LibResilientPluginConstructors", () => {
|
||||
init = {
|
||||
name: 'redirect',
|
||||
redirectTo: 'https://example.org/'
|
||||
}
|
||||
require("../../plugins/redirect.js");
|
||||
expect(LibResilientPluginConstructors.get('redirect')(LR, init).name).toEqual('redirect');
|
||||
});
|
||||
|
||||
test("it should fail with incorrect redirectTo config value", () => {
|
||||
init = {
|
||||
name: 'redirect',
|
||||
redirectTo: false
|
||||
}
|
||||
require("../../plugins/redirect.js")
|
||||
expect.assertions(1)
|
||||
expect(()=>{
|
||||
LibResilientPluginConstructors.get('redirect')(LR, init)
|
||||
}).toThrow(Error);
|
||||
});
|
||||
|
||||
test("it should fail with incorrect redirectStatus config value", () => {
|
||||
init = {
|
||||
name: 'redirect',
|
||||
redirectTo: 'https://example.org/',
|
||||
redirectStatus: 'incorrect'
|
||||
}
|
||||
require("../../plugins/redirect.js")
|
||||
expect.assertions(1)
|
||||
expect(()=>{
|
||||
LibResilientPluginConstructors.get('redirect')(LR, init)
|
||||
}).toThrow(Error);
|
||||
});
|
||||
|
||||
test("it should fail with incorrect redirectStatusText config value", () => {
|
||||
init = {
|
||||
name: 'redirect',
|
||||
redirectTo: 'https://example.org/',
|
||||
redirectStatusText: false
|
||||
}
|
||||
require("../../plugins/redirect.js")
|
||||
expect.assertions(1)
|
||||
expect(()=>{
|
||||
LibResilientPluginConstructors.get('redirect')(LR, init)
|
||||
}).toThrow(Error);
|
||||
});
|
||||
|
||||
test("it should register in LibResilientPluginConstructors without error even if all config data is incorrect, as long as enabled is false", () => {
|
||||
init = {
|
||||
name: 'redirect',
|
||||
redirectTo: false,
|
||||
redirectStatus: "incorrect",
|
||||
redirectStatusText: false,
|
||||
enabled: false
|
||||
}
|
||||
require("../../plugins/redirect.js");
|
||||
expect(LibResilientPluginConstructors.get('redirect')(LR, init).name).toEqual('redirect');
|
||||
});
|
||||
|
||||
test("it should return a 302 Found redirect for any request", async () => {
|
||||
init = {
|
||||
name: 'redirect',
|
||||
redirectTo: "https://redirected.example.org/subdirectory/"
|
||||
}
|
||||
|
||||
require("../../plugins/redirect.js");
|
||||
|
||||
const response = await LibResilientPluginConstructors.get('redirect')(LR, init).fetch('https://resilient.is/test.json');
|
||||
|
||||
|
||||
//expect().toEqual()
|
||||
expect(response.url).toEqual('https://resilient.is/test.json')
|
||||
expect(response.status).toEqual(302)
|
||||
expect(response.statusText).toEqual('Found')
|
||||
expect(response.headers.get('location')).toEqual('https://redirected.example.org/subdirectory/test.json')
|
||||
})
|
||||
|
||||
});
|
|
@ -0,0 +1,136 @@
|
|||
/* ========================================================================= *\
|
||||
|* === 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)
|
Ładowanie…
Reference in New Issue