feat(frontend): stream tab XHTTP section (Pattern A)

XHTTP is the heaviest network branch — 19 fields rendered conditionally
on mode, xPaddingObfsMode, and the three *Placement selectors. Each
gates its dependent field set via Form.useWatch.

Field structure mirrors the legacy XHTTPStreamSettings form 1:1:
- mode picker (auto / packet-up / stream-up / stream-one)
- packet-up adds scMaxBufferedPosts + scMaxEachPostBytes; stream-up
  adds scStreamUpServerSecs
- serverMaxHeaderBytes, xPaddingBytes, uplinkHTTPMethod (with the
  packet-up gate on the GET option)
- xPaddingObfsMode unlocks xPadding{Key,Header,Placement,Method}
- sessionPlacement / seqPlacement each unlock their respective Key
  field when set to anything other than 'path'
- packet-up mode additionally unlocks uplinkDataPlacement, and that
  in turn unlocks uplinkDataKey when the placement is not 'body'
- noSSEHeader Switch at the tail

XHTTP headers editor still pending (same WsHeaderMap as WS — will be
unified in the header-editor extraction commit).
This commit is contained in:
MHSanaei 2026-05-26 02:27:38 +02:00
parent 72c717bffd
commit 54a2d32343
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A

View file

@ -107,6 +107,11 @@ export default function InboundFormModalNew({
const mixedUdpOn = Form.useWatch(['settings', 'udp'], form) ?? false;
const network = Form.useWatch(['streamSettings', 'network'], form) ?? '';
const streamEnabled = canEnableStream({ protocol });
const xhttpMode = Form.useWatch(['streamSettings', 'xhttpSettings', 'mode'], form);
const xhttpObfsMode = Form.useWatch(['streamSettings', 'xhttpSettings', 'xPaddingObfsMode'], form) ?? false;
const xhttpSessionPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'sessionPlacement'], form);
const xhttpSeqPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'seqPlacement'], form);
const xhttpUplinkPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'uplinkDataPlacement'], form);
const wgSecretKey = Form.useWatch(['settings', 'secretKey'], form);
const wgPubKey = typeof wgSecretKey === 'string' && wgSecretKey.length > 0
? Wireguard.generateKeypair(wgSecretKey).publicKey
@ -866,6 +871,189 @@ export default function InboundFormModalNew({
</>
)}
{network === 'xhttp' && (
<>
<Form.Item name={['streamSettings', 'xhttpSettings', 'host']} label={t('host')}>
<Input />
</Form.Item>
<Form.Item name={['streamSettings', 'xhttpSettings', 'path']} label={t('path')}>
<Input />
</Form.Item>
<Form.Item name={['streamSettings', 'xhttpSettings', 'mode']} label="Mode">
<Select style={{ width: '50%' }}>
{(['auto', 'packet-up', 'stream-up', 'stream-one'] as const).map((m) => (
<Select.Option key={m} value={m}>{m}</Select.Option>
))}
</Select>
</Form.Item>
{xhttpMode === 'packet-up' && (
<>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'scMaxBufferedPosts']}
label="Max Buffered Upload"
>
<InputNumber />
</Form.Item>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'scMaxEachPostBytes']}
label="Max Upload Size (Byte)"
>
<Input />
</Form.Item>
</>
)}
{xhttpMode === 'stream-up' && (
<Form.Item
name={['streamSettings', 'xhttpSettings', 'scStreamUpServerSecs']}
label="Stream-Up Server"
>
<Input />
</Form.Item>
)}
<Form.Item
name={['streamSettings', 'xhttpSettings', 'serverMaxHeaderBytes']}
label="Server Max Header Bytes"
>
<InputNumber min={0} placeholder="0 (default)" />
</Form.Item>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'xPaddingBytes']}
label="Padding Bytes"
>
<Input />
</Form.Item>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'uplinkHTTPMethod']}
label="Uplink HTTP Method"
>
<Select>
<Select.Option value="">Default (POST)</Select.Option>
<Select.Option value="POST">POST</Select.Option>
<Select.Option value="PUT">PUT</Select.Option>
<Select.Option value="GET" disabled={xhttpMode !== 'packet-up'}>
GET (packet-up only)
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'xPaddingObfsMode']}
label="Padding Obfs Mode"
valuePropName="checked"
>
<Switch />
</Form.Item>
{xhttpObfsMode && (
<>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'xPaddingKey']}
label="Padding Key"
>
<Input placeholder="x_padding" />
</Form.Item>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'xPaddingHeader']}
label="Padding Header"
>
<Input placeholder="X-Padding" />
</Form.Item>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'xPaddingPlacement']}
label="Padding Placement"
>
<Select>
<Select.Option value="">Default (queryInHeader)</Select.Option>
<Select.Option value="queryInHeader">queryInHeader</Select.Option>
<Select.Option value="header">header</Select.Option>
<Select.Option value="cookie">cookie</Select.Option>
<Select.Option value="query">query</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'xPaddingMethod']}
label="Padding Method"
>
<Select>
<Select.Option value="">Default (repeat-x)</Select.Option>
<Select.Option value="repeat-x">repeat-x</Select.Option>
<Select.Option value="tokenish">tokenish</Select.Option>
</Select>
</Form.Item>
</>
)}
<Form.Item
name={['streamSettings', 'xhttpSettings', 'sessionPlacement']}
label="Session Placement"
>
<Select>
<Select.Option value="">Default (path)</Select.Option>
<Select.Option value="path">path</Select.Option>
<Select.Option value="header">header</Select.Option>
<Select.Option value="cookie">cookie</Select.Option>
<Select.Option value="query">query</Select.Option>
</Select>
</Form.Item>
{xhttpSessionPlacement && xhttpSessionPlacement !== 'path' && (
<Form.Item
name={['streamSettings', 'xhttpSettings', 'sessionKey']}
label="Session Key"
>
<Input placeholder="x_session" />
</Form.Item>
)}
<Form.Item
name={['streamSettings', 'xhttpSettings', 'seqPlacement']}
label="Sequence Placement"
>
<Select>
<Select.Option value="">Default (path)</Select.Option>
<Select.Option value="path">path</Select.Option>
<Select.Option value="header">header</Select.Option>
<Select.Option value="cookie">cookie</Select.Option>
<Select.Option value="query">query</Select.Option>
</Select>
</Form.Item>
{xhttpSeqPlacement && xhttpSeqPlacement !== 'path' && (
<Form.Item
name={['streamSettings', 'xhttpSettings', 'seqKey']}
label="Sequence Key"
>
<Input placeholder="x_seq" />
</Form.Item>
)}
{xhttpMode === 'packet-up' && (
<>
<Form.Item
name={['streamSettings', 'xhttpSettings', 'uplinkDataPlacement']}
label="Uplink Data Placement"
>
<Select>
<Select.Option value="">Default (body)</Select.Option>
<Select.Option value="body">body</Select.Option>
<Select.Option value="header">header</Select.Option>
<Select.Option value="cookie">cookie</Select.Option>
<Select.Option value="query">query</Select.Option>
</Select>
</Form.Item>
{xhttpUplinkPlacement && xhttpUplinkPlacement !== 'body' && (
<Form.Item
name={['streamSettings', 'xhttpSettings', 'uplinkDataKey']}
label="Uplink Data Key"
>
<Input placeholder="x_data" />
</Form.Item>
)}
</>
)}
<Form.Item
name={['streamSettings', 'xhttpSettings', 'noSSEHeader']}
label="No SSE Header"
valuePropName="checked"
>
<Switch />
</Form.Item>
</>
)}
{network === 'httpupgrade' && (
<>
<Form.Item