Bläddra i källkod

chore: Improve URI parser

MystiPanda 11 månader sedan
förälder
incheckning
ec6c2adf9b
2 ändrade filer med 452 tillägg och 187 borttagningar
  1. 360 96
      src/services/types.d.ts
  2. 92 91
      src/utils/uri-parser.ts

+ 360 - 96
src/services/types.d.ts

@@ -228,125 +228,389 @@ interface IProxyGroupConfig {
   icon?: string;
 }
 
-interface IProxyConfig {
-  name: string;
-  type:
-    | "ss"
-    | "ssr"
-    | "direct"
-    | "dns"
-    | "snell"
-    | "http"
-    | "trojan"
-    | "hysteria"
-    | "hysteria2"
-    | "tuic"
-    | "wireguard"
-    | "ssh"
-    | "socks5"
-    | "vmess"
-    | "vless";
-  server: string;
-  port: number;
-  "ip-version"?: string;
-  udp?: boolean;
-  "interface-name"?: string;
-  "routing-mark"?: number;
+interface WsOptions {
+  path?: string;
+  headers?: {
+    [key: string]: string;
+  };
+  "max-early-data"?: number;
+  "early-data-header-name"?: string;
+  "v2ray-http-upgrade"?: boolean;
+  "v2ray-http-upgrade-fast-open"?: boolean;
+}
+
+interface HttpOptions {
+  method?: string;
+  path?: string[];
+  headers?: {
+    [key: string]: string;
+  };
+}
+
+interface GrpcOptions {
+  "grpc-service-name"?: string;
+}
+type NetworkType = "ws" | "http" | "h2" | "grpc";
+
+// base
+interface IProxyBaseConfig {
   tfo?: boolean;
   mptcp?: boolean;
+  "interface-name"?: string;
+  "routing-mark"?: number;
+  "ip-version"?: string;
   "dialer-proxy"?: string;
-  plugin?: "obfs" | "v2ray-plugin" | "shadow-tls" | "restls";
-  "plugin-opts"?: {
-    mode?: string;
-    host?: string;
-    path?: string;
-    tls?: string;
-  };
-  cipher?: string;
+}
+// direct
+interface IProxyDirectConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "direct";
+}
+// dns
+interface IProxyDnsConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "dns";
+}
+// http
+interface IProxyHttpConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "http";
+  server?: string;
+  port?: number;
+  username?: string;
   password?: string;
-  "udp-over-tcp"?: boolean;
-  protocol?: string;
-  obfs?: string;
-  "protocol-param"?: string;
-  "obfs-param"?: string;
-  uuid?: string;
   tls?: boolean;
+  sni?: string;
   "skip-cert-verify"?: boolean;
-  network?: "ws" | "http" | "h2" | "grpc";
-  "ws-opts"?: {
-    path?: string;
-    headers?: {};
+  fingerprint?: string;
+  headers?: {};
+}
+// socks5
+interface IProxySocks5Config extends IProxyBaseConfig {
+  name?: string;
+  type: "socks5";
+  server?: string;
+  port?: number;
+  username?: string;
+  password?: string;
+  tls?: boolean;
+  udp?: boolean;
+  "skip-cert-verify"?: boolean;
+  fingerprint?: string;
+}
+// ssh
+interface IProxySshConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "ssh";
+  server?: string;
+  port?: number;
+  username?: string;
+  password?: string;
+  "private-key"?: string;
+  "private-key-passphrase"?: string;
+  "host-key"?: string;
+  "host-key-algorithms"?: string;
+}
+// trojan
+interface IProxyTrojanConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "trojan";
+  server?: string;
+  port?: number;
+  password?: string;
+  alpn?: string[];
+  sni?: string;
+  "skip-cert-verify"?: boolean;
+  fingerprint?: string;
+  udp?: boolean;
+  network?: NetworkType;
+  "reality-opts"?: {};
+  "grpc-opts"?: GrpcOptions;
+  "ws-opts"?: WsOptions;
+  "ss-opts"?: {
+    enabled?: boolean;
+    method?: string;
+    password?: string;
   };
-  alterId?: number;
+  "client-fingerprint"?: string;
+}
+// tuic
+interface IProxyTuicConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "tuic";
+  server?: string;
+  port?: number;
+  token?: string;
+  uuid?: string;
+  password?: string;
+  ip?: string;
+  "heartbeat-interval"?: number;
+  alpn?: string[];
+  "reduce-rtt"?: boolean;
+  "request-timeout"?: number;
+  "udp-relay-mode"?: string;
+  "congestion-controller"?: string;
+  "disable-sni"?: boolean;
+  "max-udp-relay-packet-size"?: number;
+  "fast-open"?: boolean;
+  "max-open-streams"?: number;
+  cwnd?: number;
+  "skip-cert-verify"?: boolean;
+  fingerprint?: string;
+  ca?: string;
+  "ca-str"?: string;
+  "recv-window-conn"?: number;
+  "recv-window"?: number;
+  "disable-mtu-discovery"?: boolean;
+  "max-datagram-frame-size"?: number;
   sni?: string;
-  "http-opts"?: {};
-  "grpc-opts"?: {};
-  "ws-opts"?: {};
-  "h2-opts"?: {};
+  "udp-over-stream"?: boolean;
+  "udp-over-stream-version"?: number;
+}
+// vless
+interface IProxyVlessConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "vless";
+  server?: string;
+  port?: number;
+  uuid?: string;
+  flow?: string;
+  tls?: boolean;
+  alpn?: string[];
+  udp?: boolean;
+  "packet-addr"?: boolean;
+  xudp?: boolean;
+  "packet-encoding"?: string;
+  network?: NetworkType;
   "reality-opts"?: {
     "public-key"?: string;
     "short-id"?: string;
   };
-  flow?: "xtls-rprx-vision";
-  "client-fingerprint"?:
-    | "chrome"
-    | "firefox"
-    | "safari"
-    | "iOS"
-    | "android"
-    | "edge"
-    | "360"
-    | "qq"
-    | "random";
+  "http-opts"?: HttpOptions;
+  "h2-opts"?: {};
+  "grpc-opts"?: GrpcOptions;
+  "ws-opts"?: WsOptions;
+  "ws-path"?: string;
+  "ws-headers"?: {};
+  "skip-cert-verify"?: boolean;
+  fingerprint?: string;
+  servername?: string;
+  "client-fingerprint"?: string;
+}
+// vmess
+interface IProxyVmessConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "vmess";
+  server?: string;
+  port?: number;
+  uuid?: string;
+  alterId?: number;
+  cipher?: string;
+  udp?: boolean;
+  network?: NetworkType;
+  tls?: boolean;
   alpn?: string[];
-  ws?: {
-    headers?: {
-      Host?: string;
-    };
-    "ws-service-name"?: string;
-    path?: string;
-  };
-  http?: {
-    headers?: {
-      Host?: string;
-    };
-    "http-service-name"?: string;
+  "skip-cert-verify"?: boolean;
+  fingerprint?: string;
+  servername?: string;
+  "reality-opts"?: {};
+  "http-opts"?: HttpOptions;
+  "h2-opts"?: {
     path?: string;
+    host?: string;
   };
-  grpc?: {};
+  "grpc-opts"?: GrpcOptions;
+  "ws-opts"?: WsOptions;
+  "packet-addr"?: boolean;
+  xudp?: boolean;
+  "packet-encoding"?: string;
+  "global-padding"?: boolean;
+  "authenticated-length"?: boolean;
+  "client-fingerprint"?: string;
+}
+interface WireGuardPeerOptions {
+  server?: string;
+  port?: number;
+  "public-key"?: string;
+  "pre-shared-key"?: string;
+  reserved?: number[];
+  "allowed-ips"?: string[];
+}
+// wireguard
+interface IProxyWireguardConfig extends IProxyBaseConfig, WireGuardPeerOptions {
+  name?: string;
+  type: "wireguard";
+  ip?: string;
+  ipv6?: string;
+  "private-key"?: string;
+  workers?: number;
+  mtu?: number;
+  udp?: boolean;
+  "persistent-keepalive"?: number;
+  peers?: WireGuardPeerOptions[];
+  "remote-dns-resolve"?: boolean;
+  dns?: string[];
+  "refresh-server-ip-interval"?: number;
+}
+// hysteria
+interface IProxyHysteriaConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "hysteria";
+  server?: string;
+  port?: number;
   ports?: string;
-  "obfs-password"?: string;
-  "tls-fingerprint"?: string;
-  "auth-str"?: string;
+  protocol?: string;
+  "obfs-protocol"?: string;
   up?: string;
+  "up-speed"?: number;
   down?: string;
-  "fast-open"?: boolean;
+  "down-speed"?: number;
+  auth?: string;
+  "auth-str"?: string;
+  obfs?: string;
+  sni?: string;
+  "skip-cert-verify"?: boolean;
   fingerprint?: string;
-  "disable-mtu-discovery"?: boolean;
+  alpn?: string[];
   ca?: string;
   "ca-str"?: string;
   "recv-window-conn"?: number;
   "recv-window"?: number;
-  token?: string;
-  ip?: string;
-  "heartbeat-interval"?: number;
-  "disable-sni"?: boolean;
-  "reduce-rtt"?: boolean;
-  "request-timeout"?: number;
-  "udp-relay-mode"?: string;
-  "congestion-controller"?: string;
-  "max-udp-relay-packet-size"?: number;
-  "max-open-streams"?: number;
-  "private-key"?: string;
-  "public-key"?: string;
-  ipv6?: string;
-  reserved?: number[];
-  mtu?: number;
-  "remote-dns-resolve"?: boolean;
-  "allowed-ips"?: string[];
-  dns?: string[];
-  "pre-shared-key"?: string;
-  username?: string;
+  "disable-mtu-discovery"?: boolean;
+  "fast-open"?: boolean;
+  "hop-interval"?: number;
+}
+// hysteria2
+interface IProxyHysteria2Config extends IProxyBaseConfig {
+  name?: string;
+  type: "hysteria2";
+  server?: string;
+  port?: number;
+  ports?: string;
+  "hop-interval"?: number;
+  protocol?: string;
+  "obfs-protocol"?: string;
+  up?: string;
+  down?: string;
+  password?: string;
+  obfs?: string;
+  "obfs-password"?: string;
+  sni?: string;
+  "skip-cert-verify"?: boolean;
+  fingerprint?: string;
+  alpn?: string[];
+  ca?: string;
+  "ca-str"?: string;
+  cwnd?: number;
+  "udp-mtu"?: number;
+}
+// shadowsocks
+interface IProxyShadowsocksConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "ss";
+  server?: string;
+  port?: number;
+  password?: string;
+  cipher?: string;
+  udp?: boolean;
+  plugin?: string;
+  "plugin-opts"?: {
+    mode?: string;
+    host?: string;
+    password?: string;
+    path?: string;
+    tls?: string;
+    fingerprint?: string;
+    headers?: {};
+    "skip-cert-verify"?: boolean;
+    version?: number;
+    mux?: boolean;
+    "v2ray-http-upgrade"?: boolean;
+    "v2ray-http-upgrade-fast-open"?: boolean;
+    "version-hint"?: string;
+    "restls-script"?: string;
+  };
+  "udp-over-tcp"?: boolean;
+  "udp-over-tcp-version"?: number;
+  "client-fingerprint"?: string;
+}
+// shadowsocksR
+interface IProxyshadowsocksRConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "ssr";
+  server?: string;
+  port?: number;
+  password?: string;
+  cipher?: string;
+  obfs?: string;
+  "obfs-param"?: string;
+  protocol?: string;
+  "protocol-param"?: string;
+  udp?: boolean;
+}
+// sing-mux
+interface IProxySmuxConfig {
+  smux?: {
+    enabled?: boolean;
+    protocol?: string;
+    "max-connections"?: number;
+    "min-streams"?: number;
+    "max-streams"?: number;
+    padding?: boolean;
+    statistic?: boolean;
+    "only-tcp"?: boolean;
+    "brutal-opts"?: {
+      enabled?: boolean;
+      up?: string;
+      down?: string;
+    };
+  };
+}
+// snell
+interface IProxySnellConfig extends IProxyBaseConfig {
+  name?: string;
+  type: "snell";
+  server?: string;
+  port?: number;
+  psk?: string;
+  udp?: boolean;
+  version?: number;
+  "obfs-opts"?: {};
+}
+interface IProxyConfig
+  extends IProxyBaseConfig,
+    IProxyDirectConfig,
+    IProxyDnsConfig,
+    IProxyHttpConfig,
+    IProxySocks5Config,
+    IProxySshConfig,
+    IProxyTrojanConfig,
+    IProxyTuicConfig,
+    IProxyVlessConfig,
+    IProxyVmessConfig,
+    IProxyWireguardConfig,
+    IProxyHysteriaConfig,
+    IProxyHysteria2Config,
+    IProxyShadowsocksConfig,
+    IProxyshadowsocksRConfig,
+    IProxySmuxConfig,
+    IProxySnellConfig {
+  type:
+    | "ss"
+    | "ssr"
+    | "direct"
+    | "dns"
+    | "snell"
+    | "http"
+    | "trojan"
+    | "hysteria"
+    | "hysteria2"
+    | "tuic"
+    | "wireguard"
+    | "ssh"
+    | "socks5"
+    | "vmess"
+    | "vless";
 }
 
 interface IVergeConfig {

+ 92 - 91
src/utils/uri-parser.ts

@@ -51,6 +51,10 @@ function isPresent(value: any): boolean {
   return value !== null && value !== undefined;
 }
 
+function trimStr(str: string | undefined): string | undefined {
+  return str ? str.trim() : str;
+}
+
 function isNotBlank(name: string) {
   return name.trim().length !== 0;
 }
@@ -76,12 +80,12 @@ function decodeBase64OrOriginal(str: string): string {
   }
 }
 
-function URI_SS(line: string): IProxyConfig {
+function URI_SS(line: string): IProxyShadowsocksConfig {
   // parse url
   let content = line.split("ss://")[1];
 
-  const proxy: IProxyConfig = {
-    name: decodeURIComponent(line.split("#")[1]),
+  const proxy: IProxyShadowsocksConfig = {
+    name: trimStr(decodeURIComponent(line.split("#")[1])),
     type: "ss",
     server: "",
     port: 0,
@@ -168,7 +172,7 @@ function URI_SS(line: string): IProxyConfig {
   return proxy;
 }
 
-function URI_SSR(line: string): IProxyConfig {
+function URI_SSR(line: string): IProxyshadowsocksRConfig {
   line = decodeBase64OrOriginal(line.split("ssr://")[1]);
 
   // handle IPV6 & IPV4 format
@@ -186,8 +190,8 @@ function URI_SSR(line: string): IProxyConfig {
     .substring(splitIdx + 1)
     .split("/?")[0]
     .split(":");
-  let proxy: IProxyConfig = {
-    name: "",
+  let proxy: IProxyshadowsocksRConfig = {
+    name: "SSR",
     type: "ssr",
     server,
     port,
@@ -210,7 +214,7 @@ function URI_SSR(line: string): IProxyConfig {
   proxy = {
     ...proxy,
     name: other_params.remarks
-      ? decodeBase64OrOriginal(other_params.remarks)
+      ? trimStr(decodeBase64OrOriginal(other_params.remarks))
       : proxy.server,
     "protocol-param": getIfNotBlank(
       decodeBase64OrOriginal(other_params.protoparam || "").replace(/\s/g, "")
@@ -222,7 +226,7 @@ function URI_SSR(line: string): IProxyConfig {
   return proxy;
 }
 
-function URI_VMESS(line: string): IProxyConfig {
+function URI_VMESS(line: string): IProxyVmessConfig {
   line = line.split("vmess://")[1];
   let content = decodeBase64OrOriginal(line);
   console.log(content);
@@ -238,8 +242,8 @@ function URI_VMESS(line: string): IProxyConfig {
       }
     }
 
-    const proxy: IProxyConfig = {
-      name: partitions[0].split("=")[0].trim(),
+    const proxy: IProxyVmessConfig = {
+      name: trimStr(partitions[0].split("=")[0]),
       type: "vmess",
       server: partitions[1],
       port: parseInt(partitions[2], 10),
@@ -308,29 +312,30 @@ function URI_VMESS(line: string): IProxyConfig {
         }
       }
     }
-    console.log(params);
+
     const server = params.add;
     const port = parseInt(getIfPresent(params.port), 10);
-    const proxy: IProxyConfig = {
+    const proxy: IProxyVmessConfig = {
       name:
-        params.ps ??
-        params.remarks ??
-        params.remark ??
+        trimStr(params.ps) ??
+        trimStr(params.remarks) ??
+        trimStr(params.remark) ??
         `VMess ${server}:${port}`,
       type: "vmess",
       server,
       port,
       cipher: getIfPresent(params.scy, "auto"),
       uuid: params.id,
-      alterId: parseInt(getIfPresent(params.aid ?? params.alterId, 0), 10),
       tls: ["tls", true, 1, "1"].includes(params.tls),
       "skip-cert-verify": isPresent(params.verify_cert)
         ? !params.verify_cert
         : undefined,
     };
 
+    proxy.alterId = parseInt(getIfPresent(params.aid ?? params.alterId, 0), 10);
+
     if (proxy.tls && params.sni) {
-      proxy.sni = params.sni;
+      proxy.servername = params.sni;
     }
 
     let httpupgrade = false;
@@ -383,7 +388,6 @@ function URI_VMESS(line: string): IProxyConfig {
         if (["grpc"].includes(proxy.network)) {
           proxy[`grpc-opts`] = {
             "grpc-service-name": getIfNotBlank(transportPath),
-            "_grpc-type": getIfNotBlank(params.type),
           };
         } else {
           const opts: Record<string, any> = {
@@ -400,8 +404,8 @@ function URI_VMESS(line: string): IProxyConfig {
         delete proxy.network;
       }
 
-      if (proxy.tls && !proxy.sni && transportHost) {
-        proxy.sni = transportHost;
+      if (proxy.tls && !proxy.servername && transportHost) {
+        proxy.servername = transportHost;
       }
     }
 
@@ -409,7 +413,7 @@ function URI_VMESS(line: string): IProxyConfig {
   }
 }
 
-function URI_VLESS(line: string): IProxyConfig {
+function URI_VLESS(line: string): IProxyVlessConfig {
   line = line.split("vless://")[1];
   let isShadowrocket;
   let parsed = /^(.*?)@(.*?):(\d+)\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
@@ -428,9 +432,9 @@ function URI_VLESS(line: string): IProxyConfig {
   uuid = decodeURIComponent(uuid);
   name = decodeURIComponent(name);
 
-  const proxy: IProxyConfig = {
+  const proxy: IProxyVlessConfig = {
     type: "vless",
-    name,
+    name: "",
     server,
     port,
     uuid,
@@ -444,14 +448,17 @@ function URI_VLESS(line: string): IProxyConfig {
   }
 
   proxy.name =
-    name ?? params.remarks ?? params.remark ?? `VLESS ${server}:${port}`;
+    trimStr(name) ??
+    trimStr(params.remarks) ??
+    trimStr(params.remark) ??
+    `VLESS ${server}:${port}`;
 
   proxy.tls = (params.security && params.security !== "none") || undefined;
   if (isShadowrocket && /TRUE|1/i.test(params.tls)) {
     proxy.tls = true;
     params.security = params.security ?? "reality";
   }
-  proxy.sni = params.sni || params.peer;
+  proxy.servername = params.sni || params.peer;
   proxy.flow = params.flow ? "xtls-rprx-vision" : undefined;
 
   proxy["client-fingerprint"] = params.fp as
@@ -468,7 +475,7 @@ function URI_VLESS(line: string): IProxyConfig {
   proxy["skip-cert-verify"] = /(TRUE)|1/i.test(params.allowInsecure);
 
   if (["reality"].includes(params.security)) {
-    const opts: IProxyConfig["reality-opts"] = {};
+    const opts: IProxyVlessConfig["reality-opts"] = {};
     if (params.pbk) {
       opts["public-key"] = params.pbk;
     }
@@ -481,18 +488,17 @@ function URI_VLESS(line: string): IProxyConfig {
   }
 
   let httpupgrade = false;
-  proxy.ws = {
+  proxy["ws-opts"] = {
     headers: undefined,
-    "ws-service-name": undefined,
     path: undefined,
   };
-  proxy.http = {
+
+  proxy["http-opts"] = {
     headers: undefined,
-    "http-service-name": undefined,
     path: undefined,
   };
-  proxy.grpc = { "_grpc-type": undefined };
-  proxy.network = params.type as "ws" | "http" | "h2" | "grpc";
+  proxy["grpc-opts"] = {};
+
   if (params.headerType === "http") {
     proxy.network = "http";
   } else {
@@ -500,7 +506,22 @@ function URI_VLESS(line: string): IProxyConfig {
     httpupgrade = true;
   }
   if (!proxy.network && isShadowrocket && params.obfs) {
-    proxy.network = params.obfs as "ws" | "http" | "h2" | "grpc";
+    switch (params.type) {
+      case "sw":
+        proxy.network = "ws";
+        break;
+      case "http":
+        proxy.network = "http";
+        break;
+      case "h2":
+        proxy.network = "h2";
+        break;
+      case "grpc":
+        proxy.network = "grpc";
+        break;
+      default: {
+      }
+    }
   }
   if (["websocket"].includes(proxy.network)) {
     proxy.network = "ws";
@@ -520,20 +541,9 @@ function URI_VLESS(line: string): IProxyConfig {
         opts.headers = { Host: host };
       }
     }
-    if (params.serviceName) {
-      opts[`${proxy.network}-service-name`] = params.serviceName;
-    } else if (isShadowrocket && params.path) {
-      if (!["ws", "http", "h2"].includes(proxy.network)) {
-        opts[`${proxy.network}-service-name`] = params.path;
-        delete params.path;
-      }
-    }
     if (params.path) {
       opts.path = params.path;
     }
-    if (["grpc"].includes(proxy.network)) {
-      opts["_grpc-type"] = params.mode || "gun";
-    }
     if (httpupgrade) {
       opts["v2ray-http-upgrade"] = true;
       opts["v2ray-http-upgrade-fast-open"] = true;
@@ -543,25 +553,25 @@ function URI_VLESS(line: string): IProxyConfig {
     }
   }
 
-  if (proxy.tls && !proxy.sni) {
+  if (proxy.tls && !proxy.servername) {
     if (proxy.network === "ws") {
-      proxy.sni = proxy.ws?.headers?.Host;
+      proxy.servername = proxy["ws-opts"]?.headers?.Host;
     } else if (proxy.network === "http") {
-      let httpHost = proxy.http?.headers?.Host;
-      proxy.sni = Array.isArray(httpHost) ? httpHost[0] : httpHost;
+      let httpHost = proxy["http-opts"]?.headers?.Host;
+      proxy.servername = Array.isArray(httpHost) ? httpHost[0] : httpHost;
     }
   }
 
   return proxy;
 }
 
-function URI_Trojan(line: string): IProxyConfig {
+function URI_Trojan(line: string): IProxyTrojanConfig {
   let [newLine, name] = line.split(/#(.+)/, 2);
   const parser = getTrojanURIParser();
-  const proxy = parser.parse(newLine);
+  const proxy: IProxyTrojanConfig = parser.parse(newLine);
   if (isNotBlank(name)) {
     try {
-      proxy.name = decodeURIComponent(name);
+      proxy.name = trimStr(decodeURIComponent(name));
     } catch (e) {
       throw Error("Can not get proxy name");
     }
@@ -569,7 +579,7 @@ function URI_Trojan(line: string): IProxyConfig {
   return proxy;
 }
 
-function URI_Hysteria2(line: string): IProxyConfig {
+function URI_Hysteria2(line: string): IProxyHysteria2Config {
   line = line.split(/(hysteria2|hy2):\/\//)[2];
   // eslint-disable-next-line no-unused-vars
   let [__, password, server, ___, port, ____, addons = "", name] =
@@ -579,12 +589,12 @@ function URI_Hysteria2(line: string): IProxyConfig {
     portNum = 443;
   }
   password = decodeURIComponent(password);
-  if (name != null) {
-    name = decodeURIComponent(name);
-  }
-  name = name ?? `Hysteria2 ${server}:${port}`;
 
-  const proxy: IProxyConfig = {
+  let decodedName = trimStr(decodeURIComponent(name));
+
+  name = decodedName ?? `Hysteria2 ${server}:${port}`;
+
+  const proxy: IProxyHysteria2Config = {
     type: "hysteria2",
     name,
     server,
@@ -612,12 +622,12 @@ function URI_Hysteria2(line: string): IProxyConfig {
   proxy["obfs-password"] = params["obfs-password"];
   proxy["skip-cert-verify"] = /(TRUE)|1/i.test(params.insecure);
   proxy.tfo = /(TRUE)|1/i.test(params.fastopen);
-  proxy["tls-fingerprint"] = params.pinSHA256;
+  proxy.fingerprint = params.pinSHA256;
 
   return proxy;
 }
 
-function URI_Hysteria(line: string): IProxyConfig {
+function URI_Hysteria(line: string): IProxyHysteriaConfig {
   line = line.split(/(hysteria|hy):\/\//)[2];
   let [__, server, ___, port, ____, addons = "", name] =
     /^(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
@@ -625,12 +635,11 @@ function URI_Hysteria(line: string): IProxyConfig {
   if (isNaN(portNum)) {
     portNum = 443;
   }
-  if (name != null) {
-    name = decodeURIComponent(name);
-  }
-  name = name ?? `Hysteria ${server}:${port}`;
+  let decodedName = trimStr(decodeURIComponent(name));
+
+  name = decodedName ?? `Hysteria ${server}:${port}`;
 
-  const proxy: IProxyConfig = {
+  const proxy: IProxyHysteriaConfig = {
     type: "hysteria",
     name,
     server,
@@ -713,7 +722,7 @@ function URI_Hysteria(line: string): IProxyConfig {
   return proxy;
 }
 
-function URI_TUIC(line: string): IProxyConfig {
+function URI_TUIC(line: string): IProxyTuicConfig {
   line = line.split(/tuic:\/\//)[1];
 
   let [__, uuid, password, server, ___, port, ____, addons = "", name] =
@@ -724,12 +733,11 @@ function URI_TUIC(line: string): IProxyConfig {
     portNum = 443;
   }
   password = decodeURIComponent(password);
-  if (name != null) {
-    name = decodeURIComponent(name);
-  }
-  name = name ?? `TUIC ${server}:${port}`;
+  let decodedName = trimStr(decodeURIComponent(name));
+
+  name = decodedName ?? `TUIC ${server}:${port}`;
 
-  const proxy: IProxyConfig = {
+  const proxy: IProxyTuicConfig = {
     type: "tuic",
     name,
     server,
@@ -794,7 +802,7 @@ function URI_TUIC(line: string): IProxyConfig {
   return proxy;
 }
 
-function URI_Wireguard(line: string): IProxyConfig {
+function URI_Wireguard(line: string): IProxyWireguardConfig {
   line = line.split(/(wireguard|wg):\/\//)[2];
   let [__, ___, privateKey, server, ____, port, _____, addons = "", name] =
     /^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
@@ -804,11 +812,10 @@ function URI_Wireguard(line: string): IProxyConfig {
     portNum = 443;
   }
   privateKey = decodeURIComponent(privateKey);
-  if (name != null) {
-    name = decodeURIComponent(name);
-  }
-  name = name ?? `WireGuard ${server}:${port}`;
-  const proxy: IProxyConfig = {
+  let decodedName = trimStr(decodeURIComponent(name));
+
+  name = decodedName ?? `WireGuard ${server}:${port}`;
+  const proxy: IProxyWireguardConfig = {
     type: "wireguard",
     name,
     server,
@@ -877,7 +884,7 @@ function URI_Wireguard(line: string): IProxyConfig {
   return proxy;
 }
 
-function URI_HTTP(line: string): IProxyConfig {
+function URI_HTTP(line: string): IProxyHttpConfig {
   line = line.split(/(http|https):\/\//)[2];
   let [__, ___, auth, server, ____, port, _____, addons = "", name] =
     /^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
@@ -889,11 +896,10 @@ function URI_HTTP(line: string): IProxyConfig {
   if (auth) {
     auth = decodeURIComponent(auth);
   }
-  if (name != null) {
-    name = decodeURIComponent(name);
-  }
-  name = name ?? `HTTP ${server}:${portNum}`;
-  const proxy: IProxyConfig = {
+  let decodedName = trimStr(decodeURIComponent(name));
+
+  name = decodedName ?? `HTTP ${server}:${portNum}`;
+  const proxy: IProxyHttpConfig = {
     type: "http",
     name,
     server,
@@ -919,9 +925,6 @@ function URI_HTTP(line: string): IProxyConfig {
       case "skip-cert-verify":
         proxy["skip-cert-verify"] = /(TRUE)|1/i.test(value);
         break;
-      case "udp":
-        proxy["udp"] = /(TRUE)|1/i.test(value);
-        break;
       case "ip-version":
         proxy["ip-version"] = value;
         break;
@@ -933,7 +936,7 @@ function URI_HTTP(line: string): IProxyConfig {
   return proxy;
 }
 
-function URI_SOCKS(line: string): IProxyConfig {
+function URI_SOCKS(line: string): IProxySocks5Config {
   line = line.split(/socks5:\/\//)[1];
   let [__, ___, auth, server, ____, port, _____, addons = "", name] =
     /^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
@@ -945,11 +948,9 @@ function URI_SOCKS(line: string): IProxyConfig {
   if (auth) {
     auth = decodeURIComponent(auth);
   }
-  if (name != null) {
-    name = decodeURIComponent(name);
-  }
-  name = name ?? `SOCKS5 ${server}:${portNum}`;
-  const proxy: IProxyConfig = {
+  let decodedName = trimStr(decodeURIComponent(name));
+  name = decodedName ?? `SOCKS5 ${server}:${portNum}`;
+  const proxy: IProxySocks5Config = {
     type: "socks5",
     name,
     server,