-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCorsMiddleware.ts
118 lines (106 loc) · 2.76 KB
/
CorsMiddleware.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import {
MatchResultAny,
} from '../matchers/MatchResult'
import {
Handler,
} from '../Router'
export interface CorsMiddlewareInput {
headers: {
origin?: string
}
method: string
}
type HttpMethod =
| 'POST'
| 'GET'
| 'PUT'
| 'PATCH'
| 'DELETE'
| 'OPTIONS'
const DEFAULT_ALLOWED_METHODS: HttpMethod[] = [
'POST',
'GET',
'PUT',
'PATCH',
'DELETE',
'OPTIONS',
]
const DEFAULT_ALLOWED_HEADERS = [
'X-Requested-With',
'Access-Control-Allow-Origin',
'Content-Type',
'Authorization',
'Accept',
]
const DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 // 24 hours
export interface CorsMiddlewareCallbackResult {
// exact origins like 'http://0.0.0.0:8080' or '*'
origins: string[]
// methods like 'POST', 'GET' etc.
allowMethods?: HttpMethod[]
// headers like 'Authorization' or 'X-Requested-With'
allowHeaders?: string[]
// allows cookies in CORS scenario
allowCredentials?: boolean
// max age in seconds
maxAge?: number
}
export function CorsMiddleware<
D2,
R extends CorsMiddlewareInput = CorsMiddlewareInput
>(callback: (
req: R,
origin: string,
params: { data: D2 }
) => Promise<CorsMiddlewareCallbackResult>) {
return function corsWrapper<T extends MatchResultAny, D extends D2 & {
req: R
res: {
writableEnded: boolean
setHeader:(name: string, value: string) => void
statusCode: number
end: () => void
}
}>(
handler: Handler<T, D>): Handler<T, D> {
return async function corsHandler(params) {
const { req, res } = params.data
// avoid "Cannot set headers after they are sent to the client"
if (res.writableEnded) {
// TODO: not sure if handler should be called
return handler(params)
}
const origin = req.headers.origin ?? ''
const config = await callback(req, origin, params)
const allowCredentials = config.allowCredentials ?? true
const allowMethods = config.allowMethods ?? DEFAULT_ALLOWED_METHODS
const allowHeaders = config.allowHeaders ?? DEFAULT_ALLOWED_HEADERS
const maxAge = config.maxAge ?? DEFAULT_MAX_AGE_SECONDS
if (config.origins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin)
res.setHeader('Vary', 'Origin')
if (allowCredentials) {
res.setHeader('Access-Control-Allow-Credentials', 'true')
}
}
if (req.method === 'OPTIONS') {
if (allowMethods.length) {
res.setHeader('Access-Control-Allow-Methods', allowMethods.join(','))
}
if (allowHeaders.length) {
res.setHeader('Access-Control-Allow-Headers', allowHeaders.join(','))
}
if (maxAge) {
res.setHeader('Access-Control-Max-Age', String(maxAge))
}
// no further processing of preflight requests
res.statusCode = 200
res.end()
// eslint-disable-next-line consistent-return
return
}
const result = await handler(params)
return result
}
}
}