Merge pull request #907 from tinode/cors

Add CORS HTTP headers to regular (non-OPTIONS) requests
This commit is contained in:
Gene
2024-04-11 09:45:44 -07:00
committed by GitHub

View File

@ -80,6 +80,7 @@ func matchCORSOrigin(allowed []string, origin string) string {
return ""
}
// allowMethods must be in UPPERCASE.
func matchCORSMethod(allowMethods []string, method string) bool {
if method == "" {
// Request has no Method header.
@ -88,7 +89,7 @@ func matchCORSMethod(allowMethods []string, method string) bool {
method = strings.ToUpper(method)
for _, mm := range allowMethods {
if strings.ToUpper(mm) == method {
if mm == method {
return true
}
}
@ -96,41 +97,51 @@ func matchCORSMethod(allowMethods []string, method string) bool {
return false
}
// CORSHandler is the default preflight OPTIONS processor for use by media handlers.
// CORSHandler is the default CORS processor for use by media handlers.
func CORSHandler(req *http.Request, allowedOrigins []string, serve bool) (http.Header, int) {
if req.Method != http.MethodOptions {
// Not an OPTIONS request. No special handling for all other requests.
return nil, 0
}
var allowMethods []string
if serve {
allowMethods = []string{http.MethodGet, http.MethodHead, http.MethodOptions}
} else {
allowMethods = []string{http.MethodPost, http.MethodPut, http.MethodHead, http.MethodOptions}
}
headers := map[string][]string{
// Always add Vary because of possible intermediate caches.
"Vary": {"Origin", "Access-Control-Request-Method"},
"Access-Control-Allow-Headers": {"*"},
"Access-Control-Max-Age": {"86400"},
"Access-Control-Allow-Credentials": {"true"},
"Access-Control-Allow-Methods": {strings.Join(allowMethods, ", ")},
"Vary": {"Origin", "Access-Control-Request-Method, Access-Control-Request-Headers"},
}
if !matchCORSMethod(allowMethods, req.Header.Get("Access-Control-Request-Method")) {
// CORS policy does not allow this method.
origin := req.Header.Get("Origin")
allowedOrigin := matchCORSOrigin(allowedOrigins, origin)
requestMethod := req.Header.Get("Access-Control-Request-Method")
if req.Method == http.MethodOptions && requestMethod != "" {
// Preflight request.
if allowedOrigin == "" {
return headers, http.StatusNoContent
}
var allowMethods []string
if serve {
allowMethods = []string{http.MethodGet, http.MethodHead, http.MethodOptions}
} else {
allowMethods = []string{http.MethodPost, http.MethodPut, http.MethodHead, http.MethodOptions}
}
if !matchCORSMethod(allowMethods, requestMethod) {
// CORS policy does not allow this method.
return headers, http.StatusNoContent
}
headers["Access-Control-Allow-Headers"] = []string{"*"}
headers["Access-Control-Allow-Credentials"] = []string{"true"}
headers["Access-Control-Allow-Methods"] = []string{strings.Join(allowMethods, ", ")}
headers["Access-Control-Max-Age"] = []string{"86400"}
headers["Access-Control-Allow-Origin"] = []string{allowedOrigin}
return headers, http.StatusNoContent
}
allowedOrigin := matchCORSOrigin(allowedOrigins, req.Header.Get("Origin"))
if allowedOrigin == "" {
// CORS policy does not match the origin.
return headers, http.StatusNoContent
// Regular request, not a preflight.
if allowedOrigin != "" {
// Returning Origin from the actual request instead of '*', otherwise there could be an issue with Credentials.
headers["Access-Control-Allow-Origin"] = []string{origin}
}
headers["Access-Control-Allow-Origin"] = []string{allowedOrigin}
return headers, http.StatusNoContent
return headers, 0
}