Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions aio/content/guide/service-worker-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ The `SwUpdate` service includes that data in the update notifications. Many apps

Specifies the file that serves as the index page to satisfy navigation requests. Usually this is `/index.html`.

## `requestOptions`

Specifies the fetch request options [@RequestInit](https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts)

## `assetGroups`

*Assets* are resources that are part of the app version that update along with the app. They can include resources loaded from the page's origin as well as third-party resources loaded from CDNs and other external URLs. As not all such external URLs may be known at build time, URL patterns can be matched.
Expand Down
1 change: 1 addition & 0 deletions packages/service-worker/config/src/in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type Duration = string;
*/
export interface Config {
appData?: {};
requestOptions: RequestInit;
index: string;
assetGroups?: AssetGroup[];
dataGroups?: DataGroup[];
Expand Down
5 changes: 2 additions & 3 deletions packages/service-worker/config/test/generator_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {MockFilesystem} from '../testing/mock';
test: true,
},
index: '/index.html',
requestOptions: {credentials: 'omit'},
assetGroups: [{
name: 'test',
resources: {
Expand Down Expand Up @@ -117,9 +118,7 @@ import {MockFilesystem} from '../testing/mock';
'/index.html': 'This is a test',
});
const gen = new Generator(fs, '/test');
const res = gen.process({
index: '/index.html',
});
const res = gen.process({index: '/index.html', requestOptions: {credentials: 'omit'}});
res.then(config => {
expect(config).toEqual({
configVersion: 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/service-worker/test/integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const manifest: Manifest = {
configVersion: 1,
appData: {version: '1'},
index: '/only.txt',
requestOptions: {credentials: 'omit'},
assetGroups: [{
name: 'assets',
installMode: 'prefetch',
Expand All @@ -49,6 +50,7 @@ const manifestUpdate: Manifest = {
configVersion: 1,
appData: {version: '2'},
index: '/only.txt',
requestOptions: {credentials: 'include'},
assetGroups: [{
name: 'assets',
installMode: 'prefetch',
Expand Down
4 changes: 2 additions & 2 deletions packages/service-worker/worker/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export interface UpdateSource {
* This returns a state value which indicates whether the resource was
* cached at all and whether that cache was utilized.
*/
recentCacheStatus(url: string): Promise<UpdateCacheStatus>;
recentCacheStatus(url: string, requestOptions?: RequestInit): Promise<UpdateCacheStatus>;
}

/**
Expand Down Expand Up @@ -108,4 +108,4 @@ export interface Debuggable {
debugState(): Promise<DebugState>;
debugVersions(): Promise<DebugVersion[]>;
debugIdleState(): Promise<DebugIdleState>;
}
}
15 changes: 9 additions & 6 deletions packages/service-worker/worker/src/app-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ export class AppVersion implements UpdateSource {
switch (config.installMode) {
case 'prefetch':
return new PrefetchAssetGroup(
this.scope, this.adapter, this.idle, config, this.hashTable, this.database, prefix);
this.scope, this.adapter, this.idle, config, this.manifest, this.hashTable,
this.database, prefix);
case 'lazy':
return new LazyAssetGroup(
this.scope, this.adapter, this.idle, config, this.hashTable, this.database, prefix);
this.scope, this.adapter, this.idle, config, this.manifest, this.hashTable,
this.database, prefix);
}
});

Expand Down Expand Up @@ -167,7 +169,8 @@ export class AppVersion implements UpdateSource {
if (req.url !== this.manifest.index && this.isNavigationRequest(req)) {
// This was a navigation request. Re-enter `handleFetch` with a request for
// the URL.
return this.handleFetch(this.adapter.newRequest(this.manifest.index), context);
return this.handleFetch(
this.adapter.newRequest(this.manifest.index, this.manifest.requestOptions), context);
}

return null;
Expand Down Expand Up @@ -198,7 +201,7 @@ export class AppVersion implements UpdateSource {
* Check this version for a given resource with a particular hash.
*/
async lookupResourceWithHash(url: string, hash: string): Promise<Response|null> {
const req = this.adapter.newRequest(url);
const req = this.adapter.newRequest(url, this.manifest.requestOptions);

// Verify that this version has the requested resource cached. If not,
// there's no point in trying.
Expand Down Expand Up @@ -247,13 +250,13 @@ export class AppVersion implements UpdateSource {
}, Promise.resolve<string[]>([]));
}

async recentCacheStatus(url: string): Promise<UpdateCacheStatus> {
async recentCacheStatus(url: string, requestOptions?: RequestInit): Promise<UpdateCacheStatus> {
return this.assetGroups.reduce(async(current, group) => {
const status = await current;
if (status === UpdateCacheStatus.CACHED) {
return status;
}
const groupStatus = await group.cacheStatus(url);
const groupStatus = await group.cacheStatus(url, this.manifest.requestOptions);
if (groupStatus === UpdateCacheStatus.NOT_CACHED) {
return status;
}
Expand Down
28 changes: 16 additions & 12 deletions packages/service-worker/worker/src/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {CacheState, UpdateCacheStatus, UpdateSource, UrlMetadata} from './api';
import {Database, Table} from './database';
import {SwCriticalError} from './error';
import {IdleScheduler} from './idle';
import {AssetGroupConfig} from './manifest';
import {AssetGroupConfig, Manifest} from './manifest';
import {sha1Binary} from './sha1';

/**
Expand Down Expand Up @@ -53,7 +53,8 @@ export abstract class AssetGroup {
constructor(
protected scope: ServiceWorkerGlobalScope, protected adapter: Adapter,
protected idle: IdleScheduler, protected config: AssetGroupConfig,
protected hashes: Map<string, string>, protected db: Database, protected prefix: string) {
protected manifest: Manifest, protected hashes: Map<string, string>, protected db: Database,
protected prefix: string) {
this.name = config.name;
// Patterns in the config are regular expressions disguised as strings. Breathe life into them.
this.patterns = this.config.patterns.map(pattern => new RegExp(pattern));
Expand All @@ -73,10 +74,10 @@ export abstract class AssetGroup {
this.adapter.parseUrl(this.scope.registration.scope, this.scope.registration.scope).origin;
}

async cacheStatus(url: string): Promise<UpdateCacheStatus> {
async cacheStatus(url: string, requestOptions?: RequestInit): Promise<UpdateCacheStatus> {
const cache = await this.cache;
const meta = await this.metadata;
const res = await cache.match(this.adapter.newRequest(url));
const res = await cache.match(this.adapter.newRequest(url, requestOptions));
if (res === undefined) {
return UpdateCacheStatus.NOT_CACHED;
}
Expand Down Expand Up @@ -145,7 +146,8 @@ export abstract class AssetGroup {
// No already-cached response exists, so attempt a fetch/cache operation. The original request
// may specify things like credential inclusion, but for assets these are not honored in order
// to avoid issues with opaque responses. The SW requests the data itself.
const res = await this.fetchAndCacheOnce(this.adapter.newRequest(req.url));
const res = await this.fetchAndCacheOnce(
this.adapter.newRequest(req.url, this.manifest.requestOptions));

// If this is successful, the response needs to be cloned as it might be used to respond to
// multiple fetch operations at the same time.
Expand Down Expand Up @@ -252,7 +254,7 @@ export abstract class AssetGroup {
const metaTable = await this.metadata;

// Lookup the response in the cache.
const response = await cache.match(this.adapter.newRequest(url));
const response = await cache.match(this.adapter.newRequest(url, this.manifest.requestOptions));
if (response === undefined) {
// It's not found, return null.
return null;
Expand Down Expand Up @@ -351,7 +353,8 @@ export abstract class AssetGroup {
}

// Unwrap the redirect directly.
return this.fetchFromNetwork(this.adapter.newRequest(res.url), redirectLimit - 1);
return this.fetchFromNetwork(
this.adapter.newRequest(res.url, this.manifest.requestOptions), redirectLimit - 1);
}

return res;
Expand Down Expand Up @@ -405,7 +408,8 @@ export abstract class AssetGroup {
// data, or because the version on the server really doesn't match. A cache-busting
// request will differentiate these two situations.
// TODO: handle case where the URL has parameters already (unlikely for assets).
const cacheBustReq = this.adapter.newRequest(this.cacheBust(req.url));
const cacheBustReq =
this.adapter.newRequest(this.cacheBust(req.url), this.manifest.requestOptions);
const cacheBustedResult = await this.safeFetch(cacheBustReq);

// If the response was unsuccessful, there's nothing more that can be done.
Expand Down Expand Up @@ -502,7 +506,7 @@ export class PrefetchAssetGroup extends AssetGroup {
await previous;

// Construct the Request for this url.
const req = this.adapter.newRequest(url);
const req = this.adapter.newRequest(url, this.manifest.requestOptions);

// First, check the cache to see if there is already a copy of this resource.
const alreadyCached = (await cache.match(req)) !== undefined;
Expand Down Expand Up @@ -537,7 +541,7 @@ export class PrefetchAssetGroup extends AssetGroup {
// Finally, process each resource in turn.
.reduce(async(previous, url) => {
await previous;
const req = this.adapter.newRequest(url);
const req = this.adapter.newRequest(url, this.manifest.requestOptions);

// It's possible that the resource in question is already cached. If so,
// continue to the next one.
Expand Down Expand Up @@ -579,7 +583,7 @@ export class LazyAssetGroup extends AssetGroup {
await previous;

// Construct the Request for this url.
const req = this.adapter.newRequest(url);
const req = this.adapter.newRequest(url, this.manifest.requestOptions);

// First, check the cache to see if there is already a copy of this resource.
const alreadyCached = (await cache.match(req)) !== undefined;
Expand All @@ -597,7 +601,7 @@ export class LazyAssetGroup extends AssetGroup {
// except if it was not previously utilized. Check the status of the
// cached resource to see.

const cacheStatus = await updateFrom.recentCacheStatus(url);
const cacheStatus = await updateFrom.recentCacheStatus(url, this.manifest.requestOptions);

// If the resource is not cached, or was cached but unused, then it will be
// loaded lazily.
Expand Down
17 changes: 12 additions & 5 deletions packages/service-worker/worker/src/db-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {Database, NotFound, Table} from './database';
export class CacheDatabase implements Database {
private tables = new Map<string, Promise<CacheTable>>();

constructor(private scope: ServiceWorkerGlobalScope, private adapter: Adapter) {}
constructor(
private scope: ServiceWorkerGlobalScope, private adapter: Adapter,
private requestOptions?: RequestInit) {}

'delete'(name: string): Promise<boolean> {
if (this.tables.has(name)) {
Expand All @@ -32,8 +34,9 @@ export class CacheDatabase implements Database {

open(name: string): Promise<Table> {
if (!this.tables.has(name)) {
const table = this.scope.caches.open(`ngsw:db:${name}`)
.then(cache => new CacheTable(name, cache, this.adapter));
const table =
this.scope.caches.open(`ngsw:db:${name}`)
.then(cache => new CacheTable(name, cache, this.adapter, this.requestOptions));
this.tables.set(name, table);
}
return this.tables.get(name) !;
Expand All @@ -44,9 +47,13 @@ export class CacheDatabase implements Database {
* A `Table` backed by a `Cache`.
*/
export class CacheTable implements Table {
constructor(readonly table: string, private cache: Cache, private adapter: Adapter) {}
constructor(
readonly table: string, private cache: Cache, private adapter: Adapter,
private requestOptions?: RequestInit) {}

private request(key: string): Request { return this.adapter.newRequest('/' + key); }
private request(key: string): Request {
return this.adapter.newRequest('/' + key, this.requestOptions);
}

'delete'(key: string): Promise<boolean> { return this.cache.delete(this.request(key)); }

Expand Down
1 change: 1 addition & 0 deletions packages/service-worker/worker/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type ManifestHash = string;
export interface Manifest {
configVersion: number;
appData?: {[key: string]: string};
requestOptions?: RequestInit;
index: string;
assetGroups?: AssetGroupConfig[];
dataGroups?: DataGroupConfig[];
Expand Down
1 change: 1 addition & 0 deletions packages/service-worker/worker/test/data_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const distUpdate = new MockFileSystemBuilder()
const manifest: Manifest = {
configVersion: 1,
index: '/index.html',
requestOptions: {credentials: 'omit'},
assetGroups: [
{
name: 'assets',
Expand Down
3 changes: 3 additions & 0 deletions packages/service-worker/worker/test/happy_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const brokenFs = new MockFileSystemBuilder().addFile('/foo.txt', 'this is foo').
const brokenManifest: Manifest = {
configVersion: 1,
index: '/foo.txt',
requestOptions: {credentials: 'omit'},
assetGroups: [{
name: 'assets',
installMode: 'prefetch',
Expand All @@ -63,6 +64,7 @@ const manifest: Manifest = {
appData: {
version: 'original',
},
requestOptions: {credentials: 'omit'},
index: '/foo.txt',
assetGroups: [
{
Expand Down Expand Up @@ -105,6 +107,7 @@ const manifestUpdate: Manifest = {
appData: {
version: 'update',
},
requestOptions: {credentials: 'include'},
index: '/foo.txt',
assetGroups: [
{
Expand Down
11 changes: 6 additions & 5 deletions packages/service-worker/worker/test/prefetch_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ const db = new CacheDatabase(scope, scope);
log: (v, ctx = '') => console.error(v, ctx),
});
group = new PrefetchAssetGroup(
scope, scope, idle, manifest.assetGroups ![0], tmpHashTable(manifest), db, 'test');
scope, scope, idle, manifest.assetGroups ![0], this.manifest, tmpHashTable(manifest), db,
'test');
});
async_it('initializes without crashing', async() => { await group.initializeFully(); });
async_it('fully caches the two files', async() => {
Expand All @@ -58,8 +59,8 @@ const db = new CacheDatabase(scope, scope);
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');
freshScope, freshScope, idle, manifest.assetGroups ![0], this.manifest,
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);
Expand All @@ -80,8 +81,8 @@ const db = new CacheDatabase(scope, scope);
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');
badScope, badScope, idle, manifest.assetGroups ![0], this.manifest,
tmpHashTable(manifest), new CacheDatabase(badScope, badScope), 'test');
const err = await errorFrom(group.initializeFully());
expect(err.message).toContain('Hash mismatch');
});
Expand Down
1 change: 1 addition & 0 deletions packages/service-worker/worker/testing/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export function tmpManifestSingleAssetGroup(fs: MockFileSystem): Manifest {
return {
configVersion: 1,
index: '/index.html',
requestOptions: {credentials: 'omit'},
assetGroups: [
{
name: 'group',
Expand Down
1 change: 1 addition & 0 deletions packages/service-worker/worker/testing/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ export class ConfigBuilder {
configVersion: 1,
index: '/index.html', assetGroups,
navigationUrls: [], hashTable,
requestOptions: {credentials: 'omit'}
};
}
}
Expand Down