Skip to content

Commit

Permalink
feat: interpreter also handles Expires header (and tests)
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurfiorette committed Sep 13, 2021
1 parent be5ee1e commit 288c118
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 8 deletions.
22 changes: 18 additions & 4 deletions src/header/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,36 @@ import { parse } from '@tusbar/cache-control';
import { HeaderInterpreter } from './types';

export const defaultHeaderInterpreter: HeaderInterpreter = (headers) => {
const cacheControl = headers?.['cache-control'];
const cacheControl = headers?.['Cache-Control'];

if (!cacheControl) {
// Checks if Expires header is present
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires
const expires = headers?.['Expires'];

if (expires) {
const milliseconds = Date.parse(expires) - Date.now();

if (milliseconds > 0) {
return milliseconds;
} else {
return false;
}
}

return undefined;
}

const { noCache, noStore, maxAge } = parse(cacheControl);
const { noCache, noStore, mustRevalidate, maxAge } = parse(cacheControl);

// Header told that this response should not be cached.
if (noCache || noStore) {
if (noCache || noStore || mustRevalidate) {
return false;
}

if (!maxAge) {
return undefined;
}

return Date.now() + maxAge * 1000;
return maxAge * 1000;
};
6 changes: 2 additions & 4 deletions src/header/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
*
* @returns `false` if cache should not be used. `undefined` when provided
* headers was not enough to determine a valid value. Or a `number` containing
* the number of **seconds** to cache the response.
* the number of **milliseconds** to cache the response.
*/
export type HeaderInterpreter = (
headers: Record<string, string> | undefined
) => false | undefined | number;
export type HeaderInterpreter = (headers?: Record<string, string>) => false | undefined | number;
71 changes: 71 additions & 0 deletions test/header/interpreter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { defaultHeaderInterpreter } from '../../src/header';

describe('tests header interpreter', () => {
it('tests without cache-control header', () => {
const noHeader = defaultHeaderInterpreter({});
expect(noHeader).toBeUndefined();

const emptyHeader = defaultHeaderInterpreter({ 'Cache-Control': 'public' });
expect(emptyHeader).toBeUndefined();
});

it('tests with cache preventing headers', () => {
const noStore = defaultHeaderInterpreter({
'Cache-Control': 'no-store'
});

expect(noStore).toBe(false);

const noCache = defaultHeaderInterpreter({
'Cache-Control': 'no-cache'
});

expect(noCache).toBe(false);

const mustRevalidate = defaultHeaderInterpreter({
'Cache-Control': 'must-revalidate'
});

expect(mustRevalidate).toBe(false);
});

it('tests with maxAge header for 10 seconds', () => {
const result = defaultHeaderInterpreter({
'Cache-Control': 'max-age=10'
});

// 10 Seconds in milliseconds
expect(result).toBe(10 * 1000);
});

it('tests with Expires and Cache-Control present', () => {
const result = defaultHeaderInterpreter({
'Cache-Control': 'max-age=10',
Expires: new Date(new Date().getFullYear() + 1, 1, 1).toISOString()
});

// Expires should be ignored
// 10 Seconds in milliseconds
expect(result).toBe(10 * 1000);
});

it('tests with past Expires', () => {
const result = defaultHeaderInterpreter({
Expires: new Date(new Date().getFullYear() - 1, 1, 1).toISOString()
});

// Past means cache invalid
expect(result).toBe(false);
});

it('tests with future Expires', () => {
const date = new Date(new Date().getFullYear() + 1, 1, 1);
const result = defaultHeaderInterpreter({
Expires: date.toISOString()
});

// the result should be what the date is in milliseconds
// minus the actual epoch milliseconds
expect(result).toBeCloseTo(date.getTime() - Date.now());
});
});

0 comments on commit 288c118

Please sign in to comment.