diff --git a/internal/proxy/mihomo.go b/internal/proxy/mihomo.go index ad9e3c5..d7af141 100644 --- a/internal/proxy/mihomo.go +++ b/internal/proxy/mihomo.go @@ -36,7 +36,21 @@ func isWebSocketUpgrade(r *http.Request) bool { if r == nil { return false } - return strings.EqualFold(r.Header.Get("Upgrade"), "websocket") + // Some clients/proxies pass comma-separated tokens or extra spaces. + // Treat request as WS only when both headers contain required upgrade tokens. + return headerHasToken(r.Header, "Connection", "upgrade") && + headerHasToken(r.Header, "Upgrade", "websocket") +} + +func headerHasToken(h http.Header, key, token string) bool { + for _, v := range h.Values(key) { + for _, part := range strings.Split(v, ",") { + if strings.EqualFold(strings.TrimSpace(part), token) { + return true + } + } + } + return false } func newMihomoWebSocketReverseProxy( diff --git a/internal/proxy/mihomo_test.go b/internal/proxy/mihomo_test.go index a38a7fb..41e1ec1 100644 --- a/internal/proxy/mihomo_test.go +++ b/internal/proxy/mihomo_test.go @@ -39,3 +39,18 @@ func TestMihomoForwardRewritesPathAndAuth(t *testing.T) { } assertSameURL(t, cap.got.URL, want) } + +func TestIsWebSocketUpgrade(t *testing.T) { + r1 := httptest.NewRequest(http.MethodGet, "http://gw/api/mtg/mihomo/traffic", nil) + r1.Header.Set("Connection", "keep-alive, Upgrade") + r1.Header.Set("Upgrade", "websocket") + if !isWebSocketUpgrade(r1) { + t.Fatal("expected websocket upgrade for tokenized headers") + } + + r2 := httptest.NewRequest(http.MethodGet, "http://gw/api/mtg/mihomo/traffic", nil) + r2.Header.Set("Upgrade", "websocket") + if isWebSocketUpgrade(r2) { + t.Fatal("expected non-websocket when Connection lacks upgrade") + } +}