From 0df17983c4c1c04658a75efa4132ec2d03c99e2b Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Mon, 8 Jan 2024 21:28:46 -0800 Subject: [PATCH] feat(sw): support range requests also removes audio/video exclusion --- src/sw/controller.js | 10 ---------- src/sw/interceptor.js | 6 ++++-- src/utils.js | 40 ++++++++++++++++++++++++++++++++++++++++ test/utils.spec.js | 22 +++++++++++++++++++++- 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/src/sw/controller.js b/src/sw/controller.js index 2a78ba9..90fa15f 100644 --- a/src/sw/controller.js +++ b/src/sw/controller.js @@ -107,16 +107,6 @@ function meetsInterceptionPreconditions (event) { return false } - // range requests not supported yet. - const isStreamingMedia = ['video', 'audio'].includes(destination) - // HLS works fine, no range requests involved. - const isHLS = url.includes('.m3u8') - - // TODO: Add check for range header, skip if present - if (isStreamingMedia && !isHLS) { - return false - } - // https://developer.mozilla.org/en-US/docs/Web/API/Request/mode // "If a request is made to another origin with this mode set, the // result is simply an error." diff --git a/src/sw/interceptor.js b/src/sw/interceptor.js index 4ffbb7e..5425da0 100644 --- a/src/sw/interceptor.js +++ b/src/sw/interceptor.js @@ -2,7 +2,7 @@ import toIterable from 'browser-readablestream-to-it' import createDebug from 'debug' import * as Sentry from '@sentry/browser' -import { getCidPathFromURL } from '../utils.js' +import { getCidPathFromURL, parseRange } from '../utils.js' const debug = createDebug('sw') const cl = console.log @@ -14,6 +14,7 @@ export class Interceptor { constructor(cid, saturn, clientId, event) { this.cid = cid this.cidPath = getCidPathFromURL(event.request.url, cid) + this.range = parseRange(event.request.headers.get('Range')) this.saturn = saturn this.clientId = clientId this.event = event @@ -36,7 +37,8 @@ export class Interceptor { const opts = { customerFallbackURL: self.event.request.url, raceNodes: true, - firstHitDNS: true + firstHitDNS: true, + range: self.range } const contentItr = await self.saturn.fetchContentWithFallback( self.cidPath, diff --git a/src/utils.js b/src/utils.js index a6a6c33..aaf1df1 100644 --- a/src/utils.js +++ b/src/utils.js @@ -80,3 +80,43 @@ export function getCidPathFromURL(url, cid) { return cidPath } + +export function parseRange(rangeHeader) { + if (typeof rangeHeader !== 'string') { + return + } + + const index = rangeHeader.indexOf('=') + + if (index === -1) { + return + } + + // split the range string + const arr = rangeHeader.slice(index + 1).split(',') + // TODO: support multi-range requests + if (arr.length !== 1) { + return + } + const type = rangeHeader.slice(0, index) + if (type !== 'bytes') { + return + } + + const range = arr[0].split('-') + const rangeStart = parseInt(range[0], 10) + const rangeEnd = parseInt(range[1], 10) + + // -nnn + if (isNaN(rangeStart)) { + if (isNaN(rangeEnd)) { + return + } + return { rangeStart: -rangeEnd } + // nnn- + } else if (isNaN(rangeEnd)) { + return { rangeStart } + } + + return { rangeStart, rangeEnd } +} diff --git a/test/utils.spec.js b/test/utils.spec.js index 2c7c755..5e52380 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -1,6 +1,6 @@ import assert from 'node:assert/strict' import { describe, it } from 'node:test' -import { findCIDInURL, getCidPathFromURL } from '#src/utils.js' +import { findCIDInURL, getCidPathFromURL, parseRange } from '#src/utils.js' describe('controller', () => { it('should find cid in the subdomain', () => { @@ -38,4 +38,24 @@ describe('controller', () => { const foundCidPath = getCidPathFromURL(url, cid) assert.strictEqual(foundCidPath, cidPath) }) + + it('should parse ranges', () => { + assert.strictEqual(parseRange(undefined), undefined) + assert.strictEqual(parseRange(null), undefined) + assert.strictEqual(parseRange(''), undefined) + assert.strictEqual(parseRange('apples=0-1000'), undefined) + assert.strictEqual(parseRange('bytes=0-1000,2000-3000'), undefined) + assert.strictEqual(parseRange('bytes=cheese-crackers'), undefined) + assert.strictEqual(parseRange('bytes=cheese'), undefined) + assert.deepEqual(parseRange('bytes=0-1000'), { + rangeStart: 0, + rangeEnd: 1000 + }) + assert.deepEqual(parseRange('bytes=1000'), { + rangeStart: 1000 + }) + assert.deepEqual(parseRange('bytes=-1000'), { + rangeStart: -1000 + }) + }) })