forked from ant0ine/go-json-rest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cors.go
129 lines (108 loc) · 4.14 KB
/
cors.go
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
119
120
121
122
123
124
125
126
127
128
129
package rest
import (
"net/http"
"strings"
)
// Possible improvements:
// If AllowedMethods["*"] then Access-Control-Allow-Methods is set to the requested methods
// If AllowedHeaderss["*"] then Access-Control-Allow-Headers is set to the requested headers
// Put some presets in AllowedHeaders
// Put some presets in AccessControlExposeHeaders
// CorsMiddleware provides a configurable CORS implementation.
type CorsMiddleware struct {
allowedMethods map[string]bool
allowedHeaders map[string]bool
// Reject non CORS requests if true. See CorsInfo.IsCors.
RejectNonCorsRequests bool
// Function excecuted for every CORS requests to validate the Origin. (Required)
// Must return true if valid, false if invalid.
// For instance: simple equality, regexp, DB lookup, ...
OriginValidator func(origin string, request *Request) bool
// List of allowed HTTP methods. Note that the comparison will be made in uppercase
// to avoid common mistakes. And that the Access-Control-Allow-Methods response header
// also uses uppercase.
// (see CorsInfo.AccessControlRequestMethod)
AllowedMethods []string
// List of allowed HTTP Headers. Note that the comparison will be made with
// noarmalized names (http.CanonicalHeaderKey). And that the response header
// also uses normalized names.
// (see CorsInfo.AccessControlRequestHeaders)
AllowedHeaders []string
// List of headers used to set the Access-Control-Expose-Headers header.
AccessControlExposeHeaders []string
// User to se the Access-Control-Allow-Credentials response header.
AccessControlAllowCredentials bool
// Used to set the Access-Control-Max-Age response header, in seconds.
AccessControlMaxAge int
}
// MiddlewareFunc makes CorsMiddleware implement the Middleware interface.
func (mw *CorsMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc {
return func(writer ResponseWriter, request *Request) {
corsInfo := request.GetCorsInfo()
// non CORS requests
if !corsInfo.IsCors {
if mw.RejectNonCorsRequests {
Error(writer, "Non CORS request", http.StatusForbidden)
return
}
// continue, execute the wrapped middleware
handler(writer, request)
return
}
// Validate the Origin
if mw.OriginValidator(corsInfo.Origin, request) == false {
Error(writer, "Invalid Origin", http.StatusForbidden)
return
}
if corsInfo.IsPreflight {
// check the request methods
if mw.allowedMethods == nil {
mw.allowedMethods = map[string]bool{}
for _, allowedMethod := range mw.AllowedMethods {
mw.allowedMethods[strings.ToUpper(allowedMethod)] = true
}
}
if mw.allowedMethods[corsInfo.AccessControlRequestMethod] == false {
Error(writer, "Invalid Preflight Request", http.StatusForbidden)
return
}
// check the request headers
if mw.allowedHeaders == nil {
mw.allowedHeaders = map[string]bool{}
for _, allowedHeader := range mw.AllowedHeaders {
mw.allowedHeaders[http.CanonicalHeaderKey(allowedHeader)] = true
}
}
for _, requestedHeader := range corsInfo.AccessControlRequestHeaders {
if mw.allowedHeaders[requestedHeader] == false {
Error(writer, "Invalid Preflight Request", http.StatusForbidden)
return
}
}
for allowedMethod := range mw.allowedMethods {
writer.Header().Add("Access-Control-Allow-Methods", allowedMethod)
}
for allowedHeader := range mw.allowedHeaders {
writer.Header().Add("Access-Control-Allow-Headers", allowedHeader)
}
writer.Header().Set("Access-Control-Allow-Origin", corsInfo.Origin)
if mw.AccessControlAllowCredentials == true {
writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
writer.Header().Set("Access-Control-Max-Age", string(mw.AccessControlMaxAge))
writer.WriteHeader(http.StatusOK)
return
}
// Non-preflight requests
for _, exposed := range mw.AccessControlExposeHeaders {
writer.Header().Add("Access-Control-Expose-Headers", exposed)
}
writer.Header().Set("Access-Control-Allow-Origin", corsInfo.Origin)
if mw.AccessControlAllowCredentials == true {
writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
// continure, execute the wrapped middleware
handler(writer, request)
return
}
}