Skip to content

Commit

Permalink
Merge pull request #13 from open-frames/02-26-Add_Cloudflare_Workers_…
Browse files Browse the repository at this point in the history
…caching

Add Cloudflare Workers caching
  • Loading branch information
neekolas authored Feb 29, 2024
2 parents c72c086 + 5a23da9 commit 42c6601
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 5 deletions.
31 changes: 30 additions & 1 deletion examples/workers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,37 @@ import { handleRequest } from '@open-frames/proxy';

export interface Env {}

// Default cache to 1hr
const DEFAULT_CACHE_LENGTH = 60 * 60;

async function handleWithCache(request: Request, ctx: ExecutionContext): Promise<Response> {
const cacheUrl = new URL(request.url);

// Construct the cache key from the cache URL
const cacheKey = new Request(cacheUrl.toString(), request);
const cache = caches.default;
let response = await cache.match(cacheKey);
if (!response) {
console.log(`Cache miss for ${request.url}`);
response = await handleRequest(request);
console.log(response.headers);
const cacheControlValue = response.headers.get('cache-control');
if (!cacheControlValue) {
response.headers.append('cache-control', `s-maxage=${DEFAULT_CACHE_LENGTH}`);
}
ctx.waitUntil(cache.put(cacheKey, response.clone()));
} else {
console.log(`Cache hit for ${request.url}`);
}
return response;
}

export default {
async fetch(request: Request /** _env: Env, _ctx: ExecutionContext **/): Promise<Response> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
if (request.method === 'GET') {
return handleWithCache(request, ctx);
}
return handleRequest(request);
},
};
17 changes: 15 additions & 2 deletions packages/server/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function handleGet(req: Request) {
if (!url) {
return new Response('Missing url query param', { status: 400 });
}
const data = await downloadAndExtract(url);
const { data, headersToForward } = await downloadAndExtract(url);
const res: GetMetadataResponse = {
url,
extractedTags: metaTagsToObject(data),
Expand All @@ -21,6 +21,7 @@ export async function handleGet(req: Request) {
return Response.json(res, {
headers: {
'content-type': 'application/json',
...headersToForward,
...CORS_HEADERS,
},
});
Expand Down Expand Up @@ -71,6 +72,7 @@ export async function handleMedia(req: Request) {
const media = await fetch(url, {
headers: getProxySafeMediaHeaders(req),
});
// This will include the cache control headers
const mediaHeaders = Object.fromEntries(media.headers.entries());
const responseHeaders = new Headers({ ...mediaHeaders, ...CORS_HEADERS });
if (!responseHeaders.has('content-type')) {
Expand Down Expand Up @@ -113,10 +115,11 @@ export async function downloadAndExtract(url: string) {
throw new ErrorResponse(`Request to ${url} failed`, response.status);
}

const headersToForward = extractCacheHeaders(response.headers);
// TODO: Stream response until you see </head> and then stop
const text = await response.text();

return extractMetaTags(text);
return { data: extractMetaTags(text), headersToForward };
}

export async function findRedirect(url: string, body: unknown): Promise<PostRedirectResponse> {
Expand All @@ -140,3 +143,13 @@ export async function findRedirect(url: string, body: unknown): Promise<PostRedi
redirectedTo: location,
};
}

function extractCacheHeaders(headers: Headers): { [k: string]: string | string[] } {
const out: { [k: string]: string | string[] } = {};
const cacheControl = headers.get('cache-control');
if (cacheControl) {
out['cache-control'] = cacheControl;
}

return out;
}
2 changes: 1 addition & 1 deletion packages/server/src/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ describe('metadata parsing', () => {

for (const testCase of testCases) {
test(`can extract tags from ${testCase.file}`, async () => {
const metaTags = await downloadAndExtract(`http://localhost:${PORT}/${testCase.file}`);
const { data: metaTags } = await downloadAndExtract(`http://localhost:${PORT}/${testCase.file}`);

const extractedTags = metaTagsToObject(metaTags);
for (const [key, value] of Object.entries(testCase.expectedTags)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function extractMetaTags(html: string, tagPrefixes = TAG_PREFIXES) {
}, []);
}

const requiredFrameFields: (keyof OpenFrameResult)[] = ['acceptedClients', 'image', 'ogImage'];
const requiredFrameFields: (keyof OpenFrameResult)[] = ['acceptedClients', 'image'];

export function getFrameInfo(metaTags: MetaTag[]): OpenFrameResult | undefined {
const frameInfo: DeepPartial<OpenFrameResult> = {};
Expand Down

0 comments on commit 42c6601

Please sign in to comment.