Skip to content

Commit 4546d4f

Browse files
rozpuszczalnythePunderWoman
authored andcommitted
fix(service-worker): assign initializing client's app version, when a request is for worker script (#58131)
When a new version of app is available in a service worker, and a client with old version exists, web workers initialized from a client with old version will now be properly assigned with the same version. Before this change, a web worker was assigned with the newest version. Fixes #57971 PR Close #58131
1 parent 84ba1ad commit 4546d4f

File tree

4 files changed

+87
-6
lines changed

4 files changed

+87
-6
lines changed

packages/service-worker/worker/src/driver.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,14 @@ export class Driver implements Debuggable, UpdateSource {
700700
//
701701
// NOTE: For navigation requests, we care about the `resultingClientId`. If it is undefined or
702702
// the empty string (which is the case for sub-resource requests), we look at `clientId`.
703-
const clientId = event.resultingClientId || event.clientId;
703+
//
704+
// NOTE: If a request is a worker script, we should use the `clientId`, as worker is a part
705+
// of requesting client.
706+
const isWorkerScriptRequest =
707+
event.request.destination === 'worker' && event.resultingClientId && event.clientId;
708+
const clientId = isWorkerScriptRequest
709+
? event.clientId
710+
: event.resultingClientId || event.clientId;
704711
if (clientId) {
705712
// Check if there is an assigned client id.
706713
if (this.clientVersionMap.has(clientId)) {
@@ -729,6 +736,18 @@ export class Driver implements Debuggable, UpdateSource {
729736
appVersion = this.lookupVersionByHash(this.latestHash, 'assignVersion');
730737
}
731738

739+
if (isWorkerScriptRequest) {
740+
if (!this.clientVersionMap.has(event.resultingClientId)) {
741+
// New worker hasn't been seen before; set this client to requesting client version
742+
this.clientVersionMap.set(event.resultingClientId, hash);
743+
await this.sync();
744+
} else if (this.clientVersionMap.get(event.resultingClientId)! !== hash) {
745+
throw new Error(
746+
`Version mismatch between worker client ${event.resultingClientId} and requesting client ${clientId}`,
747+
);
748+
}
749+
}
750+
732751
// TODO: make sure the version is valid.
733752
return appVersion;
734753
} else {
@@ -763,6 +782,17 @@ export class Driver implements Debuggable, UpdateSource {
763782
throw new Error(`Invariant violated (assignVersion): latestHash was null`);
764783
}
765784

785+
if (isWorkerScriptRequest) {
786+
if (!this.clientVersionMap.has(event.resultingClientId)) {
787+
// New worker hasn't been seen before; set this client to latest hash as well
788+
this.clientVersionMap.set(event.resultingClientId, this.latestHash);
789+
} else if (this.clientVersionMap.get(event.resultingClientId)! !== this.latestHash) {
790+
throw new Error(
791+
`Version mismatch between worker client ${event.resultingClientId} and requesting client ${clientId}`,
792+
);
793+
}
794+
}
795+
766796
// Pin this client ID to the current latest version, indefinitely.
767797
this.clientVersionMap.set(clientId, this.latestHash);
768798
await this.sync();

packages/service-worker/worker/test/happy_spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,24 @@ import {envIsSupported} from '../testing/utils';
472472
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo v2');
473473
});
474474

475+
it('returns old content for worker initialized from old version', async () => {
476+
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
477+
expect(await makeRequest(scope, '/baz.txt')).toEqual('this is baz');
478+
await driver.initialized;
479+
480+
const client = scope.clients.getMock('default')!;
481+
expect(client.messages).toEqual([]);
482+
483+
scope.updateServerState(serverUpdate);
484+
expect(await driver.checkForUpdate()).toEqual(true);
485+
486+
// Worker request came from default client, old version should be served
487+
expect(await makeWorkerRequest(scope, '/foo.txt')).toEqual('this is foo');
488+
// Worker has been initialized from default client, further requests from worker should be served from old version
489+
expect(await makeRequest(scope, '/foo.txt', 'worker')).toEqual('this is foo');
490+
expect(await makeRequest(scope, '/baz.txt', 'worker')).toEqual('this is baz');
491+
});
492+
475493
it('handles empty client ID', async () => {
476494
// Initialize the SW.
477495
expect(await makeNavigationRequest(scope, '/foo/file1', '')).toEqual('this is foo');
@@ -2657,6 +2675,26 @@ async function makeRequest(
26572675
return null;
26582676
}
26592677

2678+
async function makeWorkerRequest(
2679+
scope: SwTestHarness,
2680+
url: string,
2681+
clientId = 'default',
2682+
resultingClientId = 'worker',
2683+
init?: Object,
2684+
): Promise<string | null> {
2685+
const [resPromise, done] = scope.handleFetch(
2686+
new MockRequest(url, {...init, destination: 'worker'}),
2687+
clientId,
2688+
resultingClientId,
2689+
);
2690+
await done;
2691+
const res = await resPromise;
2692+
if (res !== undefined && res.ok) {
2693+
return res.text();
2694+
}
2695+
return null;
2696+
}
2697+
26602698
function makeNavigationRequest(
26612699
scope: SwTestHarness,
26622700
url: string,

packages/service-worker/worker/testing/fetch.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ export class MockRequest extends MockBody implements Request {
122122

123123
url: string;
124124

125-
constructor(input: string | Request, init: RequestInit = {}) {
125+
constructor(
126+
input: string | Request,
127+
init: RequestInit & {destination?: RequestDestination} = {},
128+
) {
126129
super((init.body as string | null) ?? null);
127130
if (typeof input !== 'string') {
128131
throw 'Not implemented';
@@ -150,6 +153,9 @@ export class MockRequest extends MockBody implements Request {
150153
if (init.method !== undefined) {
151154
this.method = init.method;
152155
}
156+
if (init.destination !== undefined) {
157+
this.destination = init.destination;
158+
}
153159
}
154160

155161
clone(): Request {

packages/service-worker/worker/testing/scope.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,11 @@ export class SwTestHarnessImpl
192192
this.skippedWaiting = true;
193193
}
194194

195-
handleFetch(req: Request, clientId = ''): [Promise<Response | undefined>, Promise<void>] {
195+
handleFetch(
196+
req: Request,
197+
clientId = '',
198+
resultingClientId?: string,
199+
): [Promise<Response | undefined>, Promise<void>] {
196200
if (!this.eventHandlers.has('fetch')) {
197201
throw new Error('No fetch handler registered');
198202
}
@@ -203,9 +207,12 @@ export class SwTestHarnessImpl
203207
this.clients.add(clientId, isNavigation ? req.url : this.scopeUrl);
204208
}
205209

206-
const event = isNavigation
207-
? new MockFetchEvent(req, '', clientId)
208-
: new MockFetchEvent(req, clientId, '');
210+
const event =
211+
clientId && resultingClientId
212+
? new MockFetchEvent(req, clientId, resultingClientId)
213+
: isNavigation
214+
? new MockFetchEvent(req, '', clientId)
215+
: new MockFetchEvent(req, clientId, '');
209216
this.eventHandlers.get('fetch')!.call(this, event);
210217

211218
return [event.response, event.ready];

0 commit comments

Comments
 (0)