<commit_before>// +build !js

package webrtc

import (
	"sync"
	"time"

	"github.com/pion/ice"
	"github.com/pion/logging"
)

// ICEGatherer gathers local host, server reflexive and relay
// candidates, as well as enabling the retrieval of local Interactive
// Connectivity Establishment (ICE) parameters which can be
// exchanged in signaling.
type ICEGatherer struct {
	lock  sync.RWMutex
	state ICEGathererState

	validatedServers []*ice.URL

	agentIsTrickle bool
	agent          *ice.Agent

	portMin                   uint16
	portMax                   uint16
	candidateTypes            []ice.CandidateType
	connectionTimeout         *time.Duration
	keepaliveInterval         *time.Duration
	candidateSelectionTimeout *time.Duration
	hostAcceptanceMinWait     *time.Duration
	srflxAcceptanceMinWait    *time.Duration
	prflxAcceptanceMinWait    *time.Duration
	relayAcceptanceMinWait    *time.Duration
	loggerFactory             logging.LoggerFactory
	log                       logging.LeveledLogger
	networkTypes              []NetworkType

	onLocalCandidateHdlr func(candidate *ICECandidate)
	onStateChangeHdlr    func(state ICEGathererState)
}

// NewICEGatherer creates a new NewICEGatherer.
func NewICEGatherer(
	portMin uint16,
	portMax uint16,
	connectionTimeout,
	keepaliveInterval,
	candidateSelectionTimeout,
	hostAcceptanceMinWait,
	srflxAcceptanceMinWait,
	prflxAcceptanceMinWait,
	relayAcceptanceMinWait *time.Duration,
	loggerFactory logging.LoggerFactory,
	agentIsTrickle bool,
	networkTypes []NetworkType,
	opts ICEGatherOptions,
) (*ICEGatherer, error) {
	var validatedServers []*ice.URL
	if len(opts.ICEServers) > 0 {
		for _, server := range opts.ICEServers {
			url, err := server.urls()
			if err != nil {
				return nil, err
			}
			validatedServers = append(validatedServers, url...)
		}
	}

	candidateTypes := []ice.CandidateType{}
	if opts.ICEGatherPolicy == ICETransportPolicyRelay {
		candidateTypes = append(candidateTypes, ice.CandidateTypeRelay)
	}

	return &ICEGatherer{
		state:                     ICEGathererStateNew,
		validatedServers:          validatedServers,
		portMin:                   portMin,
		portMax:                   portMax,
		connectionTimeout:         connectionTimeout,
		keepaliveInterval:         keepaliveInterval,
		loggerFactory:             loggerFactory,
		log:                       loggerFactory.NewLogger("ice"),
		agentIsTrickle:            agentIsTrickle,
		networkTypes:              networkTypes,
		candidateTypes:            candidateTypes,
		candidateSelectionTimeout: candidateSelectionTimeout,
		hostAcceptanceMinWait:     hostAcceptanceMinWait,
		srflxAcceptanceMinWait:    srflxAcceptanceMinWait,
		prflxAcceptanceMinWait:    prflxAcceptanceMinWait,
		relayAcceptanceMinWait:    relayAcceptanceMinWait,
	}, nil
}

func (g *ICEGatherer) createAgent() error {
	g.lock.Lock()
	defer g.lock.Unlock()

	if g.agent != nil {
		return nil
	}

	config := &ice.AgentConfig{
		Trickle:                   g.agentIsTrickle,
		Urls:                      g.validatedServers,
		PortMin:                   g.portMin,
		PortMax:                   g.portMax,
		ConnectionTimeout:         g.connectionTimeout,
		KeepaliveInterval:         g.keepaliveInterval,
		LoggerFactory:             g.loggerFactory,
		CandidateTypes:            g.candidateTypes,
		CandidateSelectionTimeout: g.candidateSelectionTimeout,
		HostAcceptanceMinWait:     g.hostAcceptanceMinWait,
		SrflxAcceptanceMinWait:    g.srflxAcceptanceMinWait,
		PrflxAcceptanceMinWait:    g.prflxAcceptanceMinWait,
		RelayAcceptanceMinWait:    g.relayAcceptanceMinWait,
	}

	requestedNetworkTypes := g.networkTypes
	if len(requestedNetworkTypes) == 0 {
		requestedNetworkTypes = supportedNetworkTypes()
	}

	for _, typ := range requestedNetworkTypes {
		config.NetworkTypes = append(config.NetworkTypes, ice.NetworkType(typ))
	}

	agent, err := ice.NewAgent(config)
	if err != nil {
		return err
	}

	g.agent = agent
	if !g.agentIsTrickle {
		g.state = ICEGathererStateComplete
	}

	return nil
}

// Gather ICE candidates.
func (g *ICEGatherer) Gather() error {
	if err := g.createAgent(); err != nil {
		return err
	}

	g.lock.Lock()
	onLocalCandidateHdlr := g.onLocalCandidateHdlr
	isTrickle := g.agentIsTrickle
	agent := g.agent
	g.lock.Unlock()<commit_msg>Guard against nil OnLocalCandidate handler<commit_after>// +build !js

package webrtc

import (
	"sync"
	"time"

	"github.com/pion/ice"
	"github.com/pion/logging"
)

// ICEGatherer gathers local host, server reflexive and relay
// candidates, as well as enabling the retrieval of local Interactive
// Connectivity Establishment (ICE) parameters which can be
// exchanged in signaling.
type ICEGatherer struct {
	lock  sync.RWMutex
	state ICEGathererState

	validatedServers []*ice.URL

	agentIsTrickle bool
	agent          *ice.Agent

	portMin                   uint16
	portMax                   uint16
	candidateTypes            []ice.CandidateType
	connectionTimeout         *time.Duration
	keepaliveInterval         *time.Duration
	candidateSelectionTimeout *time.Duration
	hostAcceptanceMinWait     *time.Duration
	srflxAcceptanceMinWait    *time.Duration
	prflxAcceptanceMinWait    *time.Duration
	relayAcceptanceMinWait    *time.Duration
	loggerFactory             logging.LoggerFactory
	log                       logging.LeveledLogger
	networkTypes              []NetworkType

	onLocalCandidateHdlr func(candidate *ICECandidate)
	onStateChangeHdlr    func(state ICEGathererState)
}

// NewICEGatherer creates a new NewICEGatherer.
func NewICEGatherer(
	portMin uint16,
	portMax uint16,
	connectionTimeout,
	keepaliveInterval,
	candidateSelectionTimeout,
	hostAcceptanceMinWait,
	srflxAcceptanceMinWait,
	prflxAcceptanceMinWait,
	relayAcceptanceMinWait *time.Duration,
	loggerFactory logging.LoggerFactory,
	agentIsTrickle bool,
	networkTypes []NetworkType,
	opts ICEGatherOptions,
) (*ICEGatherer, error) {
	var validatedServers []*ice.URL
	if len(opts.ICEServers) > 0 {
		for _, server := range opts.ICEServers {
			url, err := server.urls()
			if err != nil {
				return nil, err
			}
			validatedServers = append(validatedServers, url...)
		}
	}

	candidateTypes := []ice.CandidateType{}
	if opts.ICEGatherPolicy == ICETransportPolicyRelay {
		candidateTypes = append(candidateTypes, ice.CandidateTypeRelay)
	}

	return &ICEGatherer{
		state:                     ICEGathererStateNew,
		validatedServers:          validatedServers,
		portMin:                   portMin,
		portMax:                   portMax,
		connectionTimeout:         connectionTimeout,
		keepaliveInterval:         keepaliveInterval,
		loggerFactory:             loggerFactory,
		log:                       loggerFactory.NewLogger("ice"),
		agentIsTrickle:            agentIsTrickle,
		networkTypes:              networkTypes,
		candidateTypes:            candidateTypes,
		candidateSelectionTimeout: candidateSelectionTimeout,
		hostAcceptanceMinWait:     hostAcceptanceMinWait,
		srflxAcceptanceMinWait:    srflxAcceptanceMinWait,
		prflxAcceptanceMinWait:    prflxAcceptanceMinWait,
		relayAcceptanceMinWait:    relayAcceptanceMinWait,
	}, nil
}

func (g *ICEGatherer) createAgent() error {
	g.lock.Lock()
	defer g.lock.Unlock()

	if g.agent != nil {
		return nil
	}

	config := &ice.AgentConfig{
		Trickle:                   g.agentIsTrickle,
		Urls:                      g.validatedServers,
		PortMin:                   g.portMin,
		PortMax:                   g.portMax,
		ConnectionTimeout:         g.connectionTimeout,
		KeepaliveInterval:         g.keepaliveInterval,
		LoggerFactory:             g.loggerFactory,
		CandidateTypes:            g.candidateTypes,
		CandidateSelectionTimeout: g.candidateSelectionTimeout,
		HostAcceptanceMinWait:     g.hostAcceptanceMinWait,
		SrflxAcceptanceMinWait:    g.srflxAcceptanceMinWait,
		PrflxAcceptanceMinWait:    g.prflxAcceptanceMinWait,
		RelayAcceptanceMinWait:    g.relayAcceptanceMinWait,
	}

	requestedNetworkTypes := g.networkTypes
	if len(requestedNetworkTypes) == 0 {
		requestedNetworkTypes = supportedNetworkTypes()
	}

	for _, typ := range requestedNetworkTypes {
		config.NetworkTypes = append(config.NetworkTypes, ice.NetworkType(typ))
	}

	agent, err := ice.NewAgent(config)
	if err != nil {
		return err
	}

	g.agent = agent
	if !g.agentIsTrickle {
		g.state = ICEGathererStateComplete
	}

	return nil
}

// Gather ICE candidates.
func (g *ICEGatherer) Gather() error {
	if err := g.createAgent(); err != nil {
		return err
	}

	g.lock.Lock()
	onLocalCandidateHdlr := g.onLocalCandidateHdlr
	if onLocalCandidateHdlr == nil {
		onLocalCandidateHdlr = func(*ICECandidate) {}
	}

	isTrickle := g.agentIsTrickle
	agent := g.agent
	g.lock.Unlock()
{'fraction_non_alphanumeric': 0.07895648158836431, 'fraction_numerical': 0.002308669052291354, 'mean_word_length': 3.177434908389585, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3896001', 'n_tokens_mistral': 2889, 'n_tokens_neox': 2728, 'n_words': 550}