I have a service worker that catches my page's requests (fetch
event), and when the URL matches a certain pattern, it would fetch another URL and replace the response with that new content.
It works perfectly for text data (JS, XML...) or binary data (e.g. images), but when it es to video, there is a glitch.
I am using Chrome 41 on OSX.
This is the simplified code of my worker:
self.addEventListener('fetch', function(event) {
var url = event.request.url;
console.log('SW: fetch', url);
if (/\.mp4$/.test(url)) {
url = '.mp4';
var options = {
credentials: 'include',
mode: 'no-cors'
};
event.respondWith(fetch(url, options));
}
});
And this is the simplified code in my HTML page:
navigator.serviceWorker.register('sw.js').then(function(reg) {
console.info('ServiceWorker registration successful for', reg.scope);
var video = document.createElement('video');
video.src = '/video.mp4';
video.controls = true;
video.autoplay = true;
video.onerror = function(err) {
console.error('Video load fail', err);
}
video.onload = function(data) {
console.info('Video load success');
}
document.body.appendChild(video);
}).catch(function(err) {
console.error('ServiceWorker registration failed:', err);
});
The first time you load the page, the worker installs, so the video request is not caught, thus failing. But when you reload the page (without cleaning the cache), it is successfully caught, and the worker successfully loads the video (HTTP 200 in its inspector), but for some reason, the main page throws a net::ERR_FAILED
.
And I cannot manually read/stream it, because it es from a different origin, resulting in an "opaque" Response type:
UPDATE: updating to Chrome 42, the error is now HTTP 400 Service Worker Fallback Required (from ServiceWorker)
. Strange thing is that the source code (line 510) indicates that it should only be raised when CORS headers are missing, but here the mode is no-cors
.
I have a service worker that catches my page's requests (fetch
event), and when the URL matches a certain pattern, it would fetch another URL and replace the response with that new content.
It works perfectly for text data (JS, XML...) or binary data (e.g. images), but when it es to video, there is a glitch.
I am using Chrome 41 on OSX.
This is the simplified code of my worker:
self.addEventListener('fetch', function(event) {
var url = event.request.url;
console.log('SW: fetch', url);
if (/\.mp4$/.test(url)) {
url = 'https://vjs.zencdn/v/oceans.mp4';
var options = {
credentials: 'include',
mode: 'no-cors'
};
event.respondWith(fetch(url, options));
}
});
And this is the simplified code in my HTML page:
navigator.serviceWorker.register('sw.js').then(function(reg) {
console.info('ServiceWorker registration successful for', reg.scope);
var video = document.createElement('video');
video.src = '/video.mp4';
video.controls = true;
video.autoplay = true;
video.onerror = function(err) {
console.error('Video load fail', err);
}
video.onload = function(data) {
console.info('Video load success');
}
document.body.appendChild(video);
}).catch(function(err) {
console.error('ServiceWorker registration failed:', err);
});
The first time you load the page, the worker installs, so the video request is not caught, thus failing. But when you reload the page (without cleaning the cache), it is successfully caught, and the worker successfully loads the video (HTTP 200 in its inspector), but for some reason, the main page throws a net::ERR_FAILED
.
And I cannot manually read/stream it, because it es from a different origin, resulting in an "opaque" Response type: http://www.w3/TR/service-workers/#cross-origin-resources
UPDATE: updating to Chrome 42, the error is now HTTP 400 Service Worker Fallback Required (from ServiceWorker)
. Strange thing is that the source code (line 510) indicates that it should only be raised when CORS headers are missing, but here the mode is no-cors
.
1 Answer
Reset to default 7First off, I'd remend trying in the current Chrome Canary, which currently corresponds to release 44.0.2371.0. The developer tooling around service workers continues to improve with each new Chrome release, and it's gotten much better with version 43+ in general.
It doesn't look like opaque Response
objects can be used for the src
of a <video>
element, and I'm assuming that's deliberate. (Opaque Response
s can be used for certain purposes, and I'm afraid I don't have a plete rundown of what will and won't work with an opaque Response
.)
But in any case, you're fortunate in that vjs.zencdn
supports CORS, so you can get away with using the default, CORS-enabled Request
, which will give you a non-opaque Response
.
One thing that you can't do is use credentials: 'include'
in your CORS Request
, because it will lead to a Fetch API cannot load https://vjs.zencdn/v/oceans.mp4. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. error. This doesn't seem like it's an issue with your particular host, since credentials aren't required.
When I switched your code to use event.respondWith(fetch('https://vjs.zencdn/v/oceans.mp4'));
everything worked fine.