diff --git a/v8env/src/fly/cache/index.ts b/v8env/src/fly/cache/index.ts index b8249964..9285d3d7 100644 --- a/v8env/src/fly/cache/index.ts +++ b/v8env/src/fly/cache/index.ts @@ -20,9 +20,9 @@ /** */ declare var bridge: any export interface CacheSetOptions { - ttl?: number, - tags?: string[], - onlyIfEmpty?: boolean + ttl?: number; + tags?: string[]; + onlyIfEmpty?: boolean; } diff --git a/v8env/src/fly/cache/response.ts b/v8env/src/fly/cache/response.ts index 27cb0901..e7cdba34 100644 --- a/v8env/src/fly/cache/response.ts +++ b/v8env/src/fly/cache/response.ts @@ -27,12 +27,16 @@ import cache, { CacheSetOptions } from "." */ export interface Metadata { status: number, - headers: any, + headers: { [key: string]: string | null}, at?: number, ttl: number, tags?: string[] } +export interface ResponseCacheSetOptions extends CacheSetOptions { + skipCacheHeaders?: string[] +} + /** * A response with cache info attached */ @@ -57,7 +61,7 @@ export async function get(key: string) { let age = 0; if (meta.at) { age = Math.round(Date.now() / 1000) - meta.at; - meta.headers.Age = age + meta.headers.Age = age.toString(); meta.headers['Fly-Age'] = meta.headers.Age; delete meta.at; } @@ -90,15 +94,9 @@ export function setMeta(key: string, meta: Metadata, options?: CacheSetOptions | return cache.set(key + ":meta", JSON.stringify(meta), options) } -const goodHeaders = [ - 'content-type', - 'content-length', - 'content-encoding', - 'cache-control', - 'expires', - 'link', - 'set-cookie', - 'etag' +const defaultSkipHeaders = [ + 'authorization', + 'set-cookie' ]; /** @@ -107,9 +105,15 @@ const goodHeaders = [ * @param resp The response to cache * @param options Time to live */ -export async function set(key: string, resp: Response, options?: CacheSetOptions | number) { - const ttl = typeof options === "number" ? options : (options && options.ttl) - const tags = typeof options === "object" ? options.tags : undefined +export async function set(key: string, resp: Response, options?: ResponseCacheSetOptions | number) { + const ttl = typeof options === "number" ? options : (options && options.ttl); + let tags: string[] | undefined = undefined; + let skipHeaderOption: string[] = defaultSkipHeaders; + if (typeof options === "object") { + tags = options.tags; + skipHeaderOption = [...skipHeaderOption, ...(options.skipCacheHeaders || []).map((headerKey) => headerKey.toLowerCase())]; + } + const meta = { status: resp.status, headers: {}, @@ -126,10 +130,18 @@ export async function set(key: string, resp: Response, options?: CacheSetOptions resp.headers.set("etag", etag) } - for (const h of goodHeaders) { - const v = resp.headers.getAll(h); - if (v && v.length > 0) { - meta.headers[h] = v.join(', '); + const skipHeaderSet = new Set(skipHeaderOption); + for(const headerSet of resp.headers as any) { + const [name, value] = headerSet; + if (skipHeaderSet.has(name.toLowerCase())) { + continue; + } + + const existingVal = meta.headers[name]; + if (existingVal) { + meta.headers[name] = `${existingVal}, ${value}`; + } else { + meta.headers[name] = value; } } return cacheResult([ diff --git a/v8env/test/fly.cache.response.spec.js b/v8env/test/fly.cache.response.spec.js index f043daaa..3222455e 100644 --- a/v8env/test/fly.cache.response.spec.js +++ b/v8env/test/fly.cache.response.spec.js @@ -3,10 +3,11 @@ import { expect } from 'chai' import cache, { responseCache } from "@fly/cache" let counter = 0 -async function makeResponse() { +async function makeResponse(initResponseOptions, setCacheOptions) { let key = `cache-test-key-${counter++}` - const resp = new Response("hi", { status: 404 }) - const setResult = await responseCache.set(key, resp) + const responseInitOptions = Object.assign({ status: 404 }, initResponseOptions) + const resp = new Response("hi", responseInitOptions) + const setResult = await responseCache.set(key, resp, setCacheOptions) expect(setResult).to.eq(true) return [key, resp] @@ -24,6 +25,47 @@ describe("@fly/cache/response", () => { expect(cachedResponse.status).to.eq(404) }) + it("sets a Response with headers", async () => { + const [key, resp] = await makeResponse({ headers: { "authorization": "foo", "content-type": "text/plain; charset=utf-8" } }) + const cachedResponse = await responseCache.get(key) + + expect(cachedResponse).instanceOf(Response) + const cachedBody = await cachedResponse.text() + const body = await resp.text() + + expect(cachedBody).to.eq(body) + expect(cachedResponse.status).to.eq(404) + + const meta = await responseCache.getMeta(key) + expect(meta.headers["authorization"]).to.eq(undefined) + expect(meta.headers["content-type"]).to.eq("text/plain; charset=utf-8") + }) + + it("sets a Response with headers, and custom skip-headers", async () => { + const [key, resp] = await makeResponse({ + headers: { + "authorization": "foo", + "content-type": "text/plain; charset=utf-8", + "content-length": "538", + "content-encoding": "gzip" + } + }, { skipCacheHeaders: ["content-length"] }) + const cachedResponse = await responseCache.get(key) + + expect(cachedResponse).instanceOf(Response) + const cachedBody = await cachedResponse.text() + const body = await resp.text() + + expect(cachedBody).to.eq(body) + expect(cachedResponse.status).to.eq(404) + + const meta = await responseCache.getMeta(key) + expect(meta.headers["authorization"]).to.eq(undefined) + expect(meta.headers["content-length"]).to.eq(undefined) + expect(meta.headers["content-type"]).to.eq("text/plain; charset=utf-8") + expect(meta.headers["content-encoding"]).to.eq("gzip") + }) + it("deletes a response", async () => { const [key, resp] = await makeResponse() const delResult = await responseCache.del(key)