Skip to content

Commit

Permalink
remember to use a custom header for all cache-related headers
Browse files Browse the repository at this point in the history
  • Loading branch information
mhuebert committed Nov 7, 2024
1 parent 4915133 commit a474d88
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 45 deletions.
66 changes: 42 additions & 24 deletions private-website-cache/src/worker.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,56 @@
export default {
async fetch(request, env, ctx) {
// Add ETag and If-Modified-Since handling
const ifNoneMatch = request.headers.get('If-None-Match');
const ifModifiedSince = request.headers.get('If-Modified-Since');
const response = await fetch(request);

// Check both ETag and Last-Modified
if ((ifNoneMatch && response.headers.get('ETag') === ifNoneMatch) ||
(ifModifiedSince && response.headers.get('Last-Modified') === ifModifiedSince)) {
return new Response(null, {
status: 304,
headers: new Headers({
'Cache-Control': response.headers.get('Cache-Control'),
'ETag': response.headers.get('ETag'),
'Last-Modified': response.headers.get('Last-Modified')
})
});
}

// Check for custom cache policy
const cachePolicyHeader = response.headers.get('X-Cache-Policy');
if (cachePolicyHeader) {
// Check for custom cache info
const cacheInfoHeader = response.headers.get('X-Cache-Info');
if (cacheInfoHeader) {
try {
const policy = JSON.parse(cachePolicyHeader);
const cacheInfo = JSON.parse(cacheInfoHeader);
const modifiedResponse = new Response(response.body, response);

// Clear any existing cache headers that might have been set by IAP
modifiedResponse.headers.delete('Pragma');
modifiedResponse.headers.delete('Cache-Control');
modifiedResponse.headers.delete('Expires');
modifiedResponse.headers.set('Cache-Control', `${policy.visibility}, max-age=${policy.maxAge}`);
const expiresDate = new Date(Date.now() + policy.maxAge * 1000);
modifiedResponse.headers.set('Expires', expiresDate.toUTCString());
modifiedResponse.headers.delete('ETag');
modifiedResponse.headers.delete('Last-Modified');

// Restore cache control headers
const { policy, expires, etag, lastModified } = cacheInfo;
modifiedResponse.headers.set(
'Cache-Control',
`${policy.visibility}, max-age=${policy.maxAge}, stale-while-revalidate=${policy.staleWhileRevalidate}`
);
modifiedResponse.headers.set('Expires', expires);

// Restore validation headers
if (etag) {
modifiedResponse.headers.set('ETag', etag);
}
if (lastModified) {
modifiedResponse.headers.set('Last-Modified', lastModified);
}

// Handle conditional requests
const ifNoneMatch = request.headers.get('If-None-Match');
const ifModifiedSince = request.headers.get('If-Modified-Since');

if ((ifNoneMatch && etag === ifNoneMatch) ||
(ifModifiedSince && lastModified === ifModifiedSince)) {
return new Response(null, {
status: 304,
headers: new Headers({
'Cache-Control': modifiedResponse.headers.get('Cache-Control'),
'ETag': etag,
'Last-Modified': lastModified
})
});
}

return modifiedResponse;
} catch (e) {
console.error('Failed to parse cache policy:', e);
console.error('Failed to parse cache info:', e);
}
}

Expand Down
48 changes: 27 additions & 21 deletions src/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import * as assert from 'assert'

const { BUCKET_NAME } = process.env;

const HTML_MAX_AGE = 60;

export const app = express();
const storage = new Storage();
const default_bucket = storage.bucket(BUCKET_NAME);
Expand Down Expand Up @@ -49,30 +47,43 @@ const handleResponseError = (res, error) => {
}

const CACHE_POLICIES = {
HTML: { maxAge: 60, visibility: 'private' },
STATIC: { maxAge: 600, visibility: 'public' },
SIGNED_URL: { maxAge: 3000, visibility: 'public' },
HTML: { maxAge: 60, visibility: 'private', staleWhileRevalidate: 30 },
STATIC: { maxAge: 86400, visibility: 'public', staleWhileRevalidate: 3600 }, // 24 hours for static assets
SIGNED_URL: { maxAge: 3000, visibility: 'public', staleWhileRevalidate: 300 },
};

const setCacheHeaders = (res, policy) => {
res.setHeader('X-Cache-Policy', JSON.stringify(policy));
res.setHeader('Cache-Control', `${policy.visibility}, max-age=${policy.maxAge}`);
res.setHeader('Expires', new Date(Date.now() + policy.maxAge * 1000).toUTCString());
const setCacheHeaders = (res, policy, metadata = {}) => {
// Consolidate all cache-related information into a single custom header
const cacheInfo = {
policy,
expires: new Date(Date.now() + policy.maxAge * 1000).toUTCString(),
etag: metadata.etag,
lastModified: metadata.updated,
contentType: metadata.contentType,
contentLength: metadata.size
};

res.setHeader('X-Cache-Info', JSON.stringify(cacheInfo));

// Set standard headers anyway (for local development)
res.setHeader('Cache-Control', `${policy.visibility}, max-age=${policy.maxAge}, stale-while-revalidate=${policy.staleWhileRevalidate}`);
res.setHeader('Expires', cacheInfo.expires);
if (metadata.etag) {
res.setHeader('ETag', metadata.etag);
}
if (metadata.updated) {
res.setHeader('Last-Modified', metadata.updated);
}
};

const serveFile = async (res, path) => {
try {
const file = default_bucket.file(path);
const [metadata] = await file.getMetadata();

setCacheHeaders(res, CACHE_POLICIES.STATIC);
setCacheHeaders(res, CACHE_POLICIES.STATIC, metadata);
res.setHeader('Content-Type', metadata.contentType);
res.setHeader('Content-Length', metadata.size);
res.setHeader('Last-Modified', metadata.updated);

if (metadata.etag) {
res.setHeader('ETag', metadata.etag);
}

const stream = file.createReadStream();
stream.on('error', (err) => handleResponseError(res, err));
Expand All @@ -87,13 +98,8 @@ const serveHtml = async (res, path) => {
const htmlFile = default_bucket.file(path);
const [metadata] = await htmlFile.getMetadata();

setCacheHeaders(res, CACHE_POLICIES.HTML, metadata);
res.setHeader('Content-Type', 'text/html');
res.setHeader('Last-Modified', metadata.updated);
setCacheHeaders(res, CACHE_POLICIES.HTML);

if (metadata.etag) {
res.setHeader('ETag', metadata.etag);
}

return new Promise((resolve, reject) => {
let rejectLogged = (err) => {
Expand Down

0 comments on commit a474d88

Please sign in to comment.