Created by Victoria Bernard and contributors
In the window's JS one must register the service worker
navigator.serviceWorker.register('sw.js',
{ scope: '/path-to-control' })
One example eventListener
self.addEventListener('install', function (event) {
event.waitUntil(preSWWork())
})
Pre cache all the files that we specify by some CACHE_FILES
const CACHE_VERSION = 'name-of-my-cache';
const CACHE_FILES = [
'/whatever-i-want-to-precache.jpg',
'/static.html',
];
let preCacheFunc = function(){
return caches.open(CACHE_VERSION)
.then(function (cache) {
return cache.addAll(CACHE_FILES);
})
}
Populate the cache on installing the worker
self.addEventListener('install', function (event) {
event.waitUntil(preCacheFunc())
})
Set an event listener for any fetch on the scope of this service worker.
self.addEventListener('fetch', function (event) {
event.respondWith(grabFromCacheOrAdd(event.request))
});
Now, we will try to get the request from cache, if it's not there we will add ALL requests to that cache specified by CACHE_VERSION
regardless if it was in preCache files or not
async function grabFromCacheOrAdd(req) {
// Open the cache to see if we have a matching response
const cache = await caches.open(CACHE_VERSION)
const matching = await cache.match(req)
if (matching) return matching
// NO matching response, fetch and put into cache
const res = await fetch(req)
await cache.put(req.url, res)
return res
}
Warm cache with SAFETY_TMP
and ERROR_TMP
const SAFETY_TMP = 'src/safety.html'
const ERROR_TMP = 'src/error.html'
const CACHE_VERSION = 'name-of-my-cache';
let preCacheFunc = function(){
return caches.open(CACHE_VERSION)
.then(function (cache) {
return cache.addAll([SAFETY_TMP, ERROR_TMP])
})
}
Set an event listener for any fetch. If fails, return SAFETY_TMP
self.addEventListener('fetch', function (event) {
return event.respondWith(fetch(event.request)
.then(resp => {
if (!resp.ok) {
return grabFromCacheOrAdd(ERROR_TMP)
}
return resp
})
.catch(error => {
return grabFromCacheOrAdd(SAFETY_TMP)
})
)}
);
Intercept incoming requests
addEventListener('fetch', event => {
event.respondWith(ABTestResponse(event.request))
});
Set up a function to return the response for
/some-path
but really fetch
/control/some-path
or
/test/some-path
, depending on the cookie. Set the a cookie if there isn't one
const name = 'experiment0'
async function ABTestResponse(request) {
let { isNew, group } = getGroup(request.headers.get('Cookie'))
let url = new URL(request.url) url.pathname
=`/$\{group\}$\{url.pathname\}`
const modifiedRequest = new Request(url, {
method:request.method,
headers: request.headers
})
const response = await fetch(modifiedRequest)
if (isNew) {
return getResponseWithSetCookie(group,response)
} else { return response }
}
Function to get group
function getGroup(cookie) {
isNew = false;
if (cookie && cookie.includes(`$\{name\}=control`)) {
group = 'control'
} else if (cookie && cookie.includes(`$\{name\}=test`)) {
group = 'test'
} else {
// 50/50 Split
group = Math.random() < 0.5 ? 'control' : 'test'
isNew = true
}
return { isNew, group }
}
Function to set cookie
function getResponseWithSetCookie(group, response) {
let newHeaders = new Headers(response.headers)
newHeaders.append('Set-Cookie', `$\{name\}=$\{group\}`)
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
})
}
You want to run:
fetch('https://www.google.com')
fetch('https://some-other-host-aint-me.com')
Instead I could call
fetch('https://mysite.com/external/www.google.com')
fetch('https://mysite.com/external/some-other-host-aint-me.com')
Setup a path on your own domain to accept external requests
const myExtralsURI = '/external/'
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const strippedURI = request.url.toString().split(myExtralsURI)
const externalURL = 'https://' + strippedURI[1]
const res = await fetch(externalURL)
// Modify response headers to fix CORS
const headers = new Headers(res.headers)
headers.append('Access-Control-Allow-Origin', '*')
return new Response(res.body, {
status: res.status,
statusText: res.statusText,
headers,
})
}