fix(service-worker): Preserves HTTP cache mode in asset group requests

Ensures explicit HTTP cache mode from incoming requests is forwarded and maintained when creating fetch requests for assets, aligning with expected fetch behavior and preventing unintended cache handling.

(cherry picked from commit 31399c2171)
This commit is contained in:
SkyZeroZx
2026-05-24 12:47:23 -05:00
committed by Alex Rickabaugh
parent b8bd49341d
commit ca32fc1000
3 changed files with 36 additions and 1 deletions
+9 -1
View File
@@ -501,12 +501,16 @@ export abstract class AssetGroup {
* Create a new `Request` based on the specified URL and `RequestInit` options, preserving only
* metadata that are known to be safe.
*
* Currently, headers, redirect policy, and an explicit `credentials: 'omit'` are preserved.
* Currently, headers, redirect policy, an explicit `credentials: 'omit'`, and the HTTP cache
* mode are preserved.
*
* NOTE:
* `credentials: 'same-origin'` and `credentials: 'include'` are intentionally not preserved.
* Forwarding `'include'` could leak cookies to cross-origin asset hosts, and forwarding
* `'same-origin'` matches the default `fetch()` behavior so there is nothing to preserve.
* Requests with `cache: 'only-if-cached'` and `mode !== 'same-origin'` are short-circuited
* earlier in `Driver.onFetch()` (they are a known Chrome DevTools quirk), so no special
* handling for that combination is needed here.
* TODO(gkalpak):
* Investigate preserving more metadata. See, also, discussion on preserving `mode`:
* https://github.com/angular/angular/issues/41931#issuecomment-1227601347.
@@ -521,6 +525,10 @@ export abstract class AssetGroup {
init.credentials = 'omit';
}
if (options.cache !== undefined) {
init.cache = options.cache;
}
return this.adapter.newRequest(url, init);
}
@@ -1667,6 +1667,18 @@ import {envIsSupported} from '../testing/utils';
expect(bazReq.credentials).toBe('omit');
});
it(`passes 'cache' through to the server`, async () => {
// Request a lazy-cached asset (so that it is fetched from the network) and provide an
// explicit HTTP cache mode.
const reqInit = {cache: 'no-store'};
expect(await makeRequest(scope, '/baz.txt', undefined, reqInit)).toBe('this is baz');
// Verify that the explicit `cache` value was preserved (instead of being replaced by the
// default `'default'`).
const [bazReq] = server.getRequestsFor('/baz.txt');
expect(bazReq.cache).toBe('no-store');
});
describe('for redirect requests', () => {
it('passes headers through to the server', async () => {
// Request a redirected, lazy-cached asset (so that it is fetched from the network) and
@@ -1721,6 +1733,20 @@ import {envIsSupported} from '../testing/utils';
const [redirectReq] = server.getRequestsFor('/lazy/redirect-target.txt');
expect(redirectReq.credentials).toBe('omit');
});
it(`passes 'cache' through to the server`, async () => {
// Request a redirected, lazy-cached asset (so that it is fetched from the network) and
// provide an explicit HTTP cache mode.
const reqInit = {cache: 'no-store'};
expect(await makeRequest(scope, '/lazy/redirected.txt', undefined, reqInit)).toBe(
'this was a redirect too',
);
// Verify that the explicit `cache` value was preserved across the redirect
// reconstruction (instead of being replaced by the default `'default'`).
const [redirectReq] = server.getRequestsFor('/lazy/redirect-target.txt');
expect(redirectReq.cache).toBe('no-store');
});
});
});
@@ -167,6 +167,7 @@ export class MockRequest extends MockBody implements Request {
}
return new MockRequest(this.url, {
body: this._body,
cache: this.cache,
mode: this.mode,
credentials: this.credentials,
headers: this.headers,