angular-cn/packages/service-worker/worker/test/prefetch_spec.ts

101 lines
4.2 KiB
TypeScript
Raw Normal View History

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {PrefetchAssetGroup} from '../src/assets';
import {CacheDatabase} from '../src/db-cache';
import {IdleScheduler} from '../src/idle';
feat(service-worker): use `ignoreVary: true` when retrieving responses from cache (#34663) The Angular ServiceWorker always uses a copy of the request without headers for caching assets (in order to avoid issues with opaque responses). Therefore, it was previously not possible to retrieve resources from the cache if the response contained [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary) headers. In addition to that, `Vary` headers do not work in all browsers (or work differently) and may not work as intended with ServiceWorker caches. See [this article](https://www.smashingmagazine.com/2017/11/understanding-vary-header) and the linked resources for more info. This commit avoids the aforementioned issues by making sure the Angular ServiceWorker always sets the `ignoreVary` option passed to [Cache#match()](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) to `true`. This allows the ServiceWorker to correctly retrieve cached responses with `Vary` headers, which was previously not possible. Fixes #36638 BREAKING CHANGE: Previously, [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary) headers would be taken into account when retrieving resources from the cache, completely preventing the retrieval of cached assets (due to ServiceWorker implementation details) and leading to unpredictable behavior due to inconsistent/buggy implementations in different browsers. Now, `Vary` headers are ignored when retrieving resources from the ServiceWorker caches, which can result in resources being retrieved even when their headers are different. If your application needs to differentiate its responses based on request headers, please make sure the Angular ServiceWorker is [configured](https://angular.io/guide/service-worker-config) to avoid caching the affected resources. PR Close #34663
2020-04-29 10:59:15 -04:00
import {MockCache} from '../testing/cache';
import {MockRequest} from '../testing/fetch';
import {MockFileSystemBuilder, MockServerStateBuilder, tmpHashTable, tmpManifestSingleAssetGroup} from '../testing/mock';
import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
(function() {
// Skip environments that don't support the minimum APIs needed to run the SW tests.
if (!SwTestHarness.envIsSupported()) {
return;
}
const dist = new MockFileSystemBuilder()
feat(service-worker): use `ignoreVary: true` when retrieving responses from cache (#34663) The Angular ServiceWorker always uses a copy of the request without headers for caching assets (in order to avoid issues with opaque responses). Therefore, it was previously not possible to retrieve resources from the cache if the response contained [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary) headers. In addition to that, `Vary` headers do not work in all browsers (or work differently) and may not work as intended with ServiceWorker caches. See [this article](https://www.smashingmagazine.com/2017/11/understanding-vary-header) and the linked resources for more info. This commit avoids the aforementioned issues by making sure the Angular ServiceWorker always sets the `ignoreVary` option passed to [Cache#match()](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) to `true`. This allows the ServiceWorker to correctly retrieve cached responses with `Vary` headers, which was previously not possible. Fixes #36638 BREAKING CHANGE: Previously, [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary) headers would be taken into account when retrieving resources from the cache, completely preventing the retrieval of cached assets (due to ServiceWorker implementation details) and leading to unpredictable behavior due to inconsistent/buggy implementations in different browsers. Now, `Vary` headers are ignored when retrieving resources from the ServiceWorker caches, which can result in resources being retrieved even when their headers are different. If your application needs to differentiate its responses based on request headers, please make sure the Angular ServiceWorker is [configured](https://angular.io/guide/service-worker-config) to avoid caching the affected resources. PR Close #34663
2020-04-29 10:59:15 -04:00
.addFile('/foo.txt', 'this is foo', {Vary: 'Accept'})
.addFile('/bar.txt', 'this is bar')
.build();
const manifest = tmpManifestSingleAssetGroup(dist);
const server = new MockServerStateBuilder().withStaticFiles(dist).withManifest(manifest).build();
const scope = new SwTestHarnessBuilder().withServerState(server).build();
const db = new CacheDatabase(scope, scope);
describe('prefetch assets', () => {
let group: PrefetchAssetGroup;
let idle: IdleScheduler;
beforeEach(() => {
idle = new IdleScheduler(null!, 3000, 30000, {
log: (v, ctx = '') => console.error(v, ctx),
});
group = new PrefetchAssetGroup(
scope, scope, idle, manifest.assetGroups![0], tmpHashTable(manifest), db, 'test');
});
it('initializes without crashing', async () => {
await group.initializeFully();
});
it('fully caches the two files', async () => {
await group.initializeFully();
scope.updateServerState();
const res1 = await group.handleFetch(scope.newRequest('/foo.txt'), scope);
const res2 = await group.handleFetch(scope.newRequest('/bar.txt'), scope);
expect(await res1!.text()).toEqual('this is foo');
expect(await res2!.text()).toEqual('this is bar');
});
it('persists the cache across restarts', async () => {
await group.initializeFully();
const freshScope = new SwTestHarnessBuilder().withCacheState(scope.caches.dehydrate()).build();
group = new PrefetchAssetGroup(
freshScope, freshScope, idle, manifest.assetGroups![0], tmpHashTable(manifest),
new CacheDatabase(freshScope, freshScope), 'test');
await group.initializeFully();
const res1 = await group.handleFetch(scope.newRequest('/foo.txt'), scope);
const res2 = await group.handleFetch(scope.newRequest('/bar.txt'), scope);
expect(await res1!.text()).toEqual('this is foo');
expect(await res2!.text()).toEqual('this is bar');
});
it('caches properly if resources are requested before initialization', async () => {
const res1 = await group.handleFetch(scope.newRequest('/foo.txt'), scope);
const res2 = await group.handleFetch(scope.newRequest('/bar.txt'), scope);
expect(await res1!.text()).toEqual('this is foo');
expect(await res2!.text()).toEqual('this is bar');
scope.updateServerState();
await group.initializeFully();
});
it('throws if the server-side content does not match the manifest hash', async () => {
const badHashFs = dist.extend().addFile('/foo.txt', 'corrupted file').build();
const badServer =
new MockServerStateBuilder().withManifest(manifest).withStaticFiles(badHashFs).build();
const badScope = new SwTestHarnessBuilder().withServerState(badServer).build();
group = new PrefetchAssetGroup(
badScope, badScope, idle, manifest.assetGroups![0], tmpHashTable(manifest),
new CacheDatabase(badScope, badScope), 'test');
const err = await errorFrom(group.initializeFully());
expect(err.message).toContain('Hash mismatch');
});
feat(service-worker): use `ignoreVary: true` when retrieving responses from cache (#34663) The Angular ServiceWorker always uses a copy of the request without headers for caching assets (in order to avoid issues with opaque responses). Therefore, it was previously not possible to retrieve resources from the cache if the response contained [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary) headers. In addition to that, `Vary` headers do not work in all browsers (or work differently) and may not work as intended with ServiceWorker caches. See [this article](https://www.smashingmagazine.com/2017/11/understanding-vary-header) and the linked resources for more info. This commit avoids the aforementioned issues by making sure the Angular ServiceWorker always sets the `ignoreVary` option passed to [Cache#match()](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) to `true`. This allows the ServiceWorker to correctly retrieve cached responses with `Vary` headers, which was previously not possible. Fixes #36638 BREAKING CHANGE: Previously, [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary) headers would be taken into account when retrieving resources from the cache, completely preventing the retrieval of cached assets (due to ServiceWorker implementation details) and leading to unpredictable behavior due to inconsistent/buggy implementations in different browsers. Now, `Vary` headers are ignored when retrieving resources from the ServiceWorker caches, which can result in resources being retrieved even when their headers are different. If your application needs to differentiate its responses based on request headers, please make sure the Angular ServiceWorker is [configured](https://angular.io/guide/service-worker-config) to avoid caching the affected resources. PR Close #34663
2020-04-29 10:59:15 -04:00
it('CacheQueryOptions are passed through', async () => {
await group.initializeFully();
const matchSpy = spyOn(MockCache.prototype, 'match').and.callThrough();
await group.handleFetch(scope.newRequest('/foo.txt'), scope);
expect(matchSpy).toHaveBeenCalledWith(new MockRequest('/foo.txt'), {ignoreVary: true});
});
});
})();
function errorFrom(promise: Promise<any>): Promise<any> {
return promise.catch(err => err);
}