From 41f6e4ddb9d3d2077ab2d8e6b8e0eac2d7a1e85e Mon Sep 17 00:00:00 2001 From: PuerNya Date: Thu, 4 Jan 2024 01:02:06 +0800 Subject: [PATCH] chore: add new cors response --- experimental/clashapi/server.go | 49 +++++++++++++++++++++++++++++++++ option/experimental.go | 15 +++++----- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index 1eec8448af..8a92198d97 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -6,6 +6,7 @@ import ( "errors" "net" "net/http" + "net/url" "os" "strings" "time" @@ -95,6 +96,18 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ AllowedHeaders: []string{"Content-Type", "Authorization"}, MaxAge: 300, }) + domainList := map[string]bool{ + "clash.razord.top": true, + "yacd.haishan.me": true, + "yacd.metacubex.one": true, + "d.metacubex.one": true, + "metacubex.github.io": true, + "metacubexd.pages.dev": true, + } + for _, domain := range options.TrustedDomain { + domainList[domain] = true + } + chiRouter.Use(setPrivateNetworkAccess(domainList)) chiRouter.Use(cors.Handler) chiRouter.Group(func(r chi.Router) { r.Use(authentication(options.Secret)) @@ -270,6 +283,42 @@ func castMetadata(metadata adapter.InboundContext) trafficontrol.Metadata { } } +func setPrivateNetworkAccess(domainList map[string]bool) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" && checkDomain(r, domainList) { + w.Header().Add("Access-Control-Allow-Private-Network", "true") + } + next.ServeHTTP(w, r) + }) + } +} + +func parseOriginHost(r *http.Request) string { + if referer := r.Header.Get("Referer"); referer != "" { + URL, err := url.Parse(referer) + if err == nil { + return URL.Host + } + } + if origin := r.Header.Get("Origin"); origin != "" { + URL, err := url.Parse(origin) + if err == nil { + return URL.Host + } + } + return "" +} + +func checkDomain(r *http.Request, list map[string]bool) bool { + host := parseOriginHost(r) + if host == "" { + return false + } + _, exists := list[host] + return exists +} + func authentication(serverSecret string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { diff --git a/option/experimental.go b/option/experimental.go index c685f51f54..a8e7e7fe49 100644 --- a/option/experimental.go +++ b/option/experimental.go @@ -15,13 +15,14 @@ type CacheFileOptions struct { } type ClashAPIOptions struct { - ExternalController string `json:"external_controller,omitempty"` - ExternalUI string `json:"external_ui,omitempty"` - ExternalUIDownloadURL string `json:"external_ui_download_url,omitempty"` - ExternalUIDownloadDetour string `json:"external_ui_download_detour,omitempty"` - Secret string `json:"secret,omitempty"` - DefaultMode string `json:"default_mode,omitempty"` - ModeList []string `json:"-"` + ExternalController string `json:"external_controller,omitempty"` + ExternalUI string `json:"external_ui,omitempty"` + ExternalUIDownloadURL string `json:"external_ui_download_url,omitempty"` + ExternalUIDownloadDetour string `json:"external_ui_download_detour,omitempty"` + TrustedDomain Listable[string] `json:"trusted_domain,omitempty"` + Secret string `json:"secret,omitempty"` + DefaultMode string `json:"default_mode,omitempty"` + ModeList []string `json:"-"` // Deprecated: migrated to global cache file CacheFile string `json:"cache_file,omitempty"`