feat(sync): full code sync from release
This commit is contained in:
@@ -32,6 +32,7 @@ const (
|
||||
defaultMaxIdleConns = 100 // 最大空闲连接数
|
||||
defaultMaxIdleConnsPerHost = 10 // 每个主机最大空闲连接数
|
||||
defaultIdleConnTimeout = 90 * time.Second // 空闲连接超时时间(建议小于上游 LB 超时)
|
||||
validatedHostTTL = 30 * time.Second // DNS Rebinding 校验缓存 TTL
|
||||
)
|
||||
|
||||
// Options 定义共享 HTTP 客户端的构建参数
|
||||
@@ -53,6 +54,9 @@ type Options struct {
|
||||
// sharedClients 存储按配置参数缓存的 http.Client 实例
|
||||
var sharedClients sync.Map
|
||||
|
||||
// 允许测试替换校验函数,生产默认指向真实实现。
|
||||
var validateResolvedIP = urlvalidator.ValidateResolvedIP
|
||||
|
||||
// GetClient 返回共享的 HTTP 客户端实例
|
||||
// 性能优化:相同配置复用同一客户端,避免重复创建 Transport
|
||||
// 安全说明:代理配置失败时直接返回错误,不会回退到直连,避免 IP 关联风险
|
||||
@@ -84,7 +88,7 @@ func buildClient(opts Options) (*http.Client, error) {
|
||||
|
||||
var rt http.RoundTripper = transport
|
||||
if opts.ValidateResolvedIP && !opts.AllowPrivateHosts {
|
||||
rt = &validatedTransport{base: transport}
|
||||
rt = newValidatedTransport(transport)
|
||||
}
|
||||
return &http.Client{
|
||||
Transport: rt,
|
||||
@@ -149,17 +153,56 @@ func buildClientKey(opts Options) string {
|
||||
}
|
||||
|
||||
type validatedTransport struct {
|
||||
base http.RoundTripper
|
||||
base http.RoundTripper
|
||||
validatedHosts sync.Map // map[string]time.Time, value 为过期时间
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func newValidatedTransport(base http.RoundTripper) *validatedTransport {
|
||||
return &validatedTransport{
|
||||
base: base,
|
||||
now: time.Now,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *validatedTransport) isValidatedHost(host string, now time.Time) bool {
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
raw, ok := t.validatedHosts.Load(host)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
expireAt, ok := raw.(time.Time)
|
||||
if !ok {
|
||||
t.validatedHosts.Delete(host)
|
||||
return false
|
||||
}
|
||||
if now.Before(expireAt) {
|
||||
return true
|
||||
}
|
||||
t.validatedHosts.Delete(host)
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *validatedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if req != nil && req.URL != nil {
|
||||
host := strings.TrimSpace(req.URL.Hostname())
|
||||
host := strings.ToLower(strings.TrimSpace(req.URL.Hostname()))
|
||||
if host != "" {
|
||||
if err := urlvalidator.ValidateResolvedIP(host); err != nil {
|
||||
return nil, err
|
||||
now := time.Now()
|
||||
if t != nil && t.now != nil {
|
||||
now = t.now()
|
||||
}
|
||||
if !t.isValidatedHost(host, now) {
|
||||
if err := validateResolvedIP(host); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.validatedHosts.Store(host, now.Add(validatedHostTTL))
|
||||
}
|
||||
}
|
||||
}
|
||||
if t == nil || t.base == nil {
|
||||
return nil, fmt.Errorf("validated transport base is nil")
|
||||
}
|
||||
return t.base.RoundTrip(req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user