Release 3.8.2.13: iOS Packet Tunnel client and Clash URL subscriptions.
Add SwiftUI + Network Extension (LibXray/hev), parse Clash Meta YAML from subscription URLs into vless/trojan/hy2 share links, keep the selected server on connect, and ship Navis-3.8.2.13.ipa with versioned changelog. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,6 +2,8 @@ package subscription
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -43,18 +45,100 @@ proxies:
|
||||
sni: de.example.com
|
||||
obfs: salamander
|
||||
obfs-password: obfspass
|
||||
- name: CZ Vless
|
||||
type: vless
|
||||
server: cz.example.com
|
||||
port: 443
|
||||
uuid: 7e1c0fbf-1758-4781-97f6-ae8715ed3f60
|
||||
network: xhttp
|
||||
tls: true
|
||||
servername: cz.example.com
|
||||
alpn:
|
||||
- h2
|
||||
- http/1.1
|
||||
xhttp-opts:
|
||||
path: /xhttp
|
||||
host: cz.example.com
|
||||
mode: auto
|
||||
client-fingerprint: chrome
|
||||
- name: CZ Trojan
|
||||
type: trojan
|
||||
server: cz.example.com
|
||||
port: 20000
|
||||
password: secretpass
|
||||
network: ws
|
||||
tls: true
|
||||
sni: cz.example.com
|
||||
ws-opts:
|
||||
path: /ws
|
||||
proxy-groups:
|
||||
- name: Auto
|
||||
icon: https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Proxy.png
|
||||
type: select
|
||||
`
|
||||
items, err := ParseBody(body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(items) != 1 {
|
||||
t.Fatalf("got %d", len(items))
|
||||
if len(items) != 3 {
|
||||
t.Fatalf("got %d items: %+v", len(items), items)
|
||||
}
|
||||
if items[0].Protocol != "hysteria2" {
|
||||
t.Fatalf("proto %s", items[0].Protocol)
|
||||
byProto := map[string]int{}
|
||||
var vlessURI string
|
||||
for _, it := range items {
|
||||
byProto[string(it.Protocol)]++
|
||||
if it.Protocol == "naive" {
|
||||
t.Fatalf("unexpected naive: %s %s", it.Name, it.URI)
|
||||
}
|
||||
if it.Protocol == "vless" {
|
||||
vlessURI = it.URI
|
||||
}
|
||||
}
|
||||
if !strings.Contains(items[0].URI, "de.example.com:443") {
|
||||
t.Fatalf("uri %s", items[0].URI)
|
||||
if byProto["hysteria2"] != 1 || byProto["vless"] != 1 || byProto["trojan"] != 1 {
|
||||
t.Fatalf("protos %+v", byProto)
|
||||
}
|
||||
if !strings.Contains(items[0].URI+items[1].URI+items[2].URI, "de.example.com:443") {
|
||||
t.Fatalf("missing hy2 host in %+v", items)
|
||||
}
|
||||
if !strings.Contains(vlessURI, "alpn=h2") {
|
||||
t.Fatalf("alpn list broken in %s", vlessURI)
|
||||
}
|
||||
if strings.Contains(vlessURI, "alpn=-") {
|
||||
t.Fatalf("alpn bled list marker in %s", vlessURI)
|
||||
}
|
||||
if !strings.Contains(vlessURI, "mode=auto") {
|
||||
t.Fatalf("xhttp mode missing in %s", vlessURI)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNaiveAuthIgnoresPathAt(t *testing.T) {
|
||||
if naiveLinkHasAuth("https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Proxy.png") {
|
||||
t.Fatal("path @master must not count as auth")
|
||||
}
|
||||
if !naiveLinkHasAuth("https://user:pass@naive.example.com") {
|
||||
t.Fatal("real auth required")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBodyRealClashSubscriptionFile(t *testing.T) {
|
||||
path := filepath.Join("testdata", "evilfox_clash.yaml")
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Skip("optional fixture missing:", path)
|
||||
}
|
||||
items, err := ParseBody(string(raw))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(items) < 80 {
|
||||
t.Fatalf("expected ~89 proxies, got %d", len(items))
|
||||
}
|
||||
for _, it := range items {
|
||||
if it.Protocol == "naive" {
|
||||
t.Fatalf("naive noise: %s %s", it.Name, it.URI)
|
||||
}
|
||||
if strings.EqualFold(it.Name, "master") || strings.HasPrefix(strings.ToLower(it.Name), "master-") {
|
||||
t.Fatalf("icon false-positive name: %s", it.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user