From 8046d1519dfaeb07a01619ae9db0f6f309a82305 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Thu, 28 May 2026 13:54:04 +0200 Subject: [PATCH] fix(links): include TCP HTTP host header in share links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inbound form intentionally only exposes the response side of the TCP HTTP header object (xray-core's inbound listener reads the response object, not request — see the existing comment in InboundFormModal). But the share-link generators were still reading the Host header from request.headers, so the configured value ended up in tcpSettings.header.response.headers while the link query emitted host= (empty). Fix the host lookup in both code paths: - sub/subService.go: applyShareNetworkParams (VLESS / Trojan / Shadowsocks share URLs) and applyVmessNetworkParams (the VMess base64 JSON link) now try header.response.headers first and fall back to request.headers for legacy / hand-edited configs. - frontend/src/lib/xray/inbound-link.ts mirrors the same fallback in the three TCP HTTP branches (VMess obj, VLESS params, the shared Trojan+Shadowsocks writer) so the JS-side generator used by the API docs preview stays in sync with the Go output. Also restore the request-side inputs (version / method / path / headers) under the TCP HTTP toggle in InboundFormModal. They were previously removed because xray-core ignores them on the inbound side, but they're still useful when copying the same config out to an outbound or hand-tuning the share link, and they no longer mislead users about Host — the link now derives Host from response.headers.host where the response-only form writes it. --- frontend/package-lock.json | 10 ++--- frontend/package.json | 2 +- frontend/src/lib/xray/inbound-link.ts | 12 ++++-- .../src/pages/inbounds/InboundFormModal.tsx | 42 +++++++++++++++++++ sub/subService.go | 26 ++++++++++-- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1daece88..9051fe56 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,7 +8,7 @@ "name": "3x-ui-frontend", "version": "0.1.0", "dependencies": { - "@ant-design/icons": "^6.2.3", + "@ant-design/icons": "^6.2.5", "@codemirror/lang-json": "^6.0.2", "@codemirror/theme-one-dark": "^6.1.3", "@tanstack/react-query": "^5.100.14", @@ -101,14 +101,14 @@ } }, "node_modules/@ant-design/icons": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.2.3.tgz", - "integrity": "sha512-Pl3aoAtxQeKryYnt6VvDJtOxMOtA8wrRSACe/pTjOAIG3fdHrWm6Ivb4ku9tsFjYroSXBKirvuxG4QkwBXD9gg==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.2.5.tgz", + "integrity": "sha512-0hKtoKqTjGFOndUyJLJmC9Cg6k4rEO7rLo6xmgbNJH+/ZX1C57RVals2v1j1knHl9n7Q+sBOveTvn931wLOCKw==", "license": "MIT", "dependencies": { "@ant-design/colors": "^8.0.1", "@ant-design/icons-svg": "^4.4.2", - "@rc-component/util": "^1.10.1", + "@rc-component/util": "^1.11.0", "clsx": "^2.1.1" }, "engines": { diff --git a/frontend/package.json b/frontend/package.json index 2110ba63..e2c3d782 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,7 +20,7 @@ "gen:zod": "cd .. && go run ./tools/openapigen" }, "dependencies": { - "@ant-design/icons": "^6.2.3", + "@ant-design/icons": "^6.2.5", "@codemirror/lang-json": "^6.0.2", "@codemirror/theme-one-dark": "^6.1.3", "@tanstack/react-query": "^5.100.14", diff --git a/frontend/src/lib/xray/inbound-link.ts b/frontend/src/lib/xray/inbound-link.ts index 07089dd1..2f2908d6 100644 --- a/frontend/src/lib/xray/inbound-link.ts +++ b/frontend/src/lib/xray/inbound-link.ts @@ -184,7 +184,9 @@ export function genVmessLink(input: GenVmessLinkInput): string { const request = header.request; if (request) { obj.path = request.path.join(','); - const host = getHeaderValue(request.headers, 'host'); + const host = + getHeaderValue(header.response?.headers, 'host') + || getHeaderValue(request.headers, 'host'); if (host) obj.host = host; } } @@ -309,7 +311,9 @@ export function genVlessLink(input: GenVlessLinkInput): string { const request = tcp.header.request; if (request) { params.set('path', request.path.join(',')); - const host = getHeaderValue(request.headers, 'host'); + const host = + getHeaderValue(tcp.header.response?.headers, 'host') + || getHeaderValue(request.headers, 'host'); if (host) params.set('host', host); params.set('headerType', 'http'); } @@ -387,7 +391,9 @@ function writeNetworkParams(stream: NonNullable, para const request = tcp.header.request; if (request) { params.set('path', request.path.join(',')); - const host = getHeaderValue(request.headers, 'host'); + const host = + getHeaderValue(tcp.header.response?.headers, 'host') + || getHeaderValue(request.headers, 'host'); if (host) params.set('host', host); params.set('headerType', 'http'); } diff --git a/frontend/src/pages/inbounds/InboundFormModal.tsx b/frontend/src/pages/inbounds/InboundFormModal.tsx index ad6f86b9..ad8373e7 100644 --- a/frontend/src/pages/inbounds/InboundFormModal.tsx +++ b/frontend/src/pages/inbounds/InboundFormModal.tsx @@ -1758,6 +1758,48 @@ export default function InboundFormModal({ if (headerType !== 'http') return null; return ( <> + + + + + + + ({ value: Array.isArray(v) ? v.join(',') : v })} + getValueFromEvent={(e) => { + const raw = (e?.target?.value ?? '') as string; + const parts = raw.split(',').map((s) => s.trim()).filter(Boolean); + return parts.length > 0 ? parts : ['/']; + }} + > + + + + +