Fetch retrieves a cached certificate for the given hostname, or generates and caches a new one using the provided generator function. Uses singleflight to ensure concurrent requests for the same hostname share a single in-flight generation rather than waiting on a mutex. This means only one gorouti
(hostname string, genFunc func() (*tls.Certificate, error))
| 32 | // a single in-flight generation rather than waiting on a mutex. This means only |
| 33 | // one goroutine generates the certificate while others wait on the result directly. |
| 34 | func (c *CertCache) Fetch(hostname string, genFunc func() (*tls.Certificate, error)) (*tls.Certificate, error) { |
| 35 | // Cache hit: check cache with read lock. |
| 36 | c.mu.RLock() |
| 37 | cert, ok := c.certs[hostname] |
| 38 | c.mu.RUnlock() |
| 39 | if ok { |
| 40 | return cert, nil |
| 41 | } |
| 42 | |
| 43 | // Cache miss: use singleflight to ensure only one goroutine generates |
| 44 | // the certificate for a given hostname, even under concurrent requests. |
| 45 | cert, err, _ := c.singleFlight.Do(hostname, func() (*tls.Certificate, error) { |
| 46 | // Double-check cache inside singleflight in case another call |
| 47 | // already populated it. |
| 48 | c.mu.RLock() |
| 49 | if cert, ok := c.certs[hostname]; ok { |
| 50 | c.mu.RUnlock() |
| 51 | return cert, nil |
| 52 | } |
| 53 | c.mu.RUnlock() |
| 54 | |
| 55 | cert, err := genFunc() |
| 56 | if err != nil { |
| 57 | return nil, err |
| 58 | } |
| 59 | if cert == nil { |
| 60 | return nil, xerrors.New("generator function returned nil certificate") |
| 61 | } |
| 62 | |
| 63 | c.mu.Lock() |
| 64 | c.certs[hostname] = cert |
| 65 | c.mu.Unlock() |
| 66 | |
| 67 | return cert, nil |
| 68 | }) |
| 69 | |
| 70 | return cert, err |
| 71 | } |