Repository: caddyserver/caddy
File: listeners.go (Go)

Code before:
}

// sharedQuicTlsConfig manages GetConfigForClient
type sharedQuicTlsConfig struct {
	rmu           sync.RWMutex
	tlsConfs      map[*tls.Config]contextAndCancelFunc
	activeTlsConf *tls.Config
}

// newSqtc creates a new sharedQuicTlsConfig
func newSqtc(tlsConfig *tls.Config) *sharedQuicTlsConfig {
	sqtc := &sharedQuicTlsConfig{
		tlsConfs:      make(map[*tls.Config]contextAndCancelFunc),
		activeTlsConf: tlsConfig,
	}
	sqtc.addTlsConfig(tlsConfig)
	return sqtc
}

func (sqtc *sharedQuicTlsConfig) getConfigForClient(ch *tls.ClientHelloInfo) (*tls.Config, error) {
	sqtc.rmu.RLock()
	defer sqtc.rmu.RUnlock()
	return sqtc.activeTlsConf.GetConfigForClient(ch)
}

func (sqtc *sharedQuicTlsConfig) addTlsConfig(tlsConfig *tls.Config) (context.Context, context.CancelFunc) {
	sqtc.rmu.Lock()
	defer sqtc.rmu.Unlock()

	if cacc, ok := sqtc.tlsConfs[tlsConfig]; ok {
		return cacc.Context, cacc.CancelFunc
	}

	ctx, cancel := context.WithCancel(context.Background())
	wrappedCancel := func() {
		cancel()

		sqtc.rmu.Lock()
		defer sqtc.rmu.Unlock()

		delete(sqtc.tlsConfs, tlsConfig)
		if sqtc.activeTlsConf == tlsConfig {
			// select another tls.Config, if there is none,
			// related sharedQuicListener will be destroyed anyway
			for tc := range sqtc.tlsConfs {
				sqtc.activeTlsConf = tc
				break
			}
		}
	}
	sqtc.tlsConfs[tlsConfig] = contextAndCancelFunc{ctx, wrappedCancel}

Review comment (suggestion):
```suggestion
func (sqtc *sharedQuicTlsConfig) addTLSConfig(tlsConfig *tls.Config) (context.Context, context.CancelFunc) {
```

Code after:
// ListenerUsage returns the current usage count of the given listener address.
func ListenerUsage(network, addr string) int {
	count, _ := listenerPool.References(listenerKey(network, addr))
	return count
}

// contextAndCancelFunc groups context and its cancelFunc
type contextAndCancelFunc struct {
	context.Context
	context.CancelFunc
}

// sharedQUICTLSConfig manages GetConfigForClient
// see issue: https://github.com/caddyserver/caddy/pull/4849
type sharedQUICTLSConfig struct {
	rmu           sync.RWMutex
	tlsConfs      map[*tls.Config]contextAndCancelFunc
	activeTlsConf *tls.Config
}

// newSharedQUICTLSConfig creates a new sharedQUICTLSConfig
func newSharedQUICTLSConfig(tlsConfig *tls.Config) *sharedQUICTLSConfig {
	sqtc := &sharedQUICTLSConfig{
		tlsConfs:      make(map[*tls.Config]contextAndCancelFunc),
		activeTlsConf: tlsConfig,
	}
	sqtc.addTLSConfig(tlsConfig)
	return sqtc
}

// getConfigForClient is used as tls.Config's GetConfigForClient field
func (sqtc *sharedQUICTLSConfig) getConfigForClient(ch *tls.ClientHelloInfo) (*tls.Config, error) {
	sqtc.rmu.RLock()
	defer sqtc.rmu.RUnlock()
	return sqtc.activeTlsConf.GetConfigForClient(ch)
}

// addTLSConfig adds tls.Config to the map if not present and returns the corresponding context and its cancelFunc
// so that when cancelled, the active tls.Config will change
func (sqtc *sharedQUICTLSConfig) addTLSConfig(tlsConfig *tls.Config) (context.Context, context.CancelFunc) {
	sqtc.rmu.Lock()
	defer sqtc.rmu.Unlock()

	if cacc, ok := sqtc.tlsConfs[tlsConfig]; ok {
		return cacc.Context, cacc.CancelFunc
	}

	ctx, cancel := context.WithCancel(context.Background())
	wrappedCancel := func() {
		cancel()