From 0a40ec5f1371631757805c36d4c201409581c5e0 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sat, 20 Jun 2026 11:02:57 +0200 Subject: [PATCH] fix(sub): re-add xhttp mode to extra JSON for Karing (#5446) Regression of #4364. Karing parses the `extra` JSON and ignores the flat `mode=` param, so when extra was present without `mode` it stored the transport with no mode and the handshake failed. The `mode` field that #4365 added to buildXhttpExtra was dropped during the share-link refactor; restore it in both the backend and frontend generators. --- frontend/src/lib/xray/inbound-link.ts | 4 ++++ internal/sub/service.go | 4 ++++ internal/sub/service_test.go | 6 ++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/xray/inbound-link.ts b/frontend/src/lib/xray/inbound-link.ts index 6e4da8ed5..4312aa8d3 100644 --- a/frontend/src/lib/xray/inbound-link.ts +++ b/frontend/src/lib/xray/inbound-link.ts @@ -47,6 +47,10 @@ function buildXhttpExtra(xhttp: XHttpStreamSettings | undefined): Record = {}; + if (typeof xhttp.mode === 'string' && xhttp.mode.length > 0) { + extra.mode = xhttp.mode; + } + if (typeof xhttp.xPaddingBytes === 'string' && xhttp.xPaddingBytes.length > 0) { extra.xPaddingBytes = xhttp.xPaddingBytes; } diff --git a/internal/sub/service.go b/internal/sub/service.go index 35b155d53..85c1ed687 100644 --- a/internal/sub/service.go +++ b/internal/sub/service.go @@ -1657,6 +1657,10 @@ func buildXhttpExtra(xhttp map[string]any) map[string]any { } extra := map[string]any{} + if mode, ok := xhttp["mode"].(string); ok && len(mode) > 0 { + extra["mode"] = mode + } + if xpb, ok := xhttp["xPaddingBytes"].(string); ok && len(xpb) > 0 { extra["xPaddingBytes"] = xpb } diff --git a/internal/sub/service_test.go b/internal/sub/service_test.go index 6f99d8400..b860e0449 100644 --- a/internal/sub/service_test.go +++ b/internal/sub/service_test.go @@ -369,8 +369,10 @@ func TestBuildXhttpExtra_IncludesClientSideFieldsWhenPresent(t *testing.T) { t.Fatalf("extra missing %q: %#v", key, extra) } } - if _, ok := extra["mode"]; ok { - t.Fatalf("mode should stay as a top-level query parameter, got extra %#v", extra) + // mode rides inside extra (in addition to the flat param) so clients + // that only read the extra JSON keep the xhttp mode (#5446). + if extra["mode"] != "packet-up" { + t.Fatalf("extra[mode] = %#v, want packet-up", extra["mode"]) } headers, ok := extra["headers"].(map[string]any)