import { useTranslation } from 'react-i18next'; import { AutoComplete, Input, InputNumber, Select, Switch } from 'antd'; import { useFormContext, useWatch } from 'react-hook-form'; import { HeaderMapEditor } from '@/components/form'; import { FormField } from '@/components/form/rhf'; import { validateSessionIDLength, validateSessionIDTable } from '@/lib/xray/xhttp-session-id'; import { int32RangeUpper } from '@/lib/xray/stream-wire-normalize'; import { XHTTP_SESSION_ID_TABLES } from '@/schemas/protocols/stream/xhttp'; import { MODE_OPTIONS } from '../outbound-form-constants'; interface XhttpFormProps { onXmuxToggle: (checked: boolean) => void; } function antdValidatorToRhf(fn: (rule: unknown, value: unknown) => Promise) { return async (value: unknown): Promise => { try { await fn(undefined, value); return true; } catch (e) { return (e as Error).message; } }; } const XH = 'streamSettings.xhttpSettings'; export default function XhttpForm({ onXmuxToggle }: XhttpFormProps) { const { t } = useTranslation(); const { control, getValues, setValue } = useFormContext(); const mode = useWatch({ control, name: `${XH}.mode` }) as string | undefined; const obfs = !!useWatch({ control, name: `${XH}.xPaddingObfsMode` }); const sessionPlacement = useWatch({ control, name: `${XH}.sessionIDPlacement` }) as string | undefined; const table = useWatch({ control, name: `${XH}.sessionIDTable` }); const seqPlacement = useWatch({ control, name: `${XH}.seqPlacement` }) as string | undefined; const uplinkDataPlacement = useWatch({ control, name: `${XH}.uplinkDataPlacement` }) as string | undefined; const enableXmux = !!useWatch({ control, name: `${XH}.enableXmux` }); function onXmuxMaxConcurrencyChange(value: unknown) { if (int32RangeUpper(value) <= 0) return; if (int32RangeUpper(getValues(`${XH}.xmux.maxConnections`)) > 0) { setValue(`${XH}.xmux.maxConnections`, 0); } } function onXmuxMaxConnectionsChange(value: unknown) { if (int32RangeUpper(value) <= 0) return; if (int32RangeUpper(getValues(`${XH}.xmux.maxConcurrency`)) > 0) { setValue(`${XH}.xmux.maxConcurrency`, ''); } } return ( <> {/* Padding obfs sub-section: gated by a Switch. When on, four extra knobs (key/header/placement/ method) tune how Xray injects random padding to disguise the post body shape. */} {obfs && ( <> )} {sessionPlacement && sessionPlacement !== 'path' && ( )} ({ value: v }))} placeholder="Base62" /> {!!table && ( )} )} {/* Mode-conditional sub-sections. */} {(mode === 'packet-up' || mode === 'auto') && ( <> )} )} {(mode === 'stream-up' || mode === 'stream-one') && ( )} {/* XMUX is the connection-multiplexing layer xHTTP uses to fan out parallel requests over a small pool of upstream connections. UI-only toggle (enableXmux) hides the 6 nested knobs when off. */} onXmuxToggle(v as boolean)} > {enableXmux && ( <> )} ); }