Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions src/middleware/cors/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,17 +193,16 @@ describe('CORS by Middleware', () => {
).toBeFalsy()
})

it('Allow different Vary header value', async () => {
it('Should set Vary header with explicit origin', async () => {
const res = await app.request('http://localhost/api3/abc', {
headers: {
Vary: 'accept-encoding',
Origin: 'http://example.com',
},
})

expect(res.status).toBe(200)
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com')
expect(res.headers.get('Vary')).toBe('accept-encoding')
expect(res.headers.get('Vary')).toBe('Origin')
})

it('Allow origins by function', async () => {
Expand Down Expand Up @@ -310,4 +309,29 @@ describe('CORS by Middleware', () => {
expect(res2.headers.get('Access-Control-Allow-Origin')).toBe('*')
expect(res2.headers.get('Access-Control-Allow-Methods')).toBe('GET,HEAD')
})

it('Should preserve existing Vary header from response', async () => {
const app2 = new Hono()

app2.use('/api/*', cors({ origin: 'http://example.com' }))

app2.get('/api/test', (c) => {
// Controller sets Vary: Accept in response
c.header('Vary', 'Accept')
return c.json({ success: true })
})

const res = await app2.request('http://localhost/api/test', {
headers: {
Origin: 'http://example.com',
},
})

expect(res.status).toBe(200)
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com')
// Should preserve 'Accept' and add 'Origin'
const varyHeader = res.headers.get('Vary')
expect(varyHeader).toContain('Accept')
expect(varyHeader).toContain('Origin')
})
})
32 changes: 20 additions & 12 deletions src/middleware/cors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,6 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {
set('Access-Control-Allow-Origin', allowOrigin)
}

// Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard).
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
if (opts.origin !== '*') {
const existingVary = c.req.header('Vary')

if (existingVary) {
set('Vary', existingVary)
} else {
set('Vary', 'Origin')
}
}

if (opts.credentials) {
set('Access-Control-Allow-Credentials', 'true')
}
Expand All @@ -127,6 +115,11 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {
}

if (c.req.method === 'OPTIONS') {
// Set Vary header for OPTIONS requests before early return
if (opts.origin !== '*') {
set('Vary', 'Origin')
}

if (opts.maxAge != null) {
set('Access-Control-Max-Age', opts.maxAge.toString())
}
Expand Down Expand Up @@ -158,5 +151,20 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {
})
}
await next()

// Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard).
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
if (opts.origin !== '*') {
const existingVary = c.res.headers.get('Vary')

if (existingVary) {
// Preserve existing Vary header and append Origin
if (!existingVary.includes('Origin')) {
c.res.headers.set('Vary', `${existingVary}, Origin`)
}
} else {
c.res.headers.set('Vary', 'Origin')
}
}
}
}
Loading