package runner import ( "net/http" "net/url" "strings" "sync" "time" "github.com/easyai/easyai-ai-gateway/apps/api/internal/clients" "github.com/easyai/easyai-ai-gateway/apps/api/internal/netproxy" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) type httpClientCache struct { none *http.Client global *http.Client mu sync.Mutex custom map[string]*http.Client } func newHTTPClientCache() *httpClientCache { return &httpClientCache{ none: newHTTPClient(nil), global: newHTTPClient(http.ProxyFromEnvironment), custom: map[string]*http.Client{}, } } func (s *Service) httpClientForCandidate(candidate store.RuntimeModelCandidate, simulated bool) (*http.Client, error) { if simulated { return s.httpClients.none, nil } config, err := netproxy.Normalize(netproxy.FromPlatformConfig(candidate.PlatformConfig)) if err != nil { return nil, &clients.ClientError{Code: "invalid_proxy", Message: err.Error(), Retryable: false} } switch config.Mode { case netproxy.ModeGlobal: if strings.TrimSpace(s.cfg.GlobalHTTPProxy) != "" { return s.httpClients.customClient(s.cfg.GlobalHTTPProxy) } return s.httpClients.global, nil case netproxy.ModeCustom: return s.httpClients.customClient(config.HTTPProxy) default: return s.httpClients.none, nil } } func (c *httpClientCache) customClient(rawProxy string) (*http.Client, error) { normalized, proxyURL, err := netproxy.ParseHTTPProxy(rawProxy) if err != nil { return nil, &clients.ClientError{Code: "invalid_proxy", Message: err.Error(), Retryable: false} } c.mu.Lock() defer c.mu.Unlock() if client := c.custom[normalized]; client != nil { return client, nil } client := newHTTPClient(http.ProxyURL(proxyURL)) c.custom[normalized] = client return client, nil } func newHTTPClient(proxy func(*http.Request) (*url.URL, error)) *http.Client { transport := http.DefaultTransport.(*http.Transport).Clone() transport.Proxy = proxy return &http.Client{ Timeout: 120 * time.Second, Transport: transport, } }