From de094fb63e5f6d477de419aeab1ac9c17dcfcbb2 Mon Sep 17 00:00:00 2001 From: milesians <37968554+Milesians@users.noreply.github.com> Date: Mon, 20 Jul 2026 22:33:08 +0800 Subject: [PATCH] fix: use dual-stack fallback for proxy probe --- .../repository/proxy_probe_service.go | 21 +++++++------- .../repository/proxy_probe_service_test.go | 28 +++++++++---------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/backend/internal/repository/proxy_probe_service.go b/backend/internal/repository/proxy_probe_service.go index d877abde59..245eaade81 100644 --- a/backend/internal/repository/proxy_probe_service.go +++ b/backend/internal/repository/proxy_probe_service.go @@ -48,10 +48,10 @@ const ( // 某些 AI API 专用代理只允许访问特定域名,因此需要多个备选 var probeURLs = []struct { url string - parser string // "ip-api" or "httpbin" + parser string // "ip-api" or "ipify" }{ {"http://ip-api.com/json/?lang=zh-CN", "ip-api"}, - {"http://httpbin.org/ip", "httpbin"}, + {"http://api64.ipify.org?format=json", "ipify"}, } type proxyProbeService struct { @@ -119,8 +119,8 @@ func (s *proxyProbeService) probeWithURL(ctx context.Context, client *http.Clien switch parser { case "ip-api": return s.parseIPAPI(body, latencyMs) - case "httpbin": - return s.parseHTTPBin(body, latencyMs) + case "ipify": + return s.parseIPify(body, latencyMs) default: return nil, latencyMs, fmt.Errorf("unknown parser: %s", parser) } @@ -165,18 +165,17 @@ func (s *proxyProbeService) parseIPAPI(body []byte, latencyMs int64) (*service.P }, latencyMs, nil } -func (s *proxyProbeService) parseHTTPBin(body []byte, latencyMs int64) (*service.ProxyExitInfo, int64, error) { - // httpbin.org/ip 返回格式: {"origin": "1.2.3.4"} +func (s *proxyProbeService) parseIPify(body []byte, latencyMs int64) (*service.ProxyExitInfo, int64, error) { var result struct { - Origin string `json:"origin"` + IP string `json:"ip"` } if err := json.Unmarshal(body, &result); err != nil { - return nil, latencyMs, fmt.Errorf("failed to parse httpbin response: %w", err) + return nil, latencyMs, fmt.Errorf("failed to parse ipify response: %w", err) } - if result.Origin == "" { - return nil, latencyMs, fmt.Errorf("httpbin: no IP found in response") + if result.IP == "" { + return nil, latencyMs, fmt.Errorf("ipify: no IP found in response") } return &service.ProxyExitInfo{ - IP: result.Origin, + IP: result.IP, }, latencyMs, nil } diff --git a/backend/internal/repository/proxy_probe_service_test.go b/backend/internal/repository/proxy_probe_service_test.go index 7450653b7b..c778f6c1eb 100644 --- a/backend/internal/repository/proxy_probe_service_test.go +++ b/backend/internal/repository/proxy_probe_service_test.go @@ -71,24 +71,24 @@ func (s *ProxyProbeServiceSuite) TestProbeProxy_Success_IPAPI() { require.Equal(s.T(), "CC", info.CountryCode) } -func (s *ProxyProbeServiceSuite) TestProbeProxy_Success_HTTPBinFallback() { +func (s *ProxyProbeServiceSuite) TestProbeProxy_Success_IPifyFallback() { s.setupProxyServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // ip-api 失败 if strings.Contains(r.RequestURI, "ip-api.com") { w.WriteHeader(http.StatusServiceUnavailable) return } - // httpbin 成功 - if strings.Contains(r.RequestURI, "httpbin.org") { + // ipify 成功 + if strings.Contains(r.RequestURI, "api64.ipify.org") { w.Header().Set("Content-Type", "application/json") - _, _ = io.WriteString(w, `{"origin": "5.6.7.8"}`) + _, _ = io.WriteString(w, `{"ip": "5.6.7.8"}`) return } w.WriteHeader(http.StatusServiceUnavailable) })) info, latencyMs, err := s.prober.ProbeProxy(s.ctx, s.proxySrv.URL) - require.NoError(s.T(), err, "ProbeProxy should fallback to httpbin") + require.NoError(s.T(), err, "ProbeProxy should fallback to ipify") require.GreaterOrEqual(s.T(), latencyMs, int64(0), "unexpected latency") require.Equal(s.T(), "5.6.7.8", info.IP) } @@ -110,8 +110,8 @@ func (s *ProxyProbeServiceSuite) TestProbeProxy_InvalidJSON() { _, _ = io.WriteString(w, "not-json") return } - // httpbin 也返回无效响应 - if strings.Contains(r.RequestURI, "httpbin.org") { + // ipify 也返回无效响应 + if strings.Contains(r.RequestURI, "api64.ipify.org") { w.Header().Set("Content-Type", "application/json") _, _ = io.WriteString(w, "not-json") return @@ -151,17 +151,17 @@ func (s *ProxyProbeServiceSuite) TestParseIPAPI_Failure() { require.ErrorContains(s.T(), err, "rate limited") } -func (s *ProxyProbeServiceSuite) TestParseHTTPBin_Success() { - body := []byte(`{"origin": "9.8.7.6"}`) - info, latencyMs, err := s.prober.parseHTTPBin(body, 50) +func (s *ProxyProbeServiceSuite) TestParseIPify_Success() { + body := []byte(`{"ip": "2001:db8::1"}`) + info, latencyMs, err := s.prober.parseIPify(body, 50) require.NoError(s.T(), err) require.Equal(s.T(), int64(50), latencyMs) - require.Equal(s.T(), "9.8.7.6", info.IP) + require.Equal(s.T(), "2001:db8::1", info.IP) } -func (s *ProxyProbeServiceSuite) TestParseHTTPBin_NoIP() { - body := []byte(`{"origin": ""}`) - _, _, err := s.prober.parseHTTPBin(body, 50) +func (s *ProxyProbeServiceSuite) TestParseIPify_NoIP() { + body := []byte(`{"ip": ""}`) + _, _, err := s.prober.parseIPify(body, 50) require.Error(s.T(), err) require.ErrorContains(s.T(), err, "no IP found") }