Skip to content

Commit

Permalink
fix(service-worker): ensure initialization in handleMessage()
Browse files Browse the repository at this point in the history
- community members with this issue have tested this fix
  - @hsta verified that users have not seen the issue with this fix
    through all of August
- resolves "Invariant violated (initialize): latest hash null has no known manifest"

Fixes #25611
  • Loading branch information
Splaktar committed Oct 26, 2019
1 parent 29bc3a7 commit 0132797
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 26 deletions.
52 changes: 26 additions & 26 deletions packages/service-worker/worker/src/driver.ts
Expand Up @@ -295,7 +295,32 @@ export class Driver implements Debuggable, UpdateSource {
event.waitUntil(this.handleClick(event.notification, event.action));
}

private async ensureInitialized(): Promise<void> {
// Since the SW may have just been started, it may or may not have been initialized already.
// this.initialized will be null if initialization has not yet been attempted, or will be a
// Promise which will resolve (successfully or unsuccessfully) if it has.
if (this.initialized === null) {
// Initialization has not yet been attempted, so attempt it. This should only ever happen once
// per SW instantiation.
this.initialized = this.initialize();
}

// If initialization fails, the SW needs to enter a safe state, where it declines to respond to
// network requests.
try {
// Wait for initialization.
await this.initialized;
} catch (error) {
// Initialization failed. Enter a safe state.
this.state = DriverReadyState.SAFE_MODE;
this.stateMessage = `Initialization failed due to error: ${errorToString(error)}`;
throw error;
}
}

private async handleMessage(msg: MsgAny&{action: string}, from: Client): Promise<void> {
await this.ensureInitialized();

if (isMsgCheckForUpdates(msg)) {
const action = (async() => { await this.checkForUpdate(); })();
await this.reportStatus(from, action, msg.statusNonce);
Expand Down Expand Up @@ -383,32 +408,7 @@ export class Driver implements Debuggable, UpdateSource {
}

private async handleFetch(event: FetchEvent): Promise<Response> {
// Since the SW may have just been started, it may or may not have been initialized already.
// this.initialized will be `null` if initialization has not yet been attempted, or will be a
// Promise which will resolve (successfully or unsuccessfully) if it has.
if (this.initialized === null) {
// Initialization has not yet been attempted, so attempt it. This should only ever happen once
// per SW instantiation.
this.initialized = this.initialize();
}

// If initialization fails, the SW needs to enter a safe state, where it declines to respond to
// network requests.
try {
// Wait for initialization.
await this.initialized;
} catch (e) {
// Initialization failed. Enter a safe state.
this.state = DriverReadyState.SAFE_MODE;
this.stateMessage = `Initialization failed due to error: ${errorToString(e)}`;

// Even though the driver entered safe mode, background tasks still need to happen.
event.waitUntil(this.idle.trigger());

// Since the SW is already committed to responding to the currently active request,
// respond with a network fetch.
return this.safeFetch(event.request);
}
await this.ensureInitialized();

// On navigation requests, check for new updates.
if (event.request.mode === 'navigate' && !this.scheduledNavUpdateCheck) {
Expand Down
9 changes: 9 additions & 0 deletions packages/service-worker/worker/test/happy_spec.ts
Expand Up @@ -366,6 +366,15 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
server.assertNoOtherRequests();
});

it('initializes the service worker on fetch if it has not yet been initialized', async() => {
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
server.assertSawRequestFor('ngsw.json');
server.assertSawRequestFor('/foo.txt');
server.assertSawRequestFor('/bar.txt');
server.assertSawRequestFor('/redirected.txt');
server.assertNoOtherRequests();
});

it('handles non-relative URLs', async() => {
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
await driver.initialized;
Expand Down

0 comments on commit 0132797

Please sign in to comment.