From 78fa4cab13303d19b780b4a4a116dcf965082d03 Mon Sep 17 00:00:00 2001 From: Jonah Snider Date: Thu, 2 May 2024 17:18:45 -0700 Subject: [PATCH] feat(timing): allow crossOrigin in TimingOptions to be a function (#2359) * feat: allow crossOrigin in TimingOptions to be a function * chore: denoify * chore: undo accidental change * test: add tests --- bun.lockb | Bin 358186 -> 358882 bytes deno_dist/middleware/timing/index.ts | 10 +++- src/middleware/timing/index.test.ts | 85 +++++++++++++++++++++++++++ src/middleware/timing/index.ts | 10 +++- 4 files changed, 99 insertions(+), 6 deletions(-) diff --git a/bun.lockb b/bun.lockb index cbaee6d062f9fe9974e1fcfc6dea589d678d8778..78894db9b027bc7e0728f10f3aee195d6593a17b 100755 GIT binary patch delta 10145 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 X@}>pKCoWm>Zg%2^ce9fY+4X+`JAPkL delta 9456 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 TURS< boolean) totalDescription: string autoEnd: boolean - crossOrigin: boolean | string + crossOrigin: boolean | string | ((c: Context) => boolean | string) } const getTime = () => { @@ -64,10 +64,14 @@ export const timing = (config?: Partial): MiddlewareHandler => { if (enabled) { c.res.headers.append('Server-Timing', headers.join(',')) - if (options.crossOrigin) { + + const crossOrigin = + typeof options.crossOrigin === 'function' ? options.crossOrigin(c) : options.crossOrigin + + if (crossOrigin) { c.res.headers.append( 'Timing-Allow-Origin', - typeof options.crossOrigin === 'string' ? options.crossOrigin : '*' + typeof crossOrigin === 'string' ? crossOrigin : '*' ) } } diff --git a/src/middleware/timing/index.test.ts b/src/middleware/timing/index.test.ts index 079c7a18d..ae5eed34f 100644 --- a/src/middleware/timing/index.test.ts +++ b/src/middleware/timing/index.test.ts @@ -55,4 +55,89 @@ describe('Server-Timing API', () => { expect(res.headers.get('server-timing')?.includes(region)).toBeTruthy() expect(res.headers.get('server-timing')?.includes(regionDesc)).toBeTruthy() }) + + describe('Should handle crossOrigin setting', async () => { + it('Should do nothing when crossOrigin is falsy', async () => { + const crossOriginApp = new Hono() + + crossOriginApp.use( + '*', + timing({ + crossOrigin: false, + }) + ) + + crossOriginApp.get('/', (c) => c.text('/')) + + const res = await crossOriginApp.request('http://localhost/') + + expect(res).not.toBeNull() + expect(res.headers.has('server-timing')).toBeTruthy() + expect(res.headers.has('timing-allow-origin')).toBeFalsy() + }) + + it('Should set Timing-Allow-Origin to * when crossOrigin is true', async () => { + const crossOriginApp = new Hono() + + crossOriginApp.use( + '*', + timing({ + crossOrigin: true, + }) + ) + + crossOriginApp.get('/', (c) => c.text('/')) + + const res = await crossOriginApp.request('http://localhost/') + + expect(res).not.toBeNull() + expect(res.headers.has('server-timing')).toBeTruthy() + expect(res.headers.has('timing-allow-origin')).toBeTruthy() + expect(res.headers.get('timing-allow-origin')).toBe('*') + }) + + it('Should set Timing-Allow-Origin to the value of crossOrigin when it is a string', async () => { + const crossOriginApp = new Hono() + + crossOriginApp.use( + '*', + timing({ + crossOrigin: 'https://example.com', + }) + ) + + crossOriginApp.get('/', (c) => c.text('/')) + + const res = await crossOriginApp.request('http://localhost/') + + expect(res).not.toBeNull() + expect(res.headers.has('server-timing')).toBeTruthy() + expect(res.headers.has('timing-allow-origin')).toBeTruthy() + expect(res.headers.get('timing-allow-origin')).toBe('https://example.com') + }) + + it('Should set Timing-Allow-Origin to the return value of crossOrigin when it is a function', async () => { + const crossOriginApp = new Hono() + + crossOriginApp.use( + '*', + timing({ + crossOrigin: (c) => c.req.header('origin') ?? '*', + }) + ) + + crossOriginApp.get('/', (c) => c.text('/')) + + const res = await crossOriginApp.request('http://localhost/', { + headers: { + origin: 'https://example.com', + }, + }) + + expect(res).not.toBeNull() + expect(res.headers.has('server-timing')).toBeTruthy() + expect(res.headers.has('timing-allow-origin')).toBeTruthy() + expect(res.headers.get('timing-allow-origin')).toBe('https://example.com') + }) + }) }) diff --git a/src/middleware/timing/index.ts b/src/middleware/timing/index.ts index 356d7e9c1..504331b15 100644 --- a/src/middleware/timing/index.ts +++ b/src/middleware/timing/index.ts @@ -21,7 +21,7 @@ interface TimingOptions { enabled: boolean | ((c: Context) => boolean) totalDescription: string autoEnd: boolean - crossOrigin: boolean | string + crossOrigin: boolean | string | ((c: Context) => boolean | string) } const getTime = () => { @@ -64,10 +64,14 @@ export const timing = (config?: Partial): MiddlewareHandler => { if (enabled) { c.res.headers.append('Server-Timing', headers.join(',')) - if (options.crossOrigin) { + + const crossOrigin = + typeof options.crossOrigin === 'function' ? options.crossOrigin(c) : options.crossOrigin + + if (crossOrigin) { c.res.headers.append( 'Timing-Allow-Origin', - typeof options.crossOrigin === 'string' ? options.crossOrigin : '*' + typeof crossOrigin === 'string' ? crossOrigin : '*' ) } }