headers.ts 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. // Pure helpers for header-shape conversion between the panel's internal
  2. // HeaderEntry[] form and Xray's V2-style header map. Extracted from
  3. // XrayCommonClass.toHeaders / .toV2Headers so callers can stop relying on
  4. // the class hierarchy. Behavior is byte-equivalent to the legacy methods —
  5. // the shadow tests in src/test/headers.test.ts pin that.
  6. export interface HeaderEntry {
  7. name: string;
  8. value: string;
  9. }
  10. export type V2HeaderMap = Record<string, string | string[]>;
  11. // Expand a V2-style header map into the panel's flat HeaderEntry[]. A
  12. // header whose value is an array yields one entry per item, preserving
  13. // order; a string value yields a single entry. Non-object inputs (null,
  14. // undefined, primitives) yield [].
  15. export function toHeaders(v2Headers: unknown): HeaderEntry[] {
  16. const out: HeaderEntry[] = [];
  17. if (!v2Headers || typeof v2Headers !== 'object') return out;
  18. const map = v2Headers as Record<string, unknown>;
  19. for (const key of Object.keys(map)) {
  20. const values = map[key];
  21. if (typeof values === 'string') {
  22. out.push({ name: key, value: values });
  23. } else if (Array.isArray(values)) {
  24. for (const v of values) {
  25. if (typeof v === 'string') out.push({ name: key, value: v });
  26. }
  27. }
  28. }
  29. return out;
  30. }
  31. // Collapse a HeaderEntry[] back into a V2-style header map. When `arr` is
  32. // true (the default — matches Xray's TCP/WS/HTTP request/response shape),
  33. // duplicate header names accumulate into a string[]. When false (used for
  34. // WS/HTTPUpgrade/xHTTP top-level headers, sockopt portMap, etc.), the
  35. // last value wins. Entries with empty name or value are skipped — same as
  36. // the legacy ObjectUtil.isEmpty() filter.
  37. export function toV2Headers(headers: HeaderEntry[], arr: boolean = true): V2HeaderMap {
  38. const out: V2HeaderMap = {};
  39. for (const { name, value } of headers) {
  40. if (name == null || name === '' || value == null || value === '') continue;
  41. if (!(name in out)) {
  42. out[name] = arr ? [value] : value;
  43. continue;
  44. }
  45. const existing = out[name];
  46. if (arr && Array.isArray(existing)) {
  47. existing.push(value);
  48. } else {
  49. out[name] = value;
  50. }
  51. }
  52. return out;
  53. }