Files
Nip-05-api/internal/http/middleware/cors.go

71 lines
1.6 KiB
Go

package middleware
import (
"net/http"
"net/url"
"strings"
"github.com/noderunners/nip05api/internal/config"
)
// CORS sends at most one Access-Control-Allow-Origin value (echo of request Origin).
// Configure FRONTEND_URL, optional CORS_ORIGINS, and CORS_ALLOW_LOCALHOST / CORS_ALLOW_CREDENTIALS.
func CORS(cfg *config.Config) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
if origin != "" && originAllowed(origin, cfg) {
h := w.Header()
h.Set("Access-Control-Allow-Origin", origin)
h.Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
h.Set("Access-Control-Allow-Headers", "Content-Type, X-API-Key, Authorization")
h.Set("Access-Control-Max-Age", "86400")
if cfg.CORSAllowCredentials {
h.Set("Access-Control-Allow-Credentials", "true")
}
}
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
}
func originAllowed(origin string, cfg *config.Config) bool {
if origin == "" {
return false
}
u, err := url.Parse(origin)
if err != nil || u.Scheme == "" || u.Host == "" {
return false
}
for _, allowed := range cfg.CORSExactOrigins() {
if origin == allowed {
return true
}
}
if cfg.CORSAllowLocalhost && isLoopbackOrigin(u) {
return true
}
return false
}
func isLoopbackOrigin(u *url.URL) bool {
host := strings.TrimSuffix(strings.ToLower(u.Hostname()), ".")
switch host {
case "localhost", "127.0.0.1", "::1":
return true
default:
return false
}
}