feat(all): configure proxies, reuse native HTTP and stabilize recovery

Persist direct/manual proxy settings in Client Studio and apply them to resource,
Unity and native requests, with loopback bypass and no automatic system proxy.
Pool WinHTTP/curl connections, bound workers and deadlines, cancel safely,
and log queue and transport timings without exposing sensitive paths.
Return explicit relay failures, probe through native HTTP, retry interrupted
requests and recognize completed home, field and pack scenes during recovery.
This commit is contained in:
2026-10-04 20:10:01 +08:00
parent aec053f7d2
commit e4276eb493
23 changed files with 1001 additions and 410 deletions
+41
View File
@@ -7,6 +7,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"sync/atomic"
"testing"
clientconfig "bd2server/internal/client/config"
@@ -171,3 +172,43 @@ func TestOfficialDoesNotContactServer(t *testing.T) {
t.Fatalf("policy=%+v err=%v", policy, err)
}
}
func TestResourcePolicyLoopbackBypassesPlayerAndEnvironmentProxy(t *testing.T) {
var proxyCalls atomic.Int32
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
proxyCalls.Add(1)
w.WriteHeader(http.StatusBadGateway)
}))
defer proxy.Close()
t.Setenv("HTTP_PROXY", proxy.URL)
t.Setenv("HTTPS_PROXY", proxy.URL)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_ = json.NewEncoder(w).Encode(ResourcePolicy{Mode: clientconfig.CDNServer, ServerDataURL: "https://cdn.example/ServerData", GameDataURL: "https://cdn.example/GameData", BundleVersion: testVersions().BundleVersion, GameDataVersion: testVersions().GameDataVersion})
}))
defer server.Close()
for _, configured := range []string{"", proxy.URL} {
_, err := FetchResourcePolicy(context.Background(), nil, clientconfig.Settings{ServerOrigin: server.URL, CDNMode: clientconfig.CDNServer, ProxyURL: configured}, testVersions())
if err != nil {
t.Fatal(err)
}
}
if proxyCalls.Load() != 0 {
t.Fatal("loopback policy request used proxy")
}
}
func TestResourcePolicyUsesExplicitProxyForRemoteOrigin(t *testing.T) {
var proxyCalls atomic.Int32
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodConnect || r.Host != "remote.invalid:443" {
t.Errorf("unexpected proxy request: %s %s", r.Method, r.Host)
}
proxyCalls.Add(1)
w.WriteHeader(http.StatusBadGateway)
}))
defer proxy.Close()
_, err := FetchResourcePolicy(context.Background(), nil, clientconfig.Settings{ServerOrigin: "https://remote.invalid", CDNMode: clientconfig.CDNServer, ProxyURL: proxy.URL}, testVersions())
if err == nil || proxyCalls.Load() != 1 {
t.Fatal("remote policy request did not use explicit HTTP proxy")
}
}