0

I'm writing a service worker for a PWA, which handles cached audio files. I'm following this guide to respond correctly to fetches involving range headers, which Safari is very particular about. I have the following function (called from my fetch event handler), which follows fairly closely the code suggested in the guide:

async function respondRangeRequest (request) {
  const response = await respondFromCacheOrFetch(request) // fetches the cached response for the resource
  const arrayBuffer = await response.arrayBuffer()
  const rangeHeader = request.headers.get('range')
  const range = /^bytes=(\d+)-(\d+)?$/.exec(rangeHeader)

  if (!range) {
    console.log(`SW: Invalid range request for ${request.url}`)
    return rangeNotSatisfiableResponse()
  }

  const rangeStart = range[1]
  const rangeEnd = range[2] || arrayBuffer.byteLength - 1

  console.log(`SW: Range request with header ${rangeHeader}`) // range header is `bytes=0-1`

  const responseBuffer = arrayBuffer.slice(rangeStart, rangeEnd + 1)
  // copy headers from the original request, except Content-Length and Content-Range which we will replace
  const responseHeaders = Array.from(response.headers.entries())
    .filter(pair => !['content-range', 'content-length'].includes(pair[0]))

  console.log(responseBuffer) // logs an arrayBuffer with byteLength of 11

  return new Response(
    responseBuffer,
    {
      status: 206,
      statusText: 'Partial Content',
      headers: [
        ...responseHeaders,
        ['Content-Length', responseBuffer.byteLength], // results in `Content-Length 11`
        ['Content-Range', `bytes ${rangeStart}-${rangeEnd}/${arrayBuffer.byteLength}`],
      ],
    }
  )
}

As per the comments, when this code logs a range header of bytes=0-1, the response arrayBuffer constructed through arrayBuffer.slice(rangeStart, rangeEnd + 1) has a byteLength of 11. Why would this be?

My overall response is causing Safari (on iOS) to set a duration of infinity on the audio item, with a resulting inability to seek. This is the only anomalous part of my response that I've noticed, so my only lead for what might be going wrong.

Of course, if anyone has another suggestion for why my response might not be satisfying Safari, I'd be very grateful to hear it!

Igid
  • 515
  • 4
  • 15

1 Answers1

0

Thanks for being my rubber duck everyone. As soon as I asked this question, I looked back over the code snippets in the guide and noticed that I omitted converting my regex results to Number. I changed the lines in question to:

const rangeStart = Number(range[1])
const rangeEnd = Number(range[2]) || arrayBuffer.byteLength - 1

Now Safari likes my response, and I am able to seek through my cached audio files!

Evidently, I was setting my rangeEnd to the string "1", so that rangeEnd + 1 ended up as "11". I love javascript.

Igid
  • 515
  • 4
  • 15