fix(links): include TCP HTTP host header in share links

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.
This commit is contained in:
MHSanaei
2026-05-28 13:54:04 +02:00
parent 2fea71387b
commit 8046d1519d
5 changed files with 79 additions and 13 deletions
+9 -3
View File
@@ -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<Inbound['streamSettings']>, 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');
}
@@ -1758,6 +1758,48 @@ export default function InboundFormModal({
if (headerType !== 'http') return null;
return (
<>
<Form.Item
label="Request version"
name={[
'streamSettings', 'tcpSettings', 'header',
'request', 'version',
]}
>
<Input placeholder="1.1" />
</Form.Item>
<Form.Item
label="Request method"
name={[
'streamSettings', 'tcpSettings', 'header',
'request', 'method',
]}
>
<Input placeholder="GET" />
</Form.Item>
<Form.Item
label="Request path"
name={[
'streamSettings', 'tcpSettings', 'header',
'request', 'path',
]}
getValueProps={(v) => ({ 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 : ['/'];
}}
>
<Input placeholder="/" />
</Form.Item>
<Form.Item
label="Request headers"
name={[
'streamSettings', 'tcpSettings', 'header',
'request', 'headers',
]}
>
<HeaderMapEditor mode="v2" />
</Form.Item>
<Form.Item
label="Response version"
name={[