Skip to content

Commit

Permalink
Merge pull request #8 from michealroberts/feature/onRequestCorsMiddle…
Browse files Browse the repository at this point in the history
…ware

feat: Added onRequestCORSMiddleware() middleware utility.
  • Loading branch information
michealroberts authored Aug 30, 2023
2 parents 877d98c + 7bcec4c commit a2c1445
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export { corsEventHandler } from './corsEventHandler'

export { defineCORSEventHandler } from './defineCORSEventHandler'

export { onRequestCORSMiddleware, cors } from './onRequestCORSMiddleware'

/*****************************************************************************************************************/
46 changes: 46 additions & 0 deletions src/onRequestCORSMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*****************************************************************************************************************/

// @author Michael Roberts <michael@observerly.com>
// @package @observerly/nitro-cors
// @license Copyright © 2021-2023 observerly

/*****************************************************************************************************************/

import {
type H3CorsOptions,
getHeaders,
setHeader,
defineRequestMiddleware,
isCorsOriginAllowed
} from 'h3'

import { useCORS } from './internals/utils'

/*****************************************************************************************************************/

const defaultCORSOptions = {
origin: '*',
methods: '*'
} satisfies H3CorsOptions

/*****************************************************************************************************************/

export const onRequestCORSMiddleware = (options?: H3CorsOptions) =>
defineRequestMiddleware(async event => {
const corsOptions = options || defaultCORSOptions

useCORS(event, corsOptions)

const { origin } = getHeaders(event)

if (origin && isCorsOriginAllowed(origin, corsOptions)) {
setHeader(event, 'Origin', origin)
}
})

/*****************************************************************************************************************/

// Expose the onRequestCorsMiddleware as cors alias for ease of use:
export const cors = onRequestCORSMiddleware

/*****************************************************************************************************************/
131 changes: 131 additions & 0 deletions tests/mocks/corsOnRequestMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*****************************************************************************************************************/

// @author Michael Roberts <michael@observerly.com>
// @package @observerly/nitro-cors
// @license Copyright © 2021-2023 observerly

/*****************************************************************************************************************/

import { eventHandler, setHeaders } from 'h3'

import { type Handler } from '../shared/handler'

import { onRequestCORSMiddleware } from '../../src'

/*****************************************************************************************************************/

export const corsOnRequestMiddlewareHandlers: Handler[] = [
{
method: '*',
url: '/on-request-middleware/cors/allowed',
handler: eventHandler({
onRequest: onRequestCORSMiddleware({
origin: '*',
methods: '*'
}),
async handler(event) {
setHeaders(event, {
'X-CORS-Allowed': 'true'
})

return {
cors: true
}
}
})
},
{
method: '*',
url: '/on-request-middleware/cors/origin-match',
handler: eventHandler({
onRequest: onRequestCORSMiddleware({
origin: ['http://nitro-cors.unjs.io'],
methods: '*'
}),
async handler(event) {
setHeaders(event, {
'X-CORS-Allowed': 'true'
})

return {
cors: true
}
}
})
},
{
method: ['GET', 'OPTIONS'],
url: '/on-request-middleware/cors/origin-mismatch',
handler: eventHandler({
onRequest: onRequestCORSMiddleware({
origin: ['https://nitro.unjs.io'],
methods: ['GET', 'OPTIONS']
}),
async handler(event) {
setHeaders(event, {
'X-CORS-Allowed': 'false'
})

return {
cors: false
}
}
})
},
{
method: ['GET', 'OPTIONS'],
url: '/on-request-middleware/cors/method-match',
handler: eventHandler({
onRequest: onRequestCORSMiddleware({
origin: '*',
methods: ['GET', 'OPTIONS']
}),
async handler(event) {
setHeaders(event, {
'X-CORS-Allowed': 'true'
})

return {
cors: true
}
}
})
},
{
method: ['GET', 'OPTIONS'],
url: '/on-request-middleware/cors/method-mismatch',
handler: eventHandler({
onRequest: onRequestCORSMiddleware({
origin: '*',
methods: ['GET', 'OPTIONS']
}),
async handler(event) {
setHeaders(event, {
'X-CORS-Allowed': 'false'
})

return {
cors: false
}
}
})
},
{
method: 'GET',
url: '/on-request-middleware/cors/no-options-default-fallback-allowed',
handler: eventHandler({
onRequest: onRequestCORSMiddleware(),
async handler(event) {
setHeaders(event, {
'X-CORS-Allowed': 'true'
})

return {
cors: true
}
}
})
}
]

/*****************************************************************************************************************/
8 changes: 4 additions & 4 deletions tests/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

import { corsHandlers } from './cors'

import { corsOnRequestMiddlewareHandlers } from './corsOnRequestMiddleware'

import { type Handler } from '../shared/handler'

/*****************************************************************************************************************/

export const handlers: Handler[] = [
...corsHandlers
]
export const handlers: Handler[] = [...corsHandlers, ...corsOnRequestMiddlewareHandlers]

/*****************************************************************************************************************/
/*****************************************************************************************************************/
130 changes: 130 additions & 0 deletions tests/onRequestCORSMiddleware.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*****************************************************************************************************************/

// @author Michael Roberts <michael@observerly.com>
// @package @observerly/nitro-cors
// @license Copyright © 2021-2023 observerly

/*****************************************************************************************************************/

import { beforeEach, describe, expect, it, suite } from 'vitest'

import supertest, { type SuperTest, type Test } from 'supertest'

import { toNodeListener } from 'h3'

import { server } from './utilities/server'

import { onRequestCORSMiddleware, cors } from '../src'

/*****************************************************************************************************************/

suite('nitro-cors Internal Utils', () => {
describe('onRequestCORSMiddleware', () => {
let request: SuperTest<Test>

beforeEach(() => {
request = supertest(toNodeListener(server))
})

it('should be defined', () => {
expect(onRequestCORSMiddleware).toBeDefined()
})

it('should have an alias defined', () => {
expect(cors).toBeDefined()
})

it('should set CORS headers correctly on the request event when cors event handler is used with all origins allowed', async () => {
const res = await request
.get('/on-request-middleware/cors/allowed', {
method: 'GET'
})
.set('Origin', 'http://nitro-cors.unjs.io')

expect(res.header['x-cors-allowed']).toBe('true')
expect(res.header['origin']).toBe('http://nitro-cors.unjs.io')
expect(res.header['access-control-allow-origin']).toBe('*')
expect(res.body).toEqual({ cors: true })
})

it('should set CORS headers correctly on the request event when the CORS event handler is used with origin match', async () => {
const res = await request
.get('/on-request-middleware/cors/origin-match', {
method: 'GET'
})
.set('Origin', 'http://nitro-cors.unjs.io')

expect(res.header['x-cors-allowed']).toBe('true')
expect(res.header['origin']).toBe('http://nitro-cors.unjs.io')
expect(res.header['access-control-allow-origin']).toBe('http://nitro-cors.unjs.io')
expect(res.body).toEqual({ cors: true })
})

it('should set CORS headers correctly on the request event when the CORS event handler is used with method match', async () => {
const res = await request
.get('/on-request-middleware/cors/method-match', {
method: 'OPTIONS'
})
.set('Origin', 'http://nitro-cors.unjs.io')

expect(res.header['x-cors-allowed']).toBe('true')
expect(res.header['origin']).toBe('http://nitro-cors.unjs.io')
expect(res.header['access-control-allow-origin']).toBe('*')
expect(res.body).toEqual({ cors: true })
})

it('should set CORS headers correctly on the request event when the CORS event handler is used with method match', async () => {
const res = await request
.get('/on-request-middleware/cors/method-match', {
method: 'GET'
})
.set('Origin', 'http://nitro-cors.unjs.io')

expect(res.header['x-cors-allowed']).toBe('true')
expect(res.header['origin']).toBe('http://nitro-cors.unjs.io')
expect(res.header['access-control-allow-origin']).toBe('*')
expect(res.body).toEqual({ cors: true })
})

it('should set CORS headers correctly on the request event when the CORS event handler is used with origin mismatch', async () => {
const res = await request
.get('/on-request-middleware/cors/origin-mismatch', {
method: 'GET'
})
.set('Origin', 'http://nitro.unjs.io')

expect(res.header['x-cors-allowed']).toBe('false')
expect(res.header['origin']).toBe(undefined)
expect(res.header['access-control-allow-origin']).toBe(undefined)
expect(res.body).toEqual({ cors: false })
})

it('should set CORS headers correctly on the request event when the CORS event handler is used with method mismatch', async () => {
const res = await request
.get('/on-request-middleware/cors/method-mismatch', {
method: 'POST'
})
.set('Origin', 'http://nitro-cors.unjs.io')

expect(res.header['x-cors-allowed']).toBe('false')
expect(res.header['origin']).toBe('http://nitro-cors.unjs.io')
expect(res.header['access-control-allow-origin']).toBe('*')
expect(res.body).toEqual({ cors: false })
})

it('should set CORS headers correctly on the request event when default options standard event handler is used', async () => {
const res = await request
.get('/on-request-middleware/cors/no-options-default-fallback-allowed', {
method: 'GET'
})
.set('Origin', 'http://nitro-cors.unjs.io')

expect(res.header['x-cors-allowed']).toBe('true')
expect(res.header['origin']).toBe('http://nitro-cors.unjs.io')
expect(res.header['access-control-allow-origin']).toBe('*')
expect(res.body).toEqual({ cors: true })
})
})
})

/*****************************************************************************************************************/

0 comments on commit a2c1445

Please sign in to comment.