-
-
Save asvny/1af7e89db2b9d9004528f462158a8ccc to your computer and use it in GitHub Desktop.
Multiplexing downloads via Service Workers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Size of one chunk when requesting with Range | |
* @type {Number} | |
* @private | |
*/ | |
const CHUNK_SIZE = 204800; | |
/** | |
* Concat two ArrayBuffers | |
* @param {ArrayBuffer} ab1 | |
* @param {ArrayBuffer} ab2 | |
* @returns {ArrayBuffer} Returns new ArrayBuffer | |
* @private | |
*/ | |
function concatArrayBuffer(ab1, ab2) { | |
const tmp = new Uint8Array(ab1.byteLength + ab2.byteLength); | |
tmp.set(new Uint8Array(ab1), 0); | |
tmp.set(new Uint8Array(ab2), ab1.byteLength); | |
return tmp.buffer; | |
} | |
/** | |
* Triggers each time when HEAD request is successful | |
* @param {Request} request Original request | |
* @param {Response} response HEAD response from a server | |
* @returns {Promise} Returns promise that fulfils into new Response object | |
* @fulfils {Response} | |
* @private | |
*/ | |
function onHeadResponse(request, response) { | |
const contentLength = response.headers.get('content-length'); | |
const promises = Array | |
.from({length: Math.ceil(contentLength / CHUNK_SIZE)}) | |
.map((_, i) => { | |
const headers = new Headers(request.headers); | |
headers.append('Range', `bytes=${i * CHUNK_SIZE}-${(i * CHUNK_SIZE) + CHUNK_SIZE - 1}/${contentLength}`); | |
return fetch(new Request(request, {headers})); | |
}); | |
return Promise | |
.all(promises) | |
.then(responses => Promise.all(responses.map(res => res.arrayBuffer()))) | |
.then(buffers => new Response(buffers.reduce(concatArrayBuffer), {headers: response.headers})); | |
} | |
/** | |
* Triggers each time when fetch event fires in Service Worker | |
* @param {FetchEvent} event | |
* @returns {Promise} | |
* @fulfils {Response} | |
* @private | |
*/ | |
function onFetch(event) { | |
const url = new URL(event.request.url); | |
if (event.request.mode === 'navigate') return event.respondWith(fetch(event.request)); | |
if (event.request.mode === 'no-cors' && url.origin !== location.origin) return event.respondWith(fetch(event.request)); | |
let mode = 'cors'; | |
if (url.origin === location.origin) mode = 'same-origin'; | |
const request = new Request(event.request, {mode}); | |
return event.respondWith(fetch(new Request(request, {method: 'HEAD'})).then(onHeadResponse.bind(this, request))); | |
} | |
self.addEventListener('install', event => event.waitUntil(self.skipWaiting())); | |
self.addEventListener('activate', event => event.waitUntil(self.clients.claim())); | |
self.addEventListener('fetch', onFetch); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment