From 04c3b53c71a85a8d43ac328a0e327bb36849a19d Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:55:09 -0700 Subject: [PATCH 1/2] Add Cloudflare Workers caching --- examples/workers/src/index.ts | 31 +++++++++++++++++++++++++++++- packages/server/src/handlers.ts | 17 ++++++++++++++-- packages/server/src/parser.test.ts | 2 +- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/examples/workers/src/index.ts b/examples/workers/src/index.ts index 64b589e..f1ed374 100755 --- a/examples/workers/src/index.ts +++ b/examples/workers/src/index.ts @@ -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 { + 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 { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { + if (request.method === 'GET') { + return handleWithCache(request, ctx); + } return handleRequest(request); }, }; diff --git a/packages/server/src/handlers.ts b/packages/server/src/handlers.ts index d39f3d4..6747c6d 100644 --- a/packages/server/src/handlers.ts +++ b/packages/server/src/handlers.ts @@ -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), @@ -21,6 +21,7 @@ export async function handleGet(req: Request) { return Response.json(res, { headers: { 'content-type': 'application/json', + ...headersToForward, ...CORS_HEADERS, }, }); @@ -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')) { @@ -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 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 { @@ -140,3 +143,13 @@ export async function findRedirect(url: string, body: unknown): Promise { 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)) { From 5a23da9ed05338bf7d7e3d6466869682e712bec1 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:58:14 -0700 Subject: [PATCH 2/2] Remove ogImage requirement --- packages/server/src/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/parser.ts b/packages/server/src/parser.ts index 7ac12fd..df124f5 100644 --- a/packages/server/src/parser.ts +++ b/packages/server/src/parser.ts @@ -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 = {};