mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-15 09:06:07 +00:00
feat(xhttp): support sessionID* rename + sessionIDTable/Length (xray v26.6.22) (#5506)
* feat(xhttp): support sessionID* rename + sessionIDTable/Length (xray v26.6.22) xray-core v26.6.22 (PR #6258) renamed the XHTTP session config keys sessionPlacement/sessionKey to sessionIDPlacement/sessionIDKey (no fallback kept in core) and added sessionIDTable (predefined charset name or literal ASCII) and sessionIDLength (range, e.g. 16-32, lower bound > 0). Panel changes: - Schema (xhttp.ts): rename the two keys, add sessionIDTable/sessionIDLength, and a z.preprocess that lifts legacy keys off stored configs so an upgraded panel never silently drops a saved session setting. - Wire normalize + share-link build/parse: rename keys, emit the two new fields, and accept legacy sessionPlacement/sessionKey from old share links. - Inbound + outbound XHTTP forms: rename field paths, add a sessionIDTable autocomplete (9 predefined tables + free ASCII) and a sessionIDLength range input shown only when a table is set, with light client validation (ASCII table, length min > 0; xray enforces the room-size minimum server-side). - Subscription (service.go) and Clash (clash_service.go) builders: emit the renamed + new keys, with a legacy fallback for not-yet-resaved inbounds. - Locales: add sessionIDTable/sessionIDLength labels + hints in all 13 files. Two sibling v26.6.22 XHTTP commits need no panel change and are covered by the core bump alone: #6332 (XHTTP/3 closes QUIC/UDP) and #6320 (udpHop honors the existing dialerProxy). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(xhttp): add Session ID Table to inbound form-blocks snapshot The new sessionIDTable input renders by default in the inbound XHTTP form, so its label joins the field-structure snapshot. sessionIDLength stays conditional (only shown when a table is set), so it does not appear here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(xhttp): migrate legacy session keys in the running xray config The Zod preprocess plus the subscription/Clash fallbacks only covered the panel UI and share-link output. The config handed to the running xray-core process is built from the raw stored streamSettings in GetXrayConfig, which did not rewrite the renamed XHTTP session keys — so a pre-upgrade inbound (or template outbound) stored with a non-default sessionPlacement was emitted unchanged and dropped by xray-core v26.6.22, until the admin re-saved it. Lift sessionPlacement/sessionKey onto sessionIDPlacement/sessionIDKey at config-generation time, in the existing inbound stream-rewrite block (next to the tls/reality/externalProxy handling) and across template outbounds. The lift is idempotent and leaves unchanged configs byte-identical so the hot-reload diff never sees a spurious change. Also tighten validateSessionIDLength to reject an inverted range (e.g. 32-16) in addition to the existing lower-bound > 0 check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(xray): avoid summed-capacity allocation in mergeSubscriptionOutbounds CodeQL go/allocation-size-overflow flagged the pre-sized make() whose capacity was a sum of three slice lengths. Grow the slice via append on a nil slice instead; same result, no overflow-prone capacity expression.
This commit is contained in:
@@ -708,7 +708,7 @@ export default function InboundFormModal({
|
||||
// etc., not empty strings).
|
||||
// Seed each network's settings blob with its Zod schema defaults so
|
||||
// every Form.Item inside the network sub-form has a defined starting
|
||||
// value. XHTTP in particular has ~20 fields (sessionPlacement,
|
||||
// value. XHTTP in particular has ~20 fields (sessionIDPlacement,
|
||||
// seqPlacement, xPaddingMethod, uplinkHTTPMethod, ...) whose value
|
||||
// is the literal "" sentinel meaning "let xray-core pick its
|
||||
// default". Without seeding "", the Form.Item reads `undefined` and
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
|
||||
import { AutoComplete, Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
|
||||
|
||||
import { HeaderMapEditor } from '@/components/form';
|
||||
import type { InboundFormValues } from '@/schemas/forms/inbound-form';
|
||||
import { XHttpXmuxSchema } from '@/schemas/protocols/stream/xhttp';
|
||||
import { XHTTP_SESSION_ID_TABLES, XHttpXmuxSchema } from '@/schemas/protocols/stream/xhttp';
|
||||
import { validateSessionIDLength, validateSessionIDTable } from '@/lib/xray/xhttp-session-id';
|
||||
|
||||
const XMUX_DEFAULTS = XHttpXmuxSchema.parse({});
|
||||
|
||||
@@ -11,7 +12,8 @@ export default function XhttpForm({ form }: { form: FormInstance<InboundFormValu
|
||||
const { t } = useTranslation();
|
||||
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 xhttpSessionIDPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'sessionIDPlacement'], form);
|
||||
const xhttpSessionIDTable = Form.useWatch(['streamSettings', 'xhttpSettings', 'sessionIDTable'], form);
|
||||
const xhttpSeqPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'seqPlacement'], form);
|
||||
const xhttpUplinkPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'uplinkDataPlacement'], form);
|
||||
|
||||
@@ -163,7 +165,7 @@ export default function XhttpForm({ form }: { form: FormInstance<InboundFormValu
|
||||
</>
|
||||
)}
|
||||
<Form.Item
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionPlacement']}
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionIDPlacement']}
|
||||
label={t('pages.inbounds.form.sessionPlacement')}
|
||||
>
|
||||
<Select
|
||||
@@ -176,14 +178,36 @@ export default function XhttpForm({ form }: { form: FormInstance<InboundFormValu
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
{xhttpSessionPlacement && xhttpSessionPlacement !== 'path' && (
|
||||
{xhttpSessionIDPlacement && xhttpSessionIDPlacement !== 'path' && (
|
||||
<Form.Item
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionKey']}
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionIDKey']}
|
||||
label={t('pages.inbounds.form.sessionKey')}
|
||||
>
|
||||
<Input placeholder="x_session" />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionIDTable']}
|
||||
label={t('pages.inbounds.form.sessionIDTable')}
|
||||
tooltip={t('pages.inbounds.form.sessionIDTableHint')}
|
||||
rules={[{ validator: validateSessionIDTable }]}
|
||||
>
|
||||
<AutoComplete
|
||||
allowClear
|
||||
options={XHTTP_SESSION_ID_TABLES.map((v) => ({ value: v }))}
|
||||
placeholder="Base62"
|
||||
/>
|
||||
</Form.Item>
|
||||
{xhttpSessionIDTable && (
|
||||
<Form.Item
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionIDLength']}
|
||||
label={t('pages.inbounds.form.sessionIDLength')}
|
||||
tooltip={t('pages.inbounds.form.sessionIDLengthHint')}
|
||||
rules={[{ validator: validateSessionIDLength }]}
|
||||
>
|
||||
<Input placeholder="8-16" />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item
|
||||
name={['streamSettings', 'xhttpSettings', 'seqPlacement']}
|
||||
label={t('pages.inbounds.form.sequencePlacement')}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
|
||||
import { AutoComplete, Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
|
||||
|
||||
import { HeaderMapEditor } from '@/components/form';
|
||||
import { validateSessionIDLength, validateSessionIDTable } from '@/lib/xray/xhttp-session-id';
|
||||
import type { OutboundFormValues } from '@/schemas/forms/outbound-form';
|
||||
import { XHTTP_SESSION_ID_TABLES } from '@/schemas/protocols/stream/xhttp';
|
||||
|
||||
import { MODE_OPTIONS } from '../outbound-form-constants';
|
||||
|
||||
@@ -145,7 +147,7 @@ export default function XhttpForm({ form, onXmuxToggle }: XhttpFormProps) {
|
||||
only matters when placement is not 'path'. */}
|
||||
<Form.Item
|
||||
label={t('pages.inbounds.form.sessionPlacement')}
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionPlacement']}
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionIDPlacement']}
|
||||
>
|
||||
<Select
|
||||
placeholder="Default (path)"
|
||||
@@ -161,19 +163,49 @@ export default function XhttpForm({ form, onXmuxToggle }: XhttpFormProps) {
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{() => {
|
||||
const placement = form.getFieldValue([
|
||||
'streamSettings', 'xhttpSettings', 'sessionPlacement',
|
||||
'streamSettings', 'xhttpSettings', 'sessionIDPlacement',
|
||||
]);
|
||||
if (!placement || placement === 'path') return null;
|
||||
return (
|
||||
<Form.Item
|
||||
label={t('pages.inbounds.form.sessionKey')}
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionKey']}
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionIDKey']}
|
||||
>
|
||||
<Input placeholder="x_session" />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('pages.inbounds.form.sessionIDTable')}
|
||||
tooltip={t('pages.inbounds.form.sessionIDTableHint')}
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionIDTable']}
|
||||
rules={[{ validator: validateSessionIDTable }]}
|
||||
>
|
||||
<AutoComplete
|
||||
allowClear
|
||||
options={XHTTP_SESSION_ID_TABLES.map((v) => ({ value: v }))}
|
||||
placeholder="Base62"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{() => {
|
||||
const table = form.getFieldValue([
|
||||
'streamSettings', 'xhttpSettings', 'sessionIDTable',
|
||||
]);
|
||||
if (!table) return null;
|
||||
return (
|
||||
<Form.Item
|
||||
label={t('pages.inbounds.form.sessionIDLength')}
|
||||
tooltip={t('pages.inbounds.form.sessionIDLengthHint')}
|
||||
name={['streamSettings', 'xhttpSettings', 'sessionIDLength']}
|
||||
rules={[{ validator: validateSessionIDLength }]}
|
||||
>
|
||||
<Input placeholder="8-16" />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('pages.inbounds.form.sequencePlacement')}
|
||||
name={['streamSettings', 'xhttpSettings', 'seqPlacement']}
|
||||
|
||||
Reference in New Issue
Block a user