host-link.test.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /// <reference types="vite/client" />
  2. import { describe, expect, it } from 'vitest';
  3. import { hostToExternalProxyEntry, withMtprotoHostEndpoints } from '@/lib/hosts/host-link';
  4. import { inboundFromDb } from '@/lib/xray/inbound-from-db';
  5. describe('hostToExternalProxyEntry', () => {
  6. const base = {
  7. security: 'tls' as const,
  8. address: 'cdn.example.com',
  9. port: 8443,
  10. remark: 'R',
  11. sni: 'sni.example.com',
  12. alpn: ['h2'] as ('h2' | 'h3' | 'http/1.1')[],
  13. fingerprint: 'chrome' as const,
  14. pinnedPeerCertSha256: ['AAAA'],
  15. verifyPeerCertByName: 'verify.example.com',
  16. echConfigList: 'ECH',
  17. overrideSniFromAddress: false,
  18. keepSniBlank: false,
  19. vlessRoute: '',
  20. allowInsecure: false,
  21. };
  22. it('maps the overlapping fields onto an external-proxy entry', () => {
  23. const ep = hostToExternalProxyEntry(base);
  24. expect(ep.forceTls).toBe('tls');
  25. expect(ep.dest).toBe('cdn.example.com');
  26. expect(ep.port).toBe(8443);
  27. expect(ep.remark).toBe('R');
  28. expect(ep.sni).toBe('sni.example.com');
  29. expect(ep.alpn).toEqual(['h2']);
  30. expect(ep.fingerprint).toBe('chrome');
  31. expect(ep.pinnedPeerCertSha256).toEqual(['AAAA']);
  32. expect(ep.verifyPeerCertByName).toBe('verify.example.com');
  33. expect(ep.echConfigList).toBe('ECH');
  34. });
  35. it('maps reality/same security to forceTls "same"', () => {
  36. expect(hostToExternalProxyEntry({ ...base, security: 'reality' }).forceTls).toBe('same');
  37. expect(hostToExternalProxyEntry({ ...base, security: 'same' }).forceTls).toBe('same');
  38. expect(hostToExternalProxyEntry({ ...base, security: 'none' }).forceTls).toBe('none');
  39. });
  40. it('uses the address as sni when overrideSniFromAddress is set', () => {
  41. const ep = hostToExternalProxyEntry({ ...base, overrideSniFromAddress: true });
  42. expect(ep.sni).toBe('cdn.example.com');
  43. });
  44. it('omits sni when keepSniBlank is set', () => {
  45. const ep = hostToExternalProxyEntry({ ...base, keepSniBlank: true });
  46. expect(ep.sni).toBeUndefined();
  47. });
  48. it('falls back to port 443 when the host port is 0 (inherit)', () => {
  49. const ep = hostToExternalProxyEntry({ ...base, port: 0 });
  50. expect(ep.port).toBe(443);
  51. });
  52. it('carries a single vlessRoute value through to the entry', () => {
  53. expect(hostToExternalProxyEntry({ ...base, vlessRoute: '443' }).vlessRoute).toBe('443');
  54. expect(hostToExternalProxyEntry({ ...base, vlessRoute: '' }).vlessRoute).toBeUndefined();
  55. });
  56. it('carries allowInsecure through to the entry', () => {
  57. expect(hostToExternalProxyEntry({ ...base, allowInsecure: true }).allowInsecure).toBe(true);
  58. expect(
  59. hostToExternalProxyEntry({ ...base, allowInsecure: false }).allowInsecure,
  60. ).toBeUndefined();
  61. });
  62. });
  63. describe('withMtprotoHostEndpoints', () => {
  64. const inbound = inboundFromDb({
  65. protocol: 'mtproto',
  66. port: 4060,
  67. listen: '127.0.0.1',
  68. settings: { clients: [] },
  69. streamSettings: {},
  70. sniffing: {},
  71. });
  72. it('projects enabled raw Hosts onto MTProto share endpoints', () => {
  73. const got = withMtprotoHostEndpoints(
  74. inbound,
  75. 7,
  76. [
  77. {
  78. groupId: 'public',
  79. inboundIds: [7],
  80. hosts: ['proxy.example.com:443', '[2001:db8::1]'],
  81. port: 443,
  82. remark: 'public',
  83. },
  84. ],
  85. '',
  86. 'panel.example.com',
  87. );
  88. expect(got.streamSettings?.externalProxy).toEqual([
  89. { forceTls: 'same', dest: 'proxy.example.com', port: 443, remark: 'public' },
  90. { forceTls: 'same', dest: '2001:db8::1', port: 4060, remark: 'public' },
  91. ]);
  92. });
  93. it('inherits the inbound address for a port-only Host', () => {
  94. const got = withMtprotoHostEndpoints(
  95. inbound,
  96. 7,
  97. [{ groupId: 'port-only', inboundIds: [7], hosts: [':8443'], port: 8443 }],
  98. '',
  99. 'panel.example.com',
  100. );
  101. expect(got.streamSettings?.externalProxy).toEqual([
  102. { forceTls: 'same', dest: 'panel.example.com', port: 8443, remark: '' },
  103. ]);
  104. });
  105. it('ignores disabled, excluded and unrelated Hosts', () => {
  106. const got = withMtprotoHostEndpoints(
  107. inbound,
  108. 7,
  109. [
  110. { groupId: 'disabled', inboundIds: [7], hosts: ['a.example.com:443'], isDisabled: true },
  111. {
  112. groupId: 'excluded',
  113. inboundIds: [7],
  114. hosts: ['b.example.com:443'],
  115. excludeFromSubTypes: ['raw'],
  116. },
  117. { groupId: 'other', inboundIds: [8], hosts: ['c.example.com:443'] },
  118. ],
  119. '',
  120. 'panel.example.com',
  121. );
  122. expect(got).toBe(inbound);
  123. });
  124. });