148 lines
3.2 KiB
Go
148 lines
3.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"xboard-go/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func requestBaseURL(c *gin.Context) string {
|
|
if c != nil {
|
|
if origin := requestOrigin(c.Request); origin != "" {
|
|
return origin
|
|
}
|
|
}
|
|
if appURL := service.GetAppURL(); appURL != "" {
|
|
return strings.TrimRight(appURL, "/")
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func requestOrigin(r *http.Request) string {
|
|
if r == nil {
|
|
return ""
|
|
}
|
|
|
|
scheme, host := forwardedOrigin(r)
|
|
if scheme != "" && host != "" {
|
|
return scheme + "://" + host
|
|
}
|
|
|
|
if host == "" {
|
|
host = firstHeaderValue(r.Header.Get("X-Forwarded-Host"))
|
|
}
|
|
if host == "" {
|
|
host = strings.TrimSpace(r.Host)
|
|
}
|
|
if host == "" {
|
|
host = originHostFallback(r)
|
|
}
|
|
if host == "" {
|
|
return ""
|
|
}
|
|
|
|
if scheme == "" {
|
|
scheme = firstHeaderValue(r.Header.Get("X-Forwarded-Proto"))
|
|
}
|
|
if scheme == "" {
|
|
scheme = firstHeaderValue(r.Header.Get("X-Forwarded-Scheme"))
|
|
}
|
|
if scheme == "" {
|
|
scheme = firstHeaderValue(r.Header.Get("X-Scheme"))
|
|
}
|
|
if scheme == "" && strings.EqualFold(strings.TrimSpace(r.Header.Get("Front-End-Https")), "on") {
|
|
scheme = "https"
|
|
}
|
|
if scheme == "" && strings.EqualFold(strings.TrimSpace(r.Header.Get("X-Forwarded-Ssl")), "on") {
|
|
scheme = "https"
|
|
}
|
|
if scheme == "" {
|
|
switch strings.TrimSpace(firstHeaderValue(r.Header.Get("X-Forwarded-Port"))) {
|
|
case "443":
|
|
scheme = "https"
|
|
case "80":
|
|
scheme = "http"
|
|
}
|
|
}
|
|
if scheme == "" {
|
|
scheme = originSchemeFallback(r)
|
|
}
|
|
if scheme == "" {
|
|
if r.TLS != nil {
|
|
scheme = "https"
|
|
} else {
|
|
scheme = "http"
|
|
}
|
|
}
|
|
|
|
if forwardedPort := strings.TrimSpace(firstHeaderValue(r.Header.Get("X-Forwarded-Port"))); forwardedPort != "" && !strings.Contains(host, ":") {
|
|
defaultPort := map[string]string{
|
|
"http": "80",
|
|
"https": "443",
|
|
}[scheme]
|
|
if forwardedPort != defaultPort {
|
|
host = net.JoinHostPort(host, forwardedPort)
|
|
}
|
|
}
|
|
|
|
return scheme + "://" + host
|
|
}
|
|
|
|
func forwardedOrigin(r *http.Request) (scheme string, host string) {
|
|
forwarded := strings.TrimSpace(firstHeaderValue(r.Header.Get("Forwarded")))
|
|
if forwarded == "" {
|
|
return "", ""
|
|
}
|
|
|
|
for _, segment := range strings.Split(forwarded, ";") {
|
|
parts := strings.SplitN(strings.TrimSpace(segment), "=", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
key := strings.ToLower(strings.TrimSpace(parts[0]))
|
|
value := strings.Trim(strings.TrimSpace(parts[1]), "\"")
|
|
switch key {
|
|
case "proto":
|
|
if value != "" {
|
|
scheme = value
|
|
}
|
|
case "host":
|
|
if value != "" {
|
|
host = value
|
|
}
|
|
}
|
|
}
|
|
|
|
return scheme, host
|
|
}
|
|
|
|
func firstHeaderValue(value string) string {
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
parts := strings.Split(value, ",")
|
|
return strings.TrimSpace(parts[0])
|
|
}
|
|
|
|
func originHostFallback(r *http.Request) string {
|
|
for _, raw := range []string{r.Header.Get("Origin"), r.Header.Get("Referer")} {
|
|
if parsed, err := url.Parse(strings.TrimSpace(raw)); err == nil && parsed.Host != "" {
|
|
return parsed.Host
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func originSchemeFallback(r *http.Request) string {
|
|
for _, raw := range []string{r.Header.Get("Origin"), r.Header.Get("Referer")} {
|
|
if parsed, err := url.Parse(strings.TrimSpace(raw)); err == nil && parsed.Scheme != "" {
|
|
return parsed.Scheme
|
|
}
|
|
}
|
|
return ""
|
|
}
|