feat: interpreter also handles Expires header (and tests)

This commit is contained in:
Hazork 2021-09-13 17:46:26 -03:00
parent be5ee1ea8b
commit 288c11849c
3 changed files with 91 additions and 8 deletions

View File

@ -2,16 +2,30 @@ 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;
}
@ -19,5 +33,5 @@ export const defaultHeaderInterpreter: HeaderInterpreter = (headers) => {
return undefined;
}
return Date.now() + maxAge * 1000;
return maxAge * 1000;
};

View File

@ -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;

View File

@ -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());
});
});