| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /// <reference types="vite/client" />
- import { describe, expect, it } from 'vitest';
- import { formValuesToWirePayload } from '@/lib/xray/inbound-form-adapter';
- import { formValuesToWirePayload as outboundToWire } from '@/lib/xray/outbound-form-adapter';
- import {
- normalizeSockoptForWire,
- normalizeStreamSettingsForWire,
- normalizeXhttpForWire,
- validateRealityTarget,
- } from '@/lib/xray/stream-wire-normalize';
- import type { InboundFormValues } from '@/schemas/forms/inbound-form';
- describe('validateRealityTarget', () => {
- it('accepts host:port and bare port', () => {
- expect(validateRealityTarget('play.google.com:443')).toBeUndefined();
- expect(validateRealityTarget('443')).toBeUndefined();
- });
- it('rejects host without port', () => {
- expect(validateRealityTarget('play.google.com')).toBe('pages.inbounds.form.realityTargetNeedsPort');
- expect(validateRealityTarget('')).toBe('pages.inbounds.form.realityTargetRequired');
- });
- });
- describe('normalizeXhttpForWire stream-one', () => {
- it('drops packet-up and stream-up-only fields on inbound', () => {
- const out = normalizeXhttpForWire({
- path: '/app',
- host: 'play.google.com',
- mode: 'stream-one',
- xPaddingBytes: '100-1000',
- scMaxEachPostBytes: '1000000',
- scMinPostsIntervalMs: '30',
- scMaxBufferedPosts: 30,
- scStreamUpServerSecs: '20-80',
- enableXmux: false,
- headers: {},
- }, 'inbound');
- expect(out).toMatchObject({
- path: '/app',
- host: 'play.google.com',
- mode: 'stream-one',
- xPaddingBytes: '100-1000',
- });
- expect(out).not.toHaveProperty('scMaxEachPostBytes');
- expect(out).not.toHaveProperty('scMinPostsIntervalMs');
- expect(out).not.toHaveProperty('scMaxBufferedPosts');
- expect(out).not.toHaveProperty('scStreamUpServerSecs');
- expect(out).not.toHaveProperty('enableXmux');
- expect(out).not.toHaveProperty('headers');
- });
- it('keeps xmux on outbound stream-one', () => {
- const out = normalizeXhttpForWire({
- path: '/app',
- mode: 'stream-one',
- xPaddingBytes: '100-1000',
- xmux: { maxConcurrency: '16-32' },
- scMaxEachPostBytes: '1000000',
- }, 'outbound');
- expect(out.xmux).toEqual({ maxConcurrency: '16-32' });
- expect(out).not.toHaveProperty('scMaxEachPostBytes');
- });
- });
- describe('normalizeSockoptForWire', () => {
- it('omits doc-example defaults that throttle throughput', () => {
- const out = normalizeSockoptForWire({
- tcpWindowClamp: 0,
- tcpMaxSeg: 0,
- tcpUserTimeout: 0,
- tcpFastOpen: true,
- tcpcongestion: 'bbr',
- domainStrategy: 'AsIs',
- tproxy: 'off',
- mark: 0,
- });
- expect(out).toEqual({
- tcpFastOpen: true,
- tcpcongestion: 'bbr',
- });
- });
- it('preserves happyEyeballs on freedom-style outbound', () => {
- const out = normalizeSockoptForWire({
- domainStrategy: 'UseIP',
- happyEyeballs: {
- tryDelayMs: 150,
- prioritizeIPv6: true,
- interleave: 1,
- maxConcurrentTry: 4,
- },
- });
- expect(out?.happyEyeballs).toMatchObject({
- tryDelayMs: 150,
- prioritizeIPv6: true,
- });
- expect(out?.domainStrategy).toBe('UseIP');
- });
- });
- describe('normalizeStreamSettingsForWire reality', () => {
- it('preserves the nested client settings on inbound (share links read publicKey from there)', () => {
- const out = normalizeStreamSettingsForWire({
- network: 'xhttp',
- security: 'reality',
- realitySettings: {
- target: 'play.google.com:443',
- privateKey: 'priv',
- serverNames: ['play.google.com'],
- shortIds: ['abcd'],
- settings: {
- publicKey: 'pub',
- fingerprint: 'chrome',
- spiderX: '/',
- },
- },
- }, { side: 'inbound' });
- const reality = out.realitySettings as Record<string, unknown>;
- expect(reality.target).toBe('play.google.com:443');
- expect(reality.privateKey).toBe('priv');
- const settings = reality.settings as Record<string, unknown>;
- expect(settings.publicKey).toBe('pub');
- expect(settings.spiderX).toBe('/');
- });
- it('passes client realitySettings through unchanged on outbound', () => {
- const out = normalizeStreamSettingsForWire({
- network: 'xhttp',
- security: 'reality',
- realitySettings: {
- publicKey: 'pub',
- fingerprint: 'chrome',
- serverName: 'play.google.com',
- shortId: 'abcd',
- spiderX: '/x',
- },
- }, { side: 'outbound' });
- const reality = out.realitySettings as Record<string, unknown>;
- expect(reality.publicKey).toBe('pub');
- expect(reality.serverName).toBe('play.google.com');
- expect(reality.spiderX).toBe('/x');
- });
- });
- describe('inbound formValuesToWirePayload integration', () => {
- it('emits lean stream-one xhttp + sockopt on save', () => {
- const values = {
- remark: 't',
- enable: true,
- port: 443,
- listen: '0.0.0.0',
- tag: 'in-443',
- expiryTime: 0,
- sniffing: { enabled: false },
- up: 0,
- down: 0,
- total: 0,
- trafficReset: 'never',
- lastTrafficResetTime: 0,
- nodeId: null,
- protocol: 'vless',
- settings: { clients: [{ id: '7eeb09ed-ae97-400d-a1ce-2485fb904407', email: 'n' }], decryption: 'none' },
- streamSettings: {
- network: 'xhttp',
- security: 'reality',
- realitySettings: {
- target: 'play.google.com:443',
- privateKey: 'priv',
- serverNames: ['play.google.com'],
- shortIds: ['44003d86dc1e'],
- settings: { publicKey: 'pub', fingerprint: 'chrome', spiderX: '/' },
- },
- xhttpSettings: {
- path: '/app',
- host: 'play.google.com',
- mode: 'stream-one',
- xPaddingBytes: '100-1000',
- scMaxEachPostBytes: '1000000',
- scMinPostsIntervalMs: '30',
- enableXmux: false,
- },
- sockopt: {
- tcpWindowClamp: 0,
- tcpMaxSeg: 0,
- tcpUserTimeout: 0,
- tcpFastOpen: true,
- tcpcongestion: 'bbr',
- },
- },
- } as InboundFormValues;
- const payload = formValuesToWirePayload(values);
- const stream = JSON.parse(payload.streamSettings) as Record<string, unknown>;
- const xhttp = stream.xhttpSettings as Record<string, unknown>;
- const sockopt = stream.sockopt as Record<string, unknown>;
- const reality = stream.realitySettings as Record<string, unknown>;
- expect(xhttp).not.toHaveProperty('scMaxEachPostBytes');
- expect(sockopt).not.toHaveProperty('tcpWindowClamp');
- expect(sockopt.tcpFastOpen).toBe(true);
- const realitySettings = reality.settings as Record<string, unknown>;
- expect(realitySettings.publicKey).toBe('pub');
- });
- });
- describe('freedom outbound sockopt wire payload', () => {
- it('preserves happyEyeballs on direct freedom outbound', () => {
- const wire = outboundToWire({
- protocol: 'freedom',
- tag: 'direct',
- settings: { domainStrategy: 'UseIP' },
- streamSettings: {
- sockopt: {
- domainStrategy: 'UseIP',
- happyEyeballs: {
- tryDelayMs: 150,
- prioritizeIPv6: true,
- interleave: 1,
- maxConcurrentTry: 4,
- },
- },
- },
- } as Parameters<typeof outboundToWire>[0]);
- expect(wire.streamSettings).toMatchObject({
- sockopt: {
- domainStrategy: 'UseIP',
- happyEyeballs: {
- tryDelayMs: 150,
- prioritizeIPv6: true,
- },
- },
- });
- });
- });
|