fix(ui): make the Happy Eyeballs toggle produce a config xray actually enables

Toggling Happy Eyeballs on filled the object with schema defaults, and
tryDelayMs defaulted to 0. That broke the feature twice over: xray-core
treats tryDelayMs=0 as happy-eyeballs-off, and the wire normalizer
strips every field that equals its default, leaving an empty object it
then deletes - so the switch silently flipped back off on reopen (the
"disabled when Prefer IPv6 is off" symptom; prioritizeIPv6=true was the
one non-default that let the object survive). Default tryDelayMs to the
recommended 250ms so an enabled config survives serialization and is
functional in the core.

Closes #5780
This commit is contained in:
MHSanaei
2026-07-05 20:12:34 +02:00
parent 0add63984f
commit 579a9daaa0
2 changed files with 10 additions and 1 deletions
@@ -33,7 +33,7 @@ export const AddressPortStrategySchema = z.enum([
export type AddressPortStrategy = z.infer<typeof AddressPortStrategySchema>;
export const HappyEyeballsSchema = z.object({
tryDelayMs: z.number().int().min(0).default(0),
tryDelayMs: z.number().int().min(0).default(250),
prioritizeIPv6: z.boolean().default(false),
interleave: z.number().int().min(1).default(1),
maxConcurrentTry: z.number().int().min(0).default(4),
@@ -10,6 +10,7 @@ import {
validateRealityTarget,
} from '@/lib/xray/stream-wire-normalize';
import { InboundFormSchema } from '@/schemas/forms/inbound-form';
import { HappyEyeballsSchema } from '@/schemas/protocols/stream/sockopt';
import type { InboundFormValues } from '@/schemas/forms/inbound-form';
import { XHttpXmuxSchema } from '@/schemas/protocols/stream/xhttp';
@@ -204,6 +205,14 @@ describe('normalizeSockoptForWire', () => {
});
expect(out?.domainStrategy).toBe('UseIP');
});
it('keeps a freshly toggled happyEyeballs (schema defaults) across the wire round trip', () => {
const out = normalizeSockoptForWire({
happyEyeballs: HappyEyeballsSchema.parse({}),
});
expect(out?.happyEyeballs).toEqual({ tryDelayMs: 250 });
});
});
describe('normalizeStreamSettingsForWire reality', () => {