Files
kandev/apps/backend/internal/backendapp/middleware.go
T
2026-06-20 19:40:15 +01:00

78 lines
1.9 KiB
Go

package backendapp
import (
"net"
"net/http"
"net/url"
"strings"
"github.com/gin-gonic/gin"
)
// corsMiddleware returns a CORS middleware for HTTP and WebSocket connections.
func corsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if origin := c.Request.Header.Get("Origin"); origin != "" {
if !isAllowedCORSOrigin(origin, c.Request.Host) {
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusForbidden)
return
}
c.Next()
return
}
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Vary", "Origin")
c.Header("Access-Control-Allow-Credentials", "true")
} else {
c.Header("Access-Control-Allow-Origin", "*")
}
c.Header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization, Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version, Sec-WebSocket-Protocol")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
}
}
func isAllowedCORSOrigin(origin string, requestHost string) bool {
parsed, err := url.Parse(origin)
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
return false
}
if parsed.Scheme != "http" && parsed.Scheme != "https" {
return false
}
originHost := normalizeCORSHost(parsed.Hostname())
host := normalizeCORSHost(requestHost)
if originHost == "" || host == "" {
return false
}
return originHost == host || (isLoopbackCORSHost(originHost) && isLoopbackCORSHost(host))
}
func normalizeCORSHost(host string) string {
if parsedHost, _, err := net.SplitHostPort(host); err == nil {
host = parsedHost
}
return strings.ToLower(strings.Trim(host, "[]"))
}
func isLoopbackCORSHost(host string) bool {
if host == "localhost" {
return true
}
ip := net.ParseIP(host)
return ip != nil && ip.IsLoopback()
}