mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-18 04:47:28 +00:00
fix(cache): harden cache backends, resilience, recovery, and transports
- RESP -ERR no longer closes healthy conn / aborts batch; global cap replacement-aware - global health-check pass serialized (latent data race); MemoryMonitor via ResourceManager - token-exchange retry, open-circuit rejections counted as failures - TLS-posture matching transport reuse (no silent InsecureSkipVerify handoff)
This commit is contained in:
@@ -118,7 +118,7 @@ func DefaultTransportConfig() TransportConfig {
|
||||
func (p *TransportPool) GetTransport(config TransportConfig) *http.Transport {
|
||||
// Check client limit
|
||||
if atomic.LoadInt32(&p.clientCount) >= p.maxClients {
|
||||
return p.getExistingTransport()
|
||||
return p.getExistingTransport(config)
|
||||
}
|
||||
|
||||
key := p.configKey(config)
|
||||
@@ -180,20 +180,41 @@ func (p *TransportPool) ReleaseTransport(transport *http.Transport) {
|
||||
}
|
||||
|
||||
// getExistingTransport returns any available transport when limit is reached
|
||||
func (p *TransportPool) getExistingTransport() *http.Transport {
|
||||
func (p *TransportPool) getExistingTransport(config TransportConfig) *http.Transport {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
// Prefer a transport whose TLS settings (verification, min version)
|
||||
// match the request. Reusing a mismatched transport at cap could hand a
|
||||
// strict-verifying caller an InsecureSkipVerify transport (silent cert
|
||||
// verification bypass) or an insecure caller a verifying one (R146).
|
||||
for _, shared := range p.transports {
|
||||
if shared != nil && shared.transport != nil {
|
||||
if shared != nil && shared.transport != nil && p.tlsMatches(config, shared.config) {
|
||||
atomic.AddInt32(&shared.refCount, 1)
|
||||
shared.lastUsed = time.Now()
|
||||
return shared.transport
|
||||
}
|
||||
}
|
||||
// No TLS-matching pooled transport: return nil so the caller falls back
|
||||
// to a safe basic client (http.DefaultTransport verifies certs) rather
|
||||
// than receiving a mismatched peer. Bounded by the cap (R146).
|
||||
return nil
|
||||
}
|
||||
|
||||
// tlsMatches reports whether two transports share the same TLS posture
|
||||
// (certificate verification and minimum protocol version), normalized for
|
||||
// createTransport's 0-means-TLS1.2 default.
|
||||
func (p *TransportPool) tlsMatches(a, b TransportConfig) bool {
|
||||
amin, bmin := a.MinTLSVersion, b.MinTLSVersion
|
||||
if amin == 0 {
|
||||
amin = tls.VersionTLS12
|
||||
}
|
||||
if bmin == 0 {
|
||||
bmin = tls.VersionTLS12
|
||||
}
|
||||
return a.InsecureSkipVerify == b.InsecureSkipVerify && amin == bmin
|
||||
}
|
||||
|
||||
// createTransport creates a new HTTP transport with the given config
|
||||
func (p *TransportPool) createTransport(config TransportConfig) *http.Transport {
|
||||
// Set secure defaults
|
||||
@@ -276,6 +297,28 @@ func (p *TransportPool) configKey(config TransportConfig) string {
|
||||
} else {
|
||||
sb.WriteByte(0)
|
||||
}
|
||||
// Include MinTLSVersion and every timeout so two configs differing only
|
||||
// in TLS minimum version or timeouts do NOT collide on one key and
|
||||
// silently share a transport with whoever-arrived-first's settings
|
||||
// (a possible TLS-version downgrade). Mirror http_client_pool.go's
|
||||
// all-field key (R146).
|
||||
sb.WriteByte('|')
|
||||
if config.MinTLSVersion == 0 {
|
||||
sb.WriteString("tls12")
|
||||
} else {
|
||||
sb.WriteString(strconv.Itoa(int(config.MinTLSVersion)))
|
||||
}
|
||||
for _, d := range []time.Duration{
|
||||
config.DialTimeout,
|
||||
config.TLSHandshakeTimeout,
|
||||
config.ResponseHeaderTimeout,
|
||||
config.ExpectContinueTimeout,
|
||||
config.IdleConnTimeout,
|
||||
config.KeepAlive,
|
||||
} {
|
||||
sb.WriteByte('|')
|
||||
sb.WriteString(strconv.FormatInt(int64(d), 10))
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user