I use service worker to modify the fetch response with below code,
self.addEventListener('install', function(event) { console.log('install'); }); self.addEventListener('activate', function(event) { console.log('Claiming control'); return self.clients.claim(); }); self.addEventListener('fetch', function(event) { console.log("fetch event") event.respondWith( fetch(event.request).then(function (response) { if(response.url.match('.mp4')){ console.log(event); response.arrayBuffer().then( buffer =>{ let length = 100 view = new Uint8Array(buffer,0,length) for(i=0,j=length - 1; i<j; i++,j--){ view[i] = view[i] ^ view[j] view[j] = view[j] ^ view[i] view[i] = view[i] ^ view[j] } } ) } return response.clone(); }) ); });
Whichi will lead to this warning and error,
The FetchEvent for “” resulted in a network error response: the promise was rejected.
sw.js:60 Uncaught (in promise) TypeError: Failed to execute ‘clone’ on ‘Response’: Response body is already used at sw.js:60
If I put the clone()
before arrayBuffer()
like below,
self.addEventListener('install', function(event) { console.log('install'); }); self.addEventListener('activate', function(event) { console.log('Claiming control'); return self.clients.claim(); }); self.addEventListener('fetch', function(event) { console.log("fetch event") event.respondWith( fetch(event.request).then(function (response) { if(response.url.match('.mp4')){ console.log(event); responseCloned = response.clone(); responseCloned.arrayBuffer().then( buffer =>{ let length = 100 view = new Uint8Array(buffer,0,length) for(i=0,j=length - 1; i<j; i++,j--){ view[i] = view[i] ^ view[j] view[j] = view[j] ^ view[i] view[i] = view[i] ^ view[j] } } ) } return responseCloned; }) ); });
It will lead to below warning and error,
The FetchEvent for “” resulted in a network error response: a Response whose “body” is locked cannot be used to respond to a request.
GET http://localhost/web/357765_decrypted.mp4 net::ERR_FAILED
Advertisement
Answer
Try instantiating and returning a new Response()
with the decrypted bytes:
self.addEventListener('fetch', event => { if (event.request.url.endsWith('.mp4') { event.respondWith(getDecryptedResponse(event.request)) } }) async function getDecryptedResponse(request) { const response = await fetch(request) const bytes = new Uint8Array(await response.arrayBuffer()) return new Response( new Blob([ decryptMp4Bytes(bytes) ], { type: 'application/mp4' }), { headers: response.headers, // possibly required for this case } ) }