fix(service-worker): several misc fixes for corner cases
This commit fixes several issues discovered through use in real apps. * The sha1() function operated on text content, causing issues for binary-format files. A sha1Binary() function which operates on unparsed data now avoids any encoding issues. * The characters '?' and '+' were not escaped in Glob-to-regex conversion previously, but are now. * URLs from the browser contain the full origin, but were checked against the table of hashes from the manifest which only has the path for URLs from the same origin. Now the origin is checked and URLs are relativized to the domain root before comparison if appropriate. * ngsw: prefix was missing from data groups, is now added. * Occasionally servers will return a redirected response for an asset, and caching it could cause errors for navigation requests. The SW now handles this by detecting such responses and following the redirect manually, to avoid caching a redirected response. * The request for known assets is now created from scratch from the URL before fetching from the network, in order to sanitize it and avoid carrying any special modes or headers that might result in opaque responses. * Debugging log for troubleshooting. * Avoid creating errors by returning 504 responses on error. * Fix bug where idle queue doesn't run in some circumstances. * Add tests for the above.
This commit is contained in:
@ -332,7 +332,7 @@ export class DataGroup {
|
||||
await this.syncLru();
|
||||
|
||||
// Finally, fall back on the network.
|
||||
return this.scope.fetch(req);
|
||||
return this.safeFetch(req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,7 +347,7 @@ export class DataGroup {
|
||||
res = fromCache.res;
|
||||
// Check the age of the resource.
|
||||
if (this.config.refreshAheadMs !== undefined && fromCache.age >= this.config.refreshAheadMs) {
|
||||
ctx.waitUntil(this.safeCacheResponse(req, this.scope.fetch(req)));
|
||||
ctx.waitUntil(this.safeCacheResponse(req, this.safeFetch(req)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -414,17 +414,34 @@ export class DataGroup {
|
||||
}
|
||||
|
||||
private networkFetchWithTimeout(req: Request): [Promise<Response|undefined>, Promise<Response>] {
|
||||
const networkFetch = this.scope.fetch(req);
|
||||
|
||||
// If there is a timeout configured, race a timeout Promise with the network fetch.
|
||||
// Otherwise, just fetch from the network directly.
|
||||
if (this.config.timeoutMs !== undefined) {
|
||||
const networkFetch = this.scope.fetch(req);
|
||||
const safeNetworkFetch = (async() => {
|
||||
try {
|
||||
return await networkFetch;
|
||||
} catch (err) {
|
||||
return this.adapter.newResponse(null, {
|
||||
status: 504,
|
||||
statusText: 'Gateway Timeout',
|
||||
});
|
||||
}
|
||||
})();
|
||||
const networkFetchUndefinedError = (async() => {
|
||||
try {
|
||||
return await networkFetch;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
// Construct a Promise<undefined> for the timeout.
|
||||
const timeout = this.adapter.timeout(this.config.timeoutMs) as Promise<undefined>;
|
||||
// Race that with the network fetch. This will either be a Response, an error, or
|
||||
// `undefined` in the event that the request times out.
|
||||
return [Promise.race([networkFetch, timeout]), networkFetch];
|
||||
// Race that with the network fetch. This will either be a Response, or `undefined`
|
||||
// in the event that the request errored or timed out.
|
||||
return [Promise.race([networkFetchUndefinedError, timeout]), safeNetworkFetch];
|
||||
} else {
|
||||
const networkFetch = this.safeFetch(req);
|
||||
// Do a plain fetch.
|
||||
return [networkFetch, networkFetch];
|
||||
}
|
||||
@ -538,4 +555,15 @@ export class DataGroup {
|
||||
ageTable.delete(url),
|
||||
]);
|
||||
}
|
||||
|
||||
private async safeFetch(req: Request): Promise<Response> {
|
||||
try {
|
||||
return this.scope.fetch(req);
|
||||
} catch (err) {
|
||||
return this.adapter.newResponse(null, {
|
||||
status: 504,
|
||||
statusText: 'Gateway Timeout',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user