Skip to content

Commit

Permalink
fix: support max-stale on header interpreter (#543)
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurfiorette committed Jun 9, 2023
1 parent 82182bf commit 683dbe0
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 10 deletions.
19 changes: 15 additions & 4 deletions src/header/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const defaultHeaderInterpreter: HeaderInterpreter = (headers) => {
const cacheControl: unknown = headers[Header.CacheControl];

if (cacheControl) {
const { noCache, noStore, maxAge, immutable, staleWhileRevalidate } = parse(
const { noCache, noStore, maxAge, maxStale, immutable, staleWhileRevalidate } = parse(
String(cacheControl)
);

Expand All @@ -19,7 +19,8 @@ export const defaultHeaderInterpreter: HeaderInterpreter = (headers) => {

if (immutable) {
// 1 year is sufficient, as Infinity may cause problems with certain storages.
// It might not be the best way, but a year is better than none.
// It might not be the best way, but a year is better than none. Facebook shows
// that a browser session stays at the most 1 month.
return {
cache: 1000 * 60 * 60 * 24 * 365
};
Expand All @@ -33,8 +34,18 @@ export const defaultHeaderInterpreter: HeaderInterpreter = (headers) => {
? // If age is present, we must subtract it from maxAge
(maxAge - Number(age)) * 1000
: maxAge * 1000,
// Already out of date, for cache can be saved, but must be requested again
stale: staleWhileRevalidate !== undefined ? staleWhileRevalidate * 1000 : 0
// Already out of date, must be requested again
stale:
// I couldn't find any documentation about who should be used, as they
// are not meant to overlap each other. But, as we cannot request in the
// background, as the stale-while-revalidate says, and we just increase
// its staleTtl when its present, max-stale is being preferred over
// stale-while-revalidate.
maxStale !== undefined
? maxStale * 1000
: staleWhileRevalidate !== undefined
? staleWhileRevalidate * 1000
: undefined
};
}
}
Expand Down
1 change: 1 addition & 0 deletions src/interceptors/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export function defaultRequestInterceptor(axios: AxiosCacheInstance) {
}

// Hydrates any UI temporarily, if cache is available
/* istanbul ignore if 'really hard to test' */
if (cache.data) {
await config.cache.hydrate?.(cache);
}
Expand Down
4 changes: 2 additions & 2 deletions test/header/cache-control.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ describe('test Cache-Control header', () => {
});

// 10 Seconds in milliseconds
expect(result).toEqual({ cache: 10 * 1000, stale: 0 });
expect(result).toEqual({ cache: 10 * 1000, stale: undefined });
});

it('tests with max-age of 0', () => {
const result = defaultHeaderInterpreter({
[Header.CacheControl]: 'max-age=0'
});

expect(result).toEqual({ cache: 0, stale: 0 });
expect(result).toEqual({ cache: 0, stale:undefined });

Check failure on line 39 in test/header/cache-control.test.ts

View workflow job for this annotation

GitHub Actions / Build and test code

Insert `路`
});

it('tests stale values with age', () => {
Expand Down
34 changes: 32 additions & 2 deletions test/header/interpreter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('tests header interpreter', () => {
[Header.Age]: '3'
});

expect(result).toEqual({ cache: 7 * 1000, stale: 0 });
expect(result).toEqual({ cache: 7 * 1000, stale: undefined });
});

it('tests with expires and cache-control present', () => {
Expand All @@ -34,7 +34,7 @@ describe('tests header interpreter', () => {

// expires should be ignored
// 10 Seconds in milliseconds
expect(result).toEqual({ cache: 10 * 1000, stale: 0 });
expect(result).toEqual({ cache: 10 * 1000, stale: undefined });
});

it('tests with immutable', () => {
Expand Down Expand Up @@ -75,4 +75,34 @@ describe('tests header interpreter', () => {

expect(result.cached).toBe(true);
});

it('tests header interpreter stale with staleWhileRevalidate and maxStale', () => {
// only staleWhileRevalidate
expect(
defaultHeaderInterpreter({
[Header.CacheControl]: 'max-age=10, stale-while-revalidate=5'
})
).toEqual({ cache: 10 * 1000, stale: 5 * 1000 });

// only maxStale
expect(
defaultHeaderInterpreter({
[Header.CacheControl]: 'max-age=10, max-stale=4'
})
).toEqual({ cache: 10 * 1000, stale: 4 * 1000 });

// both should use max-stale
expect(
defaultHeaderInterpreter({
[Header.CacheControl]: 'max-age=10, stale-while-revalidate=5, max-stale=4'
})
).toEqual({ cache: 10 * 1000, stale: 4 * 1000 });

// none should return undefined
expect(
defaultHeaderInterpreter({
[Header.CacheControl]: 'max-age=10'
})
).toEqual({ cache: 10 * 1000, stale: undefined });
});
});
5 changes: 3 additions & 2 deletions test/mocks/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export function mockAxios(
const should304: unknown =
config.headers?.[Header.IfNoneMatch] || config.headers?.[Header.IfModifiedSince];
const status = should304 ? 304 : 200;
const statusText = should304 ? '304 Not Modified' : '200 OK';

if (config.validateStatus?.(status) === false) {
throw {
Expand All @@ -28,7 +29,7 @@ export function mockAxios(
response: {
data: true,
status,
statusText: should304 ? '304 Not Modified' : '200 OK',
statusText,
headers: {
...responseHeaders,
// Random header for every request made
Expand All @@ -43,7 +44,7 @@ export function mockAxios(
return {
data: true,
status,
statusText: should304 ? '304 Not Modified' : '200 OK',
statusText,
headers: {
...responseHeaders,
// Random header for every request made
Expand Down

0 comments on commit 683dbe0

Please sign in to comment.