mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
217 lines
6.8 KiB
Go
217 lines
6.8 KiB
Go
package proxy
|
|
|
|
import (
|
|
"ant-chrome/backend/internal/config"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestValidateProxyConfigInvalidRawString(t *testing.T) {
|
|
ok, msg := ValidateProxyConfig("not-a-proxy-config", nil, "")
|
|
if ok {
|
|
t.Fatalf("expected invalid raw string to fail validation")
|
|
}
|
|
if !strings.Contains(msg, "解析失败") {
|
|
t.Fatalf("unexpected message: %s", msg)
|
|
}
|
|
}
|
|
|
|
func TestValidateProxyConfigMissingProxyId(t *testing.T) {
|
|
ok, msg := ValidateProxyConfig("", []config.BrowserProxy{
|
|
{ProxyId: "p1", ProxyConfig: "http://proxy.invalid:8080"},
|
|
}, "missing-proxy")
|
|
if ok {
|
|
t.Fatalf("expected missing proxyId to fail validation")
|
|
}
|
|
if !strings.Contains(msg, "不存在") {
|
|
t.Fatalf("unexpected message: %s", msg)
|
|
}
|
|
}
|
|
|
|
func TestValidateProxyConfigMissingProxyIdFallbackToRawConfig(t *testing.T) {
|
|
ok, msg := ValidateProxyConfig("socks5://127.0.0.1:1080", []config.BrowserProxy{
|
|
{ProxyId: "p1", ProxyConfig: "http://proxy.invalid:8080"},
|
|
}, "missing-proxy")
|
|
if !ok {
|
|
t.Fatalf("expected fallback proxyConfig to pass, msg=%s", msg)
|
|
}
|
|
}
|
|
|
|
func TestValidateProxyConfigStandardProxy(t *testing.T) {
|
|
ok, msg := ValidateProxyConfig("socks5://127.0.0.1:1080", nil, "")
|
|
if !ok {
|
|
t.Fatalf("expected standard proxy to pass: %s", msg)
|
|
}
|
|
}
|
|
|
|
func TestValidateProxyConfigChainSocks5Proxy(t *testing.T) {
|
|
chainConfig := buildTestChainSocks5Config(t, 0)
|
|
ok, msg := ValidateProxyConfig(chainConfig, nil, "")
|
|
if !ok {
|
|
t.Fatalf("expected chain proxy to pass: %s", msg)
|
|
}
|
|
if !RequiresBridge(chainConfig, nil, "") {
|
|
t.Fatalf("expected chain proxy to require bridge")
|
|
}
|
|
}
|
|
|
|
func TestValidateProxyConfigChainHTTPProxy(t *testing.T) {
|
|
chainConfig := buildTestChainHTTPConfig(t)
|
|
ok, msg := ValidateProxyConfig(chainConfig, nil, "")
|
|
if !ok {
|
|
t.Fatalf("expected chain http proxy to pass: %s", msg)
|
|
}
|
|
if !RequiresBridge(chainConfig, nil, "") {
|
|
t.Fatalf("expected chain http proxy to require bridge")
|
|
}
|
|
}
|
|
|
|
func TestRequiresLocalProxyBridgeForBrowserAuthenticatedSocks5(t *testing.T) {
|
|
if !RequiresLocalProxyBridgeForBrowser("socks5://user:pass@127.0.0.1:1080") {
|
|
t.Fatal("expected authenticated socks5 proxy to require browser bridge")
|
|
}
|
|
if !RequiresLocalProxyBridgeForBrowser("http://user:pass@127.0.0.1:8080") {
|
|
t.Fatal("expected authenticated http proxy to require browser bridge")
|
|
}
|
|
if RequiresLocalProxyBridgeForBrowser("socks5://127.0.0.1:1080") {
|
|
t.Fatal("expected unauthenticated socks5 proxy not to require browser bridge")
|
|
}
|
|
if RequiresLocalProxyBridgeForBrowser("http://127.0.0.1:8080") {
|
|
t.Fatal("expected unauthenticated http proxy not to require browser bridge")
|
|
}
|
|
}
|
|
|
|
func TestBuildDirectProxyBridgeOutboundAuthenticatedSocks5(t *testing.T) {
|
|
outbound, ok, err := buildDirectProxyBridgeOutbound("socks5://user:pass@127.0.0.1:1080")
|
|
if err != nil {
|
|
t.Fatalf("buildDirectProxyBridgeOutbound returned error: %v", err)
|
|
}
|
|
if !ok {
|
|
t.Fatal("expected authenticated socks5 proxy to build bridge outbound")
|
|
}
|
|
|
|
if outbound["protocol"] != "socks" {
|
|
t.Fatalf("protocol = %v, want socks", outbound["protocol"])
|
|
}
|
|
settings, ok := outbound["settings"].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("settings missing: %+v", outbound)
|
|
}
|
|
servers, ok := settings["servers"].([]interface{})
|
|
if !ok || len(servers) != 1 {
|
|
t.Fatalf("servers invalid: %+v", settings["servers"])
|
|
}
|
|
server, ok := servers[0].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("server invalid: %+v", servers[0])
|
|
}
|
|
if server["address"] != "127.0.0.1" {
|
|
t.Fatalf("address = %v, want 127.0.0.1", server["address"])
|
|
}
|
|
if got := int(server["port"].(int)); got != 1080 {
|
|
t.Fatalf("port = %d, want 1080", got)
|
|
}
|
|
users, ok := server["users"].([]interface{})
|
|
if !ok || len(users) != 1 {
|
|
t.Fatalf("users invalid: %+v", server["users"])
|
|
}
|
|
user, ok := users[0].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("user invalid: %+v", users[0])
|
|
}
|
|
if user["user"] != "user" || user["pass"] != "pass" {
|
|
t.Fatalf("unexpected user payload: %+v", user)
|
|
}
|
|
}
|
|
|
|
func TestBuildDirectProxyBridgeOutboundAuthenticatedHTTP(t *testing.T) {
|
|
outbound, ok, err := buildDirectProxyBridgeOutbound("http://user:pass@127.0.0.1:8080")
|
|
if err != nil {
|
|
t.Fatalf("buildDirectProxyBridgeOutbound returned error: %v", err)
|
|
}
|
|
if !ok {
|
|
t.Fatal("expected bridge outbound for authenticated http proxy")
|
|
}
|
|
if outbound["protocol"] != "http" {
|
|
t.Fatalf("protocol = %v, want http", outbound["protocol"])
|
|
}
|
|
settings, ok := outbound["settings"].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("settings missing: %+v", outbound)
|
|
}
|
|
servers, ok := settings["servers"].([]interface{})
|
|
if !ok || len(servers) != 1 {
|
|
t.Fatalf("servers invalid: %+v", settings["servers"])
|
|
}
|
|
server, ok := servers[0].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("server invalid: %+v", servers[0])
|
|
}
|
|
users, ok := server["users"].([]interface{})
|
|
if !ok || len(users) != 1 {
|
|
t.Fatalf("users invalid: %+v", server["users"])
|
|
}
|
|
user, ok := users[0].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("user invalid: %+v", users[0])
|
|
}
|
|
if user["user"] != "user" || user["pass"] != "pass" {
|
|
t.Fatalf("unexpected user payload: %+v", user)
|
|
}
|
|
}
|
|
|
|
func buildTestChainSocks5Config(t *testing.T, localPort int) string {
|
|
t.Helper()
|
|
localPortField := ""
|
|
if localPort > 0 {
|
|
localPortField = fmt.Sprintf(`,"localPort":%d`, localPort)
|
|
}
|
|
raw := fmt.Sprintf(`{"first":{"protocol":"socks5","server":"127.0.0.1","port":1081,"username":"u1","password":"p1"},"second":{"protocol":"socks5","server":"127.0.0.2","port":1082}%s}`, localPortField)
|
|
return "chain+socks5://" + url.QueryEscape(raw)
|
|
}
|
|
|
|
func buildTestChainHTTPConfig(t *testing.T) string {
|
|
t.Helper()
|
|
raw := `{"first":{"protocol":"http","server":"first-hop.invalid","port":8080,"username":"u1","password":"p1"},"second":{"protocol":"http","server":"second-hop.invalid","port":8081,"username":"u2","password":"p2"}}`
|
|
return "chain+socks5://" + url.QueryEscape(raw)
|
|
}
|
|
|
|
func TestDirectProxyBridgeIgnoresClashYAML(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
src := "name: JP-Vmess(Dmit) 1.5x\n" +
|
|
"type: vmess\n" +
|
|
"server: example.com\n" +
|
|
"port: 10091\n" +
|
|
"uuid: 5ef4299f-a5eb-4aaa-9bf5-60b541b19294\n" +
|
|
"alterId: 0\n" +
|
|
"cipher: auto\n" +
|
|
"tls: true\n" +
|
|
"network: ws\n" +
|
|
"ws-opts:\n" +
|
|
" path: /\n" +
|
|
" headers:\n" +
|
|
" Host: example.com\n"
|
|
|
|
outbound, ok, err := buildDirectProxyBridgeOutbound(src)
|
|
if err != nil {
|
|
t.Fatalf("buildDirectProxyBridgeOutbound returned error: %v", err)
|
|
}
|
|
if ok || outbound != nil {
|
|
t.Fatalf("clash yaml must not be treated as direct proxy bridge: ok=%v outbound=%v", ok, outbound)
|
|
}
|
|
|
|
standard, parsedOutbound, err := ParseProxyNode(src)
|
|
if err != nil {
|
|
t.Fatalf("ParseProxyNode returned error: %v", err)
|
|
}
|
|
if standard != "" {
|
|
t.Fatalf("expected xray outbound, got standard proxy %q", standard)
|
|
}
|
|
if parsedOutbound == nil || parsedOutbound["protocol"] != "vmess" {
|
|
t.Fatalf("expected vmess outbound, got %#v", parsedOutbound)
|
|
}
|
|
}
|