package netproxy import ( "context" "net/http" "net/url" "path/filepath" "strings" "testing" "time" "github.com/yetone/magpie/internal/settings" ) func TestParseScutil(t *testing.T) { out := ` { ExceptionsList : { 0 : 127.0.0.1 1 : *.local } HTTPEnable : 1 HTTPPort : 7890 HTTPProxy : 127.0.0.1 HTTPSEnable : 1 HTTPSPort : 7891 HTTPSProxy : 127.0.0.1 SOCKSEnable : 1 SOCKSPort : 7892 SOCKSProxy : 127.0.0.1 }` p := parseScutil(out) if p.URL != "http://127.0.0.1:7891" || len(p.Bypass) != 2 || p.Bypass[1] != "*.local" { t.Fatalf("%+v", p) } if p := parseScutil(" {\n SOCKSEnable : 1\n SOCKSPort : 1080\n SOCKSProxy : 10.0.0.2\n}"); p.URL != "socks5://10.0.0.2:1080" { t.Fatalf("socks: %+v", p) } if p := parseScutil(" {\n HTTPEnable : 0\n HTTPProxy : x\n}"); p.URL != "" { t.Fatalf("off: %+v", p) } } func TestParseWindows(t *testing.T) { for server, want := range map[string]string{ "127.0.0.1:7890": "http://127.0.0.1:7890", "http=127.0.0.1:1;https=127.0.0.1:2": "http://127.0.0.1:2", "socks=127.0.0.1:1080": "socks5://127.0.0.1:1080", "": "", } { if got := parseWindows(server, "").URL; got != want { t.Errorf("%q: %q, want %q", server, got, want) } } p := parseWindows("127.0.0.1:7890", "*.corp;") if !bypassed("git.corp", p.Bypass) || !bypassed("intranet", p.Bypass) || bypassed("chatgpt.com", p.Bypass) { t.Fatalf("bypass: %+v", p.Bypass) } } func TestFor(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) for _, k := range []string{"HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy", "ALL_PROXY", "all_proxy", "NO_PROXY", "no_proxy"} { t.Setenv(k, "") } sysCache.at, sysCache.p = farFuture, Proxy{URL: "http://127.0.0.1:7890", Bypass: []string{"*.corp"}} t.Cleanup(func() { sysCache.at = zero }) u, _ := url.Parse("https://chatgpt.com/backend-api/codex/responses") if p, _ := For(u); p == nil || p.String() != "http://127.0.0.1:7890" { t.Fatalf("system: %v", p) } corp, _ := url.Parse("https://git.corp/x") if p, _ := For(corp); p != nil { t.Fatalf("bypass: %v", p) } local := &http.Request{URL: &url.URL{Scheme: "http", Host: "127.0.0.1:3425"}} if p, _ := Func(local); p != nil { t.Fatalf("loopback: %v", p) } // magpie's own setting wins; "direct" turns it off settings.Save(settings.Settings{Proxy: "socks5://127.0.0.1:1080"}) if p, _ := For(u); p == nil || p.String() != "socks5://127.0.0.1:1080" { t.Fatalf("setting: %v", p) } settings.Save(settings.Settings{Proxy: "direct"}) if p, _ := For(u); p != nil { t.Fatalf("direct: %v", p) } if err := settings.Save(settings.Settings{Proxy: "ftp://x"}); err == nil { t.Fatal("ftp proxy accepted") } if settings.Save(settings.Settings{Proxy: "127.0.0.1:7890"}) != nil || filepath.Dir(settings.Path()) != filepath.Join(dir, "magpie") { t.Fatal("host:port proxy refused") } } func TestEnv(t *testing.T) { t.Setenv("XDG_CONFIG_HOME", t.TempDir()) sysCache.at, sysCache.p = farFuture, Proxy{URL: "http://127.0.0.1:7890", Bypass: []string{"*.corp", ""}} t.Cleanup(func() { sysCache.at = zero }) get := func(env []string, k string) string { v := "" for _, e := range env { if key, val, _ := strings.Cut(e, "="); key == k { v = val } } return v } // the system's proxy, where the environment names none env := Env([]string{"PATH=/bin", "HOME=/h"}) if get(env, "HTTPS_PROXY") != "http://127.0.0.1:7890" || get(env, "all_proxy") != "http://127.0.0.1:7890" || get(env, "NO_PROXY") != "localhost,127.0.0.1,::1,.corp" || get(env, "PATH") != "/bin" { t.Fatalf("system: %v", env) } // one the environment names is kept env = Env([]string{"https_proxy=http://10.0.0.1:1", "NO_PROXY=x"}) if len(env) != 2 || get(env, "https_proxy") != "http://10.0.0.1:1" { t.Fatalf("environment: %v", env) } // magpie's own replaces the environment's settings.Save(settings.Settings{Proxy: "127.0.0.1:6152"}) env = Env([]string{"HTTPS_PROXY=http://10.0.0.1:1", "PATH=/bin"}) if get(env, "HTTPS_PROXY") != "http://127.0.0.1:6152" || get(env, "http_proxy") != "http://127.0.0.1:6152" || get(env, "NO_PROXY") != "localhost,127.0.0.1,::1" || len(env) != 9 { t.Fatalf("setting: %v", env) } // and "direct" leaves none settings.Save(settings.Settings{Proxy: "direct"}) if env = Env([]string{"HTTPS_PROXY=http://10.0.0.1:1", "PATH=/bin"}); len(env) != 1 { t.Fatalf("direct: %v", env) } } var farFuture = time.Now().Add(time.Hour) var zero time.Time // A provider's own proxy (#237) wins over the global one, "direct" over // both, and each proxy is given one transport of its own, kept. func TestWith(t *testing.T) { t.Setenv("XDG_CONFIG_HOME", t.TempDir()) settings.Save(settings.Settings{Proxy: "127.0.0.1:6152"}) u := &url.URL{Scheme: "https", Host: "api.openai.com"} req := func(choice string) *http.Request { return (&http.Request{URL: u}).WithContext(With(context.Background(), choice)) } if p, _ := Func(req("")); p == nil || p.String() != "http://127.0.0.1:6152" { t.Fatalf("follow: %v", p) } if p, _ := Func(req("socks5://127.0.0.1:1080")); p == nil || p.String() != "socks5://127.0.0.1:1080" { t.Fatalf("own: %v", p) } if p, _ := Func(req("direct")); p != nil { t.Fatalf("direct: %v", p) } local := (&http.Request{URL: &url.URL{Scheme: "http", Host: "127.0.0.1:3425"}}).WithContext(With(context.Background(), "127.0.0.1:1")) if p, _ := Func(local); p != nil { t.Fatalf("loopback: %v", p) } d := Dispatch(&http.Transport{Proxy: Func}).(*dispatch) a, b := d.transport("127.0.0.1:1"), d.transport("direct") if a == b || d.transport("127.0.0.1:1") != a || len(d.own) != 2 { t.Fatalf("transports: %d kept", len(d.own)) } if p, _ := a.Proxy(req("")); p == nil || p.Host != "127.0.0.1:1" { t.Fatalf("own transport: %v", p) } if p, _ := b.Proxy(req("")); p != nil { t.Fatalf("direct transport: %v", p) } get := func(env []string, k string) string { for _, e := range env { if key, val, _ := strings.Cut(e, "="); key == k { return val } } return "" } if env := EnvWith("socks5://127.0.0.1:1080", []string{"PATH=/bin"}); get(env, "HTTPS_PROXY") != "socks5://127.0.0.1:1080" { t.Fatalf("own env: %v", env) } if env := EnvWith("direct", []string{"HTTPS_PROXY=http://x:1", "PATH=/bin"}); len(env) != 1 { t.Fatalf("direct env: %v", env) } if env := EnvWith("", []string{"PATH=/bin"}); get(env, "HTTPS_PROXY") != "http://127.0.0.1:6152" { t.Fatalf("follow env: %v", env) } }