package oci import ( "fmt" "net" "net/http" "net/url" "time" "golang.org/x/net/proxy" "github.com/oracle/oci-go-sdk/v65/common" ) // ProxySpec 是租户关联的出站代理;密码已解密,仅在进程内传递。 type ProxySpec struct { Type string // "socks5" / "http" / "https" Host string Port int Username string Password string } // proxyClientTimeout 与 SDK 默认 HTTPClient 超时保持一致。 const proxyClientTimeout = 60 * time.Second // 阶段超时对齐 SDK 直连 Transport 模板(transport_template_provider):连不上的 // 代理快速失败,而不是拖满总超时;responses 直通去掉总超时后这是建立阶段的兜底之一。 const ( proxyDialTimeout = 30 * time.Second proxyTLSHandshakeTimeout = 10 * time.Second ) // applyProxy 在 SDK client 构造后统一挂出站代理;未关联代理时不动默认配置。 // 所有 New*ClientWithConfigurationProvider 调用点构造成功后都必须经过这里。 func applyProxy(base *common.BaseClient, cred Credentials) { if hc := proxyHTTPClient(cred.Proxy); hc != nil { base.HTTPClient = hc } } // proxyHTTPClient 按代理配置构造 http.Client;nil 或非法配置返回 nil(走直连)。 func proxyHTTPClient(p *ProxySpec) *http.Client { if p == nil || p.Host == "" || p.Port <= 0 { return nil } tr := transportFor(p) if tr == nil { return nil } return &http.Client{Transport: tr, Timeout: proxyClientTimeout} } // HTTPClientFor 供面板自身请求(如代理出口地理探测)复用与租户 SDK // 完全一致的代理链路;nil 或非法配置返回 nil。 func HTTPClientFor(p *ProxySpec) *http.Client { return proxyHTTPClient(p) } // transportFor 构造代理 Transport:http / https 走 CONNECT,socks5 走拨号器。 func transportFor(p *ProxySpec) *http.Transport { addr := fmt.Sprintf("%s:%d", p.Host, p.Port) dialer := &net.Dialer{Timeout: proxyDialTimeout} if p.Type == "http" || p.Type == "https" { u := &url.URL{Scheme: p.Type, Host: addr} if p.Username != "" { u.User = url.UserPassword(p.Username, p.Password) } return &http.Transport{ Proxy: http.ProxyURL(u), DialContext: dialer.DialContext, TLSHandshakeTimeout: proxyTLSHandshakeTimeout, } } var auth *proxy.Auth if p.Username != "" { auth = &proxy.Auth{User: p.Username, Password: p.Password} } d, err := proxy.SOCKS5("tcp", addr, auth, dialer) if err != nil { return nil } cd, ok := d.(proxy.ContextDialer) if !ok { return nil } return &http.Transport{ DialContext: cd.DialContext, TLSHandshakeTimeout: proxyTLSHandshakeTimeout, } }