-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathsw-simple.js
109 lines (96 loc) · 3.75 KB
/
sw-simple.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// evaluate progress on *?requestId=* URLs only
const progressIndicatorUrls = /\?requestId=/i;
// always install updated SW immediately
self.addEventListener('install', event => {
self.skipWaiting();
})
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
const scope = self.registration.scope;
// redirect index.html to service-worker-enabled page
if (event.request.url === scope || event.request.url === scope+'index.html') {
const newUrl = scope+'sw-installed.html';
console.log('respondWith', newUrl);
event.respondWith(fetch(newUrl))
} else if (progressIndicatorUrls.test(event.request.url)) {
console.log('VER',2,event.request.url)
event.respondWith(fetchWithProgressMonitor(event))
}
})
function fetchWithProgressMonitor(event) {
/* opaque request responses won't give us access to Content-Length and
* Response.body.getReader(), which are required for calculating download
* progress. Respond with a newly-constructed Request from the original Request
* that will give us access to those.
* See https://stackoverflow.com/questions/39109789/what-limitations-apply-to-opaque-responses
* 'Access-Control-Allow-Origin' header in the response must not be the
* wildcard '*' when the request's credentials mode is 'include'. We'll omit credentials in this demo.
*/
const newRequest = new Request(event.request, {
mode: 'cors',
credentials: 'omit'
})
return fetch(newRequest).then(response => respondWithProgressMonitor(event.clientId, response));
}
function respondWithProgressMonitor(clientId, response) {
if (!response.body) {
console.warn("ReadableStream is not yet supported in this browser. See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream")
return response;
}
if (!response.ok) {
// HTTP error code response
return response;
}
// server must send custom x-file-size header if gzip or other content-encoding is used
const contentEncoding = response.headers.get('content-encoding');
const contentLength = response.headers.get(contentEncoding ? 'x-file-size' : 'content-length');
if (contentLength === null) {
// don't track download progress if we can't compare against a total size
console.warn('Response size header unavailable. Cannot measure progress');
return response;
}
let loaded = 0;
debugReadIterations=0; // direct correlation to server's response buffer size
const total = parseInt(contentLength,10);
const reader = response.body.getReader();
return new Response(
new ReadableStream({
start(controller) {
// get client to post message. Awaiting resolution first read() progress
// is sent for progress indicator accuracy
let client;
clients.get(clientId).then(c => {
client = c;
read();
});
function read() {
debugReadIterations++;
reader.read().then(({done, value}) => {
if (done) {
console.log('read()', debugReadIterations);
controller.close();
return;
}
controller.enqueue(value);
loaded += value.byteLength;
// console.log(' SW', Math.round(loaded/total*100)+'%');
dispatchProgress({client, loaded, total});
read();
})
.catch(error => {
// error only typically occurs if network fails mid-download
console.error('error in read()', error);
controller.error(error)
});
}
},
// Firefox excutes this on page stop, Chrome does not
cancel(reason) {
console.log('cancel()', reason);
}
})
)
}
function dispatchProgress({client, loaded, total}) {
client.postMessage({loaded,total})
}