Repository: pions/webrtc
Pull request #550 (2019-03-25 23:18:28)

Move ICE out of tree
Now that we have exported logging this is possible. Last export, nice work @backkem you started this process a long time back :)


Patch:
diff --git a/configuration.go b/configuration.go
index 34dcdcb2..adbb10d7 100644
--- a/configuration.go
+++ b/configuration.go
@@ -3,7 +3,7 @@
 package webrtc
 
 import (
-	"github.com/pions/webrtc/internal/ice"
+	"github.com/pions/ice"
 )
 
 // Configuration defines a set of parameters to configure how the
diff --git a/configuration_js.go b/configuration_js.go
index 3417432e..58491073 100644
--- a/configuration_js.go
+++ b/configuration_js.go
@@ -3,7 +3,7 @@
 package webrtc
 
 import (
-	"github.com/pions/webrtc/internal/ice"
+	"github.com/pions/ice"
 )
 
 // Configuration defines a set of parameters to configure how the
diff --git a/go.mod b/go.mod
index 9e8d86cf..3f8e09f0 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/pions/webrtc
 require (
 	github.com/pions/datachannel v1.2.0
 	github.com/pions/dtls v1.2.2
+	github.com/pions/ice v0.1.0
 	github.com/pions/logging v0.1.0
 	github.com/pions/quic v0.0.1
 	github.com/pions/rtcp v1.1.2
@@ -10,7 +11,7 @@ require (
 	github.com/pions/sctp v1.4.2
 	github.com/pions/sdp/v2 v2.1.0
 	github.com/pions/srtp v1.1.2
-	github.com/pions/stun v0.2.0
+	github.com/pions/stun v0.2.0 // indirect
 	github.com/pions/transport v0.5.0
 	github.com/stretchr/testify v1.3.0
 )
diff --git a/go.sum b/go.sum
index 54d247c2..55584013 100644
--- a/go.sum
+++ b/go.sum
@@ -23,6 +23,8 @@ github.com/pions/datachannel v1.2.0 h1:N12qhHSRVlgBcaal2Hi4skdz7VI4yz6bNC5IJDMzC
 github.com/pions/datachannel v1.2.0/go.mod h1:MKPEKJRwX/a9/tyQvcVTUI9szyf8ZuUyZxSA9AVMSro=
 github.com/pions/dtls v1.2.2 h1:izn/74bIBxVuHb+NdirkmWMH/yFNd8PBVXJgeH/ofGU=
 github.com/pions/dtls v1.2.2/go.mod h1:5o0cLHyBEl8CvuA88enDDM6aBwn4SNo8md2dPhcynfc=
+github.com/pions/ice v0.1.0 h1:V3RaaUV7QEFUrNk2tHzRqNo3ldCwwnghEpDIQ8+m/eA=
+github.com/pions/ice v0.1.0/go.mod h1:vGnMrwYxOMmfR093luGSh2yMm1DDpPqj8lGRNsmo9VQ=
 github.com/pions/logging v0.1.0 h1:vr+vInmjaRI06CqPWErEQpywxaqoIZcUjQ2eN68jRbk=
 github.com/pions/logging v0.1.0/go.mod h1:duuz9/Se8ujqvq7OPzbnPpRlha6A0fk1Ba2wrbn4zew=
 github.com/pions/qtls-vendor-extracted v0.0.0-20190210024908-018998217c65 h1:skcEQZ2eUdm1WKlYu7y1y0HBzOwa1pgSAwvhG6PrI2s=
diff --git a/icecandidate.go b/icecandidate.go
index d4e8a77b..d7ba06ed 100644
--- a/icecandidate.go
+++ b/icecandidate.go
@@ -5,8 +5,8 @@ import (
 	"fmt"
 	"net"
 
+	"github.com/pions/ice"
 	"github.com/pions/sdp/v2"
-	"github.com/pions/webrtc/internal/ice"
 )
 
 // ICECandidate represents a ice candidate
diff --git a/icegatherer.go b/icegatherer.go
index 092bed73..82eead33 100644
--- a/icegatherer.go
+++ b/icegatherer.go
@@ -6,7 +6,7 @@ import (
 	"errors"
 	"sync"
 
-	"github.com/pions/webrtc/internal/ice"
+	"github.com/pions/ice"
 )
 
 // The ICEGatherer gathers local host, server reflexive and relay
diff --git a/iceserver.go b/iceserver.go
index 4c33034d..e3f465a4 100644
--- a/iceserver.go
+++ b/iceserver.go
@@ -3,7 +3,7 @@
 package webrtc
 
 import (
-	"github.com/pions/webrtc/internal/ice"
+	"github.com/pions/ice"
 	"github.com/pions/webrtc/pkg/rtcerr"
 )
 
diff --git a/iceserver_js.go b/iceserver_js.go
index 31255095..984c01e7 100644
--- a/iceserver_js.go
+++ b/iceserver_js.go
@@ -5,7 +5,7 @@ package webrtc
 import (
 	"errors"
 
-	"github.com/pions/webrtc/internal/ice"
+	"github.com/pions/ice"
 )
 
 // ICEServer describes a single STUN and TURN server that can be used by
diff --git a/icetransport.go b/icetransport.go
index c4037aa9..4d7039c3 100644
--- a/icetransport.go
+++ b/icetransport.go
@@ -7,8 +7,8 @@ import (
 	"errors"
 	"sync"
 
+	"github.com/pions/ice"
 	"github.com/pions/logging"
-	"github.com/pions/webrtc/internal/ice"
 	"github.com/pions/webrtc/internal/mux"
 )
 
diff --git a/icetransportstate.go b/icetransportstate.go
index bd2d87c8..9faa95d4 100644
--- a/icetransportstate.go
+++ b/icetransportstate.go
@@ -1,6 +1,6 @@
 package webrtc
 
-import "github.com/pions/webrtc/internal/ice"
+import "github.com/pions/ice"
 
 // ICETransportState represents the current state of the ICE transport.
 type ICETransportState int
diff --git a/internal/ice/agent.go b/internal/ice/agent.go
deleted file mode 100644
index 0139b159..00000000
--- a/internal/ice/agent.go
+++ /dev/null
@@ -1,804 +0,0 @@
-// Package ice implements the Interactive Connectivity Establishment (ICE)
-// protocol defined in rfc5245.
-package ice
-
-import (
-	"fmt"
-	"math/rand"
-	"net"
-	"sort"
-	"sync"
-	"time"
-
-	"errors"
-
-	"github.com/pions/logging"
-	"github.com/pions/stun"
-	"github.com/pions/transport/packetio"
-	"github.com/pions/webrtc/internal/util"
-)
-
-const (
-	// taskLoopInterval is the interval at which the agent performs checks
-	taskLoopInterval = 2 * time.Second
-
-	// keepaliveInterval used to keep candidates alive
-	defaultKeepaliveInterval = 10 * time.Second
-
-	// defaultConnectionTimeout used to declare a connection dead
-	defaultConnectionTimeout = 30 * time.Second
-
-	// the number of bytes that can be buffered before we start to error
-	maxBufferSize = 1000 * 1000 // 1MB
-)
-
-// Agent represents the ICE agent
-type Agent struct {
-	onConnectionStateChangeHdlr       func(ConnectionState)
-	onSelectedCandidatePairChangeHdlr func(*Candidate, *Candidate)
-
-	// Used to block double Dial/Accept
-	opened bool
-
-	// State owned by the taskLoop
-	taskChan        chan task
-	onConnected     chan struct{}
-	onConnectedOnce sync.Once
-
-	connectivityTicker *time.Ticker
-	connectivityChan   <-chan time.Time
-
-	tieBreaker      uint64
-	connectionState ConnectionState
-	gatheringState  GatheringState
-
-	haveStarted   bool
-	isControlling bool
-
-	portmin uint16
-	portmax uint16
-
-	//How long should a pair stay quiet before we declare it dead?
-	//0 means never timeout
-	connectionTimeout time.Duration
-
-	//How often should we send keepalive packets?
-	//0 means never
-	keepaliveInterval time.Duration
-
-	localUfrag      string
-	localPwd        string
-	localCandidates map[NetworkType][]*Candidate
-
-	remoteUfrag      string
-	remotePwd        string
-	remoteCandidates map[NetworkType][]*Candidate
-
-	selectedPair *candidatePair
-	validPairs   []*candidatePair
-
-	buffer *packetio.Buffer
-
-	// State for closing
-	done chan struct{}
-	err  atomicError
-
-	log *logging.LeveledLogger
-}
-
-func (a *Agent) ok() error {
-	select {
-	case <-a.done:
-		return a.getErr()
-	default:
-	}
-	return nil
-}
-
-func (a *Agent) getErr() error {
-	err := a.err.Load()
-	if err != nil {
-		return err
-	}
-	return ErrClosed
-}
-
-// AgentConfig collects the arguments to ice.Agent construction into
-// a single structure, for future-proofness of the interface
-type AgentConfig struct {
-	Urls []*URL
-
-	// PortMin and PortMax are optional. Leave them 0 for the default UDP port allocation strategy.
-	PortMin uint16
-	PortMax uint16
-
-	// ConnectionTimeout defaults to 30 seconds when this property is nil.
-	// If the duration is 0, we will never timeout this connection.
-	ConnectionTimeout *time.Duration
-	// KeepaliveInterval determines how often should we send ICE
-	// keepalives (should be less then connectiontimeout above)
-	// when this is nil, it defaults to 10 seconds.
-	// A keepalive interval of 0 means we never send keepalive packets
-	KeepaliveInterval *time.Duration
-
-	// NetworkTypes is an optional configuration for disabling or enablding
-	// support for specific network types.
-	NetworkTypes []NetworkType
-}
-
-// NewAgent creates a new Agent
-func NewAgent(config *AgentConfig) (*Agent, error) {
-	if config.PortMax < config.PortMin {
-		return nil, ErrPort
-	}
-
-	a := &Agent{
-		tieBreaker:       rand.New(rand.NewSource(time.Now().UnixNano())).Uint64(),
-		gatheringState:   GatheringStateComplete, // TODO trickle-ice
-		connectionState:  ConnectionStateNew,
-		localCandidates:  make(map[NetworkType][]*Candidate),
-		remoteCandidates: make(map[NetworkType][]*Candidate),
-
-		localUfrag:  util.RandSeq(16),
-		localPwd:    util.RandSeq(32),
-		taskChan:    make(chan task),
-		onConnected: make(chan struct{}),
-		buffer:      packetio.NewBuffer(),
-		done:        make(chan struct{}),
-		portmin:     config.PortMin,
-		portmax:     config.PortMax,
-		log:         logging.NewScopedLogger("ice"),
-	}
-
-	// Make sure the buffer doesn't grow indefinitely.
-	// NOTE: We actually won't get anywhere close to this limit.
-	// SRTP will constantly read from the endpoint and drop packets if it's full.
-	a.buffer.SetLimitSize(maxBufferSize)
-
-	// connectionTimeout used to declare a connection dead
-	if config.ConnectionTimeout == nil {
-		a.connectionTimeout = defaultConnectionTimeout
-	} else {
-		a.connectionTimeout = *config.ConnectionTimeout
-	}
-
-	if config.KeepaliveInterval == nil {
-		a.keepaliveInterval = defaultKeepaliveInterval
-	} else {
-		a.keepaliveInterval = *config.KeepaliveInterval
-	}
-
-	// Initialize local candidates
-	a.gatherCandidatesLocal(config.NetworkTypes)
-	a.gatherCandidatesReflective(config.Urls, config.NetworkTypes)
-
-	go a.taskLoop()
-	return a, nil
-}
-
-// OnConnectionStateChange sets a handler that is fired when the connection state changes
-func (a *Agent) OnConnectionStateChange(f func(ConnectionState)) error {
-	return a.run(func(agent *Agent) {
-		agent.onConnectionStateChangeHdlr = f
-	})
-}
-
-// OnSelectedCandidatePairChange sets a handler that is fired when the final candidate
-// pair is selected
-func (a *Agent) OnSelectedCandidatePairChange(f func(*Candidate, *Candidate)) error {
-	return a.run(func(agent *Agent) {
-		agent.onSelectedCandidatePairChangeHdlr = f
-	})
-}
-
-func (a *Agent) onSelectedCandidatePairChange(p *candidatePair) {
-	if p != nil {
-		if a.onSelectedCandidatePairChangeHdlr != nil {
-			a.onSelectedCandidatePairChangeHdlr(p.local, p.remote)
-		}
-	}
-}
-
-func (a *Agent) listenUDP(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
-	if (laddr.Port != 0) || ((a.portmin == 0) && (a.portmax == 0)) {
-		return net.ListenUDP(network, laddr)
-	}
-	var i, j int
-	i = int(a.portmin)
-	if i == 0 {
-		i = 1
-	}
-	j = int(a.portmax)
-	if j == 0 {
-		j = 0xFFFF
-	}
-	for i <= j {
-		c, e := net.ListenUDP(network, &net.UDPAddr{IP: laddr.IP, Port: i})
-		if e == nil {
-			return c, e
-		}
-		i++
-	}
-	return nil, ErrPort
-}
-
-func (a *Agent) gatherCandidatesLocal(networkTypes []NetworkType) {
-	localIPs := localInterfaces(networkTypes)
-	for _, ip := range localIPs {
-		for _, network := range supportedNetworks {
-			conn, err := a.listenUDP(network, &net.UDPAddr{IP: ip, Port: 0})
-			if err != nil {
-				a.log.Warnf("could not listen %s %s\n", network, ip)
-				continue
-			}
-
-			port := conn.LocalAddr().(*net.UDPAddr).Port
-			c, err := NewCandidateHost(network, ip, port, ComponentRTP)
-			if err != nil {
-				a.log.Warnf("Failed to create host candidate: %s %s %d: %v\n", network, ip, port, err)
-				continue
-			}
-
-			networkType := c.NetworkType
-			set := a.localCandidates[networkType]
-			set = append(set, c)
-			a.localCandidates[networkType] = set
-
-			c.start(a, conn)
-		}
-	}
-}
-
-func (a *Agent) gatherCandidatesReflective(urls []*URL, networkTypes []NetworkType) {
-	for _, networkType := range networkTypes {
-		network := networkType.String()
-		for _, url := range urls {
-			switch url.Scheme {
-			case SchemeTypeSTUN:
-				laddr, xoraddr, err := allocateUDP(network, url)
-				if err != nil {
-					a.log.Warnf("could not allocate %s %s: %v\n", network, url, err)
-					continue
-				}
-				conn, err := net.ListenUDP(network, laddr)
-				if err != nil {
-					a.log.Warnf("could not listen %s %s: %v\n", network, laddr, err)
-				}
-
-				ip := xoraddr.IP
-				port := xoraddr.Port
-				relIP := laddr.IP.String()
-				relPort := laddr.Port
-				c, err := NewCandidateServerReflexive(network, ip, port, ComponentRTP, relIP, relPort)
-				if err != nil {
-					a.log.Warnf("Failed to create server reflexive candidate: %s %s %d: %v\n", network, ip, port, err)
-					continue
-				}
-
-				networkType := c.NetworkType
-				set := a.localCandidates[networkType]
-				set = append(set, c)
-				a.localCandidates[networkType] = set
-
-				c.start(a, conn)
-
-			default:
-				a.log.Warnf("scheme %s is not implemented\n", url.Scheme)
-				continue
-			}
-		}
-	}
-}
-
-func allocateUDP(network string, url *URL) (*net.UDPAddr, *stun.XorAddress, error) {
-	// TODO Do we want the timeout to be configurable?
-	client, err := stun.NewClient(network, fmt.Sprintf("%s:%d", url.Host, url.Port), time.Second*5)
-	if err != nil {
-		return nil, nil, util.FlattenErrs([]error{errors.New("failed to create STUN client"), err})
-	}
-	localAddr, ok := client.LocalAddr().(*net.UDPAddr)
-	if !ok {
-		return nil, nil, fmt.Errorf("failed to cast STUN client to UDPAddr")
-	}
-
-	resp, err := client.Request()
-	if err != nil {
-		return nil, nil, util.FlattenErrs([]error{errors.New("failed to make STUN request"), err})
-	}
-
-	if err = client.Close(); err != nil {
-		return nil, nil, util.FlattenErrs([]error{errors.New("failed to close STUN client"), err})
-	}
-
-	attr, ok := resp.GetOneAttribute(stun.AttrXORMappedAddress)
-	if !ok {
-		return nil, nil, fmt.Errorf("got response from STUN server that did not contain XORAddress")
-	}
-
-	var addr stun.XorAddress
-	if err = addr.Unpack(resp, attr); err != nil {
-		return nil, nil, util.FlattenErrs([]error{errors.New("failed to unpack STUN XorAddress response"), err})
-	}
-
-	return localAddr, &addr, nil
-}
-
-func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remotePwd string) error {
-	switch {
-	case a.haveStarted:
-		return fmt.Errorf("attempted to start agent twice")
-	case remoteUfrag == "":
-		return fmt.Errorf("remoteUfrag is empty")
-	case remotePwd == "":
-		return fmt.Errorf("remotePwd is empty")
-	}
-	a.log.Debugf("Started agent: isControlling? %t, remoteUfrag: %q, remotePwd: %q", isControlling, remoteUfrag, remotePwd)
-
-	return a.run(func(agent *Agent) {
-		agent.isControlling = isControlling
-		agent.remoteUfrag = remoteUfrag
-		agent.remotePwd = remotePwd
-
-		// TODO this should be dynamic, and grow when the connection is stable
-		t := time.NewTicker(taskLoopInterval)
-		agent.connectivityTicker = t
-		agent.connectivityChan = t.C
-
-		agent.updateConnectionState(ConnectionStateChecking)
-	})
-}
-
-func (a *Agent) pingCandidate(local, remote *Candidate) {
-	var msg *stun.Message
-	var err error
-
-	// The controlling agent MUST include the USE-CANDIDATE attribute in
-	// order to nominate a candidate pair (Section 8.1.1).  The controlled
-	// agent MUST NOT include the USE-CANDIDATE attribute in a Binding
-	// request.
-
-	if a.isControlling {
-		msg, err = stun.Build(stun.ClassRequest, stun.MethodBinding, stun.GenerateTransactionID(),
-			&stun.Username{Username: a.remoteUfrag + ":" + a.localUfrag},
-			&stun.UseCandidate{},
-			&stun.IceControlling{TieBreaker: a.tieBreaker},
-			&stun.Priority{Priority: uint32(local.Priority())},
-			&stun.MessageIntegrity{
-				Key: []byte(a.remotePwd),
-			},
-			&stun.Fingerprint{},
-		)
-	} else {
-		msg, err = stun.Build(stun.ClassRequest, stun.MethodBinding, stun.GenerateTransactionID(),
-			&stun.Username{Username: a.remoteUfrag + ":" + a.localUfrag},
-			&stun.IceControlled{TieBreaker: a.tieBreaker},
-			&stun.Priority{Priority: uint32(local.Priority())},
-			&stun.MessageIntegrity{
-				Key: []byte(a.remotePwd),
-			},
-			&stun.Fingerprint{},
-		)
-	}
-
-	if err != nil {
-		a.log.Debug(err.Error())
-		return
-	}
-
-	a.log.Tracef("ping STUN from %s to %s\n", local.String(), remote.String())
-	a.sendSTUN(msg, local, remote)
-}
-
-func (a *Agent) updateConnectionState(newState ConnectionState) {
-	if a.connectionState != newState {
-		a.log.Infof("Setting new connection state: %s", newState)
-		a.connectionState = newState
-		hdlr := a.onConnectionStateChangeHdlr
-		if hdlr != nil {
-			// Call handler async since we may be holding the agent lock
-			// and the handler may also require it
-			go hdlr(newState)
-		}
-	}
-}
-
-type candidatePairs []*candidatePair
-
-func (cp candidatePairs) Len() int      { return len(cp) }
-func (cp candidatePairs) Swap(i, j int) { cp[i], cp[j] = cp[j], cp[i] }
-
-type byPairPriority struct{ candidatePairs }
-
-// NB: Reverse sort so our candidates start at highest priority
-func (bp byPairPriority) Less(i, j int) bool {
-	return bp.candidatePairs[i].Priority() > bp.candidatePairs[j].Priority()
-}
-
-func (a *Agent) setValidPair(local, remote *Candidate, selected, controlling bool) {
-	// TODO: avoid duplicates
-	p := newCandidatePair(local, remote, controlling)
-	a.log.Tracef("Found valid candidate pair: %s (selected? %t)", p, selected)
-
-	if selected {
-		// Notify when the selected pair changes
-		if !a.selectedPair.Equal(p) {
-			a.onSelectedCandidatePairChange(p)
-		}
-		a.selectedPair = p
-		a.validPairs = nil
-		// TODO: only set state to connected on selecting final pair?
-		a.updateConnectionState(ConnectionStateConnected)
-	} else {
-		// keep track of pairs with succesfull bindings since any of them
-		// can be used for communication until the final pair is selected:
-		// https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-20#section-12
-		a.validPairs = append(a.validPairs, p)
-		// Sort the candidate pairs by priority of the remotes
-		sort.Sort(byPairPriority{a.validPairs})
-	}
-
-	// Signal connected
-	a.onConnectedOnce.Do(func() { close(a.onConnected) })
-}
-
-// A task is a
-type task func(*Agent)
-
-func (a *Agent) run(t task) error {
-	err := a.ok()
-	if err != nil {
-		return err
-	}
-
-	select {
-	case <-a.done:
-		return a.getErr()
-	case a.taskChan <- t:
-	}
-	return nil
-}
-
-func (a *Agent) taskLoop() {
-	for {
-		select {
-		case <-a.connectivityChan:
-			if a.validateSelectedPair() {
-				a.log.Trace("checking keepalive")
-				a.checkKeepalive()
-			} else {
-				a.log.Trace("pinging all candidates")
-				a.pingAllCandidates()
-			}
-
-		case t := <-a.taskChan:
-			// Run the task
-			t(a)
-
-		case <-a.done:
-			return
-		}
-	}
-}
-
-// validateSelectedPair checks if the selected pair is (still) valid
-// Note: the caller should hold the agent lock.
-func (a *Agent) validateSelectedPair() bool {
-	if a.selectedPair == nil {
-		// Not valid since not selected
-		return false
-	}
-
-	if (a.connectionTimeout != 0) &&
-		(time.Since(a.selectedPair.remote.LastReceived()) > a.connectionTimeout) {
-		a.selectedPair = nil
-		a.updateConnectionState(ConnectionStateDisconnected)
-		return false
-	}
-
-	return true
-}
-
-// checkKeepalive sends STUN Binding Indications to the selected pair
-// if no packet has been sent on that pair in the last keepaliveInterval
-// Note: the caller should hold the agent lock.
-func (a *Agent) checkKeepalive() {
-	if a.selectedPair == nil {
-		return
-	}
-
-	if (a.keepaliveInterval != 0) &&
-		(time.Since(a.selectedPair.local.LastSent()) > a.keepaliveInterval) {
-		a.keepaliveCandidate(a.selectedPair.local, a.selectedPair.remote)
-	}
-}
-
-// pingAllCandidates sends STUN Binding Requests to all candidates
-// Note: the caller should hold the agent lock.
-func (a *Agent) pingAllCandidates() {
-	for networkType, localCandidates := range a.localCandidates {
-		if remoteCandidates, ok := a.remoteCandidates[networkType]; ok {
-
-			for _, localCandidate := range localCandidates {
-				for _, remoteCandidate := range remoteCandidates {
-					a.pingCandidate(localCandidate, remoteCandidate)
-				}
-			}
-
-		}
-	}
-}
-
-// AddRemoteCandidate adds a new remote candidate
-func (a *Agent) AddRemoteCandidate(c *Candidate) error {
-	return a.run(func(agent *Agent) {
-		agent.addRemoteCandidate(c)
-	})
-}
-
-// addRemoteCandidate assumes you are holding the lock (must be execute using a.run)
-func (a *Agent) addRemoteCandidate(c *Candidate) {
-	networkType := c.NetworkType
-	set := a.remoteCandidates[networkType]
-
-	for _, candidate := range set {
-		if candidate.Equal(c) {
-			return
-		}
-	}
-
-	set = append(set, c)
-	a.remoteCandidates[networkType] = set
-}
-
-// GetLocalCandidates returns the local candidates
-func (a *Agent) GetLocalCandidates() ([]*Candidate, error) {
-	res := make(chan []*Candidate)
-
-	err := a.run(func(agent *Agent) {
-		var candidates []*Candidate
-		for _, set := range agent.localCandidates {
-			candidates = append(candidates, set...)
-		}
-		res <- candidates
-	})
-	if err != nil {
-		return nil, err
-	}
-
-	return <-res, nil
-}
-
-// GetLocalUserCredentials returns the local user credentials
-func (a *Agent) GetLocalUserCredentials() (frag string, pwd string) {
-	return a.localUfrag, a.localPwd
-}
-
-// Close cleans up the Agent
-func (a *Agent) Close() error {
-	done := make(chan struct{})
-	err := a.run(func(agent *Agent) {
-		defer func() {
-			close(done)
-		}()
-		agent.err.Store(ErrClosed)
-		close(agent.done)
-
-		// Cleanup all candidates
-		for net, cs := range agent.localCandidates {
-			for _, c := range cs {
-				err := c.close()
-				if err != nil {
-					a.log.Warnf("Failed to close candidate %s: %v", c, err)
-				}
-			}
-			delete(agent.localCandidates, net)
-		}
-		for net, cs := range agent.remoteCandidates {
-			for _, c := range cs {
-				err := c.close()
-				if err != nil {
-					a.log.Warnf("Failed to close candidate %s: %v", c, err)
-				}
-			}
-			delete(agent.remoteCandidates, net)
-		}
-
-		err := a.buffer.Close()
-		if err != nil {
-			a.log.Warnf("failed to close buffer: %v", err)
-		}
-	})
-	if err != nil {
-		return err
-	}
-
-	<-done
-
-	return nil
-}
-
-func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) *Candidate {
-	var ip net.IP
-	var port int
-
-	switch casted := addr.(type) {
-	case *net.UDPAddr:
-		ip = casted.IP
-		port = casted.Port
-	case *net.TCPAddr:
-		ip = casted.IP
-		port = casted.Port
-	default:
-		a.log.Warnf("unsupported address type %T", a)
-		return nil
-	}
-
-	set := a.remoteCandidates[networkType]
-	for _, c := range set {
-		base := c
-		if base.IP.Equal(ip) &&
-			base.Port == port {
-			return c
-		}
-	}
-	return nil
-}
-
-func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote *Candidate) {
-	base := remote
-	if out, err := stun.Build(stun.ClassSuccessResponse, stun.MethodBinding, m.TransactionID,
-		&stun.XorMappedAddress{
-			XorAddress: stun.XorAddress{
-				IP:   base.IP,
-				Port: base.Port,
-			},
-		},
-		&stun.MessageIntegrity{
-			Key: []byte(a.localPwd),
-		},
-		&stun.Fingerprint{},
-	); err != nil {
-		a.log.Warnf("Failed to handle inbound ICE from: %s to: %s error: %s", local, remote, err)
-	} else {
-		a.sendSTUN(out, local, remote)
-	}
-}
-
-func (a *Agent) handleInboundControlled(m *stun.Message, localCandidate, remoteCandidate *Candidate) {
-	if _, isControlled := m.GetOneAttribute(stun.AttrIceControlled); isControlled && !a.isControlling {
-		a.log.Debug("inbound isControlled && a.isControlling == false")
-		return
-	}
-
-	successResponse := m.Method == stun.MethodBinding && m.Class == stun.ClassSuccessResponse
-	_, usepair := m.GetOneAttribute(stun.AttrUseCandidate)
-	a.log.Tracef("got controlled message (success? %t, usepair? %t)", successResponse, usepair)
-	// Remember the working pair and select it when marked with usepair
-	a.setValidPair(localCandidate, remoteCandidate, usepair, false)
-
-	if !successResponse {
-		// Send success response
-		a.sendBindingSuccess(m, localCandidate, remoteCandidate)
-	}
-}
-
-func (a *Agent) handleInboundControlling(m *stun.Message, localCandidate, remoteCandidate *Candidate) {
-	if _, isControlling := m.GetOneAttribute(stun.AttrIceControlling); isControlling && a.isControlling {
-		a.log.Debug("inbound isControlling && a.isControlling == true")
-		return
-	} else if _, useCandidate := m.GetOneAttribute(stun.AttrUseCandidate); useCandidate && a.isControlling {
-		a.log.Debug("useCandidate && a.isControlling == true")
-		return
-	}
-	a.log.Tracef("got controlling message: %#v", m)
-
-	successResponse := m.Method == stun.MethodBinding && m.Class == stun.ClassSuccessResponse
-	// Remember the working pair and select it when receiving a success response
-	a.setValidPair(localCandidate, remoteCandidate, successResponse, true)
-
-	if !successResponse {
-		// Send success response
-		a.sendBindingSuccess(m, localCandidate, remoteCandidate)
-
-		// We received a ping from the controlled agent. We know the pair works so now we ping with use-candidate set:
-		a.pingCandidate(localCandidate, remoteCandidate)
-	}
-}
-
-// handleNewPeerReflexiveCandidate adds an unseen remote transport address
-// to the remote candidate list as a peer-reflexive candidate.
-func (a *Agent) handleNewPeerReflexiveCandidate(local *Candidate, remote net.Addr) error {
-	var ip net.IP
-	var port int
-
-	switch addr := remote.(type) {
-	case *net.UDPAddr:
-		ip = addr.IP
-		port = addr.Port
-	case *net.TCPAddr:
-		ip = addr.IP
-		port = addr.Port
-	default:
-		return fmt.Errorf("unsupported address type %T", addr)
-	}
-
-	pflxCandidate, err := NewCandidatePeerReflexive(
-		local.NetworkType.String(), // assume, same as that of local
-		ip,
-		port,
-		local.Component,
-		"", // unknown at this moment. TODO: need a review
-		0,  // unknown at this moment. TODO: need a review
-	)
-
-	if err != nil {
-		return util.FlattenErrs([]error{fmt.Errorf("failed to create peer-reflexive candidate: %v", remote), err})
-	}
-
-	// Add pflxCandidate to the remote candidate list
-	a.addRemoteCandidate(pflxCandidate)
-	return nil
-}
-
-// handleInbound processes STUN traffic from a remote candidate
-func (a *Agent) handleInbound(m *stun.Message, local *Candidate, remote net.Addr) {
-	a.log.Tracef("inbound STUN from %s to %s", remote.String(), local.String())
-	remoteCandidate := a.findRemoteCandidate(local.NetworkType, remote)
-	if remoteCandidate == nil {
-		a.log.Debugf("detected a new peer-reflexive candiate: %s ", remote)
-		err := a.handleNewPeerReflexiveCandidate(local, remote)
-		if err != nil {
-			// Log warning, then move on..
-			a.log.Warn(err.Error())
-		}
-		return
-	}
-
-	remoteCandidate.seen(false)
-
-	if m.Class == stun.ClassIndication {
-		return
-	}
-
-	if a.isControlling {
-		a.handleInboundControlling(m, local, remoteCandidate)
-	} else {
-		a.handleInboundControlled(m, local, remoteCandidate)
-	}
-}
-
-// noSTUNSeen processes non STUN traffic from a remote candidate
-func (a *Agent) noSTUNSeen(local *Candidate, remote net.Addr) {
-	remoteCandidate := a.findRemoteCandidate(local.NetworkType, remote)
-	if remoteCandidate != nil {
-		remoteCandidate.seen(false)
-	}
-}
-
-func (a *Agent) getBestPair() (*candidatePair, error) {
-	res := make(chan *candidatePair)
-
-	err := a.run(func(agent *Agent) {
-		if agent.selectedPair != nil {
-			res <- agent.selectedPair
-			return
-		}
-		for _, p := range agent.validPairs {
-			res <- p
-			return
-		}
-		res <- nil
-	})
-
-	if err != nil {
-		return nil, err
-	}
-
-	out := <-res
-
-	if out == nil {
-		return nil, errors.New("no Valid Candidate Pairs Available")
-	}
-
-	return out, nil
-}
diff --git a/internal/ice/candidate.go b/internal/ice/candidate.go
deleted file mode 100644
index 766ab69c..00000000
--- a/internal/ice/candidate.go
+++ /dev/null
@@ -1,272 +0,0 @@
-package ice
-
-import (
-	"fmt"
-	"net"
-	"sync"
-	"time"
-
-	"github.com/pions/stun"
-)
-
-const (
-	receiveMTU             = 8192
-	defaultLocalPreference = 65535
-
-	// ComponentRTP indicates that the candidate is used for RTP
-	ComponentRTP uint16 = 1
-	// ComponentRTCP indicates that the candidate is used for RTCP
-	ComponentRTCP
-)
-
-// Candidate represents an ICE candidate
-type Candidate struct {
-	NetworkType
-
-	Type            CandidateType
-	LocalPreference uint16
-	Component       uint16
-	IP              net.IP
-	Port            int
-	RelatedAddress  *CandidateRelatedAddress
-
-	lock         sync.RWMutex
-	lastSent     time.Time
-	lastReceived time.Time
-
-	agent    *Agent
-	conn     net.PacketConn
-	closeCh  chan struct{}
-	closedCh chan struct{}
-}
-
-// NewCandidateHost creates a new host candidate
-func NewCandidateHost(network string, ip net.IP, port int, component uint16) (*Candidate, error) {
-	networkType, err := determineNetworkType(network, ip)
-	if err != nil {
-		return nil, err
-	}
-
-	return &Candidate{
-		Type:            CandidateTypeHost,
-		NetworkType:     networkType,
-		IP:              ip,
-		Port:            port,
-		LocalPreference: defaultLocalPreference,
-		Component:       component,
-	}, nil
-}
-
-// NewCandidateServerReflexive creates a new server reflective candidate
-func NewCandidateServerReflexive(network string, ip net.IP, port int, component uint16, relAddr string, relPort int) (*Candidate, error) {
-	networkType, err := determineNetworkType(network, ip)
-	if err != nil {
-		return nil, err
-	}
-	return &Candidate{
-		Type:            CandidateTypeServerReflexive,
-		NetworkType:     networkType,
-		IP:              ip,
-		Port:            port,
-		LocalPreference: defaultLocalPreference,
-		Component:       component,
-		RelatedAddress: &CandidateRelatedAddress{
-			Address: relAddr,
-			Port:    relPort,
-		},
-	}, nil
-}
-
-// NewCandidatePeerReflexive creates a new peer reflective candidate
-func NewCandidatePeerReflexive(network string, ip net.IP, port int, component uint16, relAddr string, relPort int) (*Candidate, error) {
-	networkType, err := determineNetworkType(network, ip)
-	if err != nil {
-		return nil, err
-	}
-	return &Candidate{
-		Type:            CandidateTypePeerReflexive,
-		NetworkType:     networkType,
-		IP:              ip,
-		Port:            port,
-		LocalPreference: defaultLocalPreference,
-		Component:       component,
-		RelatedAddress: &CandidateRelatedAddress{
-			Address: relAddr,
-			Port:    relPort,
-		},
-	}, nil
-}
-
-// NewCandidateRelay creates a new relay candidate
-func NewCandidateRelay(network string, ip net.IP, port int, component uint16, relAddr string, relPort int) (*Candidate, error) {
-	networkType, err := determineNetworkType(network, ip)
-	if err != nil {
-		return nil, err
-	}
-	return &Candidate{
-		Type:            CandidateTypeRelay,
-		NetworkType:     networkType,
-		IP:              ip,
-		Port:            port,
-		LocalPreference: defaultLocalPreference,
-		Component:       component,
-		RelatedAddress: &CandidateRelatedAddress{
-			Address: relAddr,
-			Port:    relPort,
-		},
-	}, nil
-}
-
-// start runs the candidate using the provided connection
-func (c *Candidate) start(a *Agent, conn net.PacketConn) {
-	c.agent = a
-	c.conn = conn
-	c.closeCh = make(chan struct{})
-	c.closedCh = make(chan struct{})
-
-	go c.recvLoop()
-}
-
-func (c *Candidate) recvLoop() {
-	defer func() {
-		close(c.closedCh)
-	}()
-
-	log := c.agent.log
-	buffer := make([]byte, receiveMTU)
-	for {
-		n, srcAddr, err := c.conn.ReadFrom(buffer)
-		if err != nil {
-			return
-		}
-
-		if stun.IsSTUN(buffer[:n]) {
-			var m *stun.Message
-			m, err = stun.NewMessage(buffer[:n])
-			if err != nil {
-				log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err)
-				continue
-			}
-			err = c.agent.run(func(agent *Agent) {
-				agent.handleInbound(m, c, srcAddr)
-			})
-			if err != nil {
-				log.Warnf("Failed to handle message: %v", err)
-			}
-
-			continue
-		} else {
-			err = c.agent.run(func(agent *Agent) {
-				agent.noSTUNSeen(c, srcAddr)
-			})
-			if err != nil {
-				log.Warnf("Failed to handle message: %v", err)
-			}
-		}
-
-		// NOTE This will return packetio.ErrFull if the buffer ever manages to fill up.
-		_, err = c.agent.buffer.Write(buffer[:n])
-		if err != nil {
-			log.Warnf("failed to write packet")
-		}
-	}
-}
-
-// close stops the recvLoop
-func (c *Candidate) close() error {
-	c.lock.Lock()
-	defer c.lock.Unlock()
-	if c.conn != nil {
-		// Unblock recvLoop
-		close(c.closeCh)
-		// Close the conn
-		err := c.conn.Close()
-		if err != nil {
-			return err
-		}
-
-		// Wait until the recvLoop is closed
-		<-c.closedCh
-	}
-
-	return nil
-}
-
-func (c *Candidate) writeTo(raw []byte, dst *Candidate) (int, error) {
-	n, err := c.conn.WriteTo(raw, dst.addr())
-	if err != nil {
-		return n, fmt.Errorf("failed to send packet: %v", err)
-	}
-	c.seen(true)
-	return n, nil
-}
-
-// Priority computes the priority for this ICE Candidate
-func (c *Candidate) Priority() uint16 {
-	// The local preference MUST be an integer from 0 (lowest preference) to
-	// 65535 (highest preference) inclusive.  When there is only a single IP
-	// address, this value SHOULD be set to 65535.  If there are multiple
-	// candidates for a particular component for a particular data stream
-	// that have the same type, the local preference MUST be unique for each
-	// one.
-	return (2^24)*c.Type.Preference() +
-		(2^8)*c.LocalPreference +
-		(2^0)*(256-c.Component)
-}
-
-// Equal is used to compare two CandidateBases
-func (c *Candidate) Equal(other *Candidate) bool {
-	return c.NetworkType == other.NetworkType &&
-		c.Type == other.Type &&
-		c.IP.Equal(other.IP) &&
-		c.Port == other.Port &&
-		c.RelatedAddress.Equal(other.RelatedAddress)
-}
-
-// String makes the CandidateHost printable
-func (c *Candidate) String() string {
-	return fmt.Sprintf("%s %s:%d%s", c.Type, c.IP, c.Port, c.RelatedAddress)
-}
-
-// LastReceived returns a time.Time indicating the last time
-// this candidate was received
-func (c *Candidate) LastReceived() time.Time {
-	c.lock.RLock()
-	defer c.lock.RUnlock()
-	return c.lastReceived
-}
-
-func (c *Candidate) setLastReceived(t time.Time) {
-	c.lock.Lock()
-	defer c.lock.Unlock()
-	c.lastReceived = t
-}
-
-// LastSent returns a time.Time indicating the last time
-// this candidate was sent
-func (c *Candidate) LastSent() time.Time {
-	c.lock.RLock()
-	defer c.lock.RUnlock()
-	return c.lastSent
-}
-
-func (c *Candidate) setLastSent(t time.Time) {
-	c.lock.Lock()
-	defer c.lock.Unlock()
-	c.lastSent = t
-}
-
-func (c *Candidate) seen(outbound bool) {
-	if outbound {
-		c.setLastSent(time.Now())
-	} else {
-		c.setLastReceived(time.Now())
-	}
-}
-
-func (c *Candidate) addr() net.Addr {
-	return &net.UDPAddr{
-		IP:   c.IP,
-		Port: c.Port,
-	}
-}
diff --git a/internal/ice/candidatepair.go b/internal/ice/candidatepair.go
deleted file mode 100644
index 3eba1d89..00000000
--- a/internal/ice/candidatepair.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package ice
-
-import (
-	"fmt"
-
-	"github.com/pions/stun"
-)
-
-func newCandidatePair(local, remote *Candidate, controlling bool) *candidatePair {
-	return &candidatePair{
-		iceRoleControlling: controlling,
-		remote:             remote,
-		local:              local,
-	}
-}
-
-// candidatePair represents a combination of a local and remote candidate
-type candidatePair struct {
-	iceRoleControlling bool
-	remote             *Candidate
-	local              *Candidate
-}
-
-func (p *candidatePair) String() string {
-	return fmt.Sprintf("prio %d (local, prio %d) %s <-> %s (remote, prio %d)",
-		p.Priority(), p.local.Priority(), p.local, p.remote, p.remote.Priority())
-}
-
-func (p *candidatePair) Equal(other *candidatePair) bool {
-	if p == nil && other == nil {
-		return true
-	}
-	if p == nil || other == nil {
-		return false
-	}
-	return p.local.Equal(other.local) && p.remote.Equal(other.remote)
-}
-
-// RFC 5245 - 5.7.2.  Computing Pair Priority and Ordering Pairs
-// Let G be the priority for the candidate provided by the controlling
-// agent.  Let D be the priority for the candidate provided by the
-// controlled agent.
-// pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
-func (p *candidatePair) Priority() uint32 {
-	var g uint32
-	var d uint32
-	if p.iceRoleControlling {
-		g = uint32(p.local.Priority())
-		d = uint32(p.remote.Priority())
-	} else {
-		g = uint32(p.remote.Priority())
-		d = uint32(p.local.Priority())
-	}
-
-	// Just implement these here rather
-	// than fooling around with the math package
-	min := func(x, y uint32) uint32 {
-		if x < y {
-			return x
-		}
-		return y
-	}
-	max := func(x, y uint32) uint32 {
-		if x > y {
-			return x
-		}
-		return y
-	}
-	cmp := func(x, y uint32) uint32 {
-		if x > y {
-			return 1
-		}
-		return 0
-	}
-
-	return (2^32)*min(g, d) + 2*max(g, d) + cmp(g, d)
-}
-
-func (p *candidatePair) Write(b []byte) (int, error) {
-	return p.local.writeTo(b, p.remote)
-}
-
-// keepaliveCandidate sends a STUN Binding Indication to the remote candidate
-func (a *Agent) keepaliveCandidate(local, remote *Candidate) {
-	msg, err := stun.Build(stun.ClassIndication, stun.MethodBinding, stun.GenerateTransactionID(),
-		&stun.Username{Username: a.remoteUfrag + ":" + a.localUfrag},
-		&stun.MessageIntegrity{
-			Key: []byte(a.remotePwd),
-		},
-		&stun.Fingerprint{},
-	)
-
-	if err != nil {
-		a.log.Warn(err.Error())
-		return
-	}
-
-	a.sendSTUN(msg, local, remote)
-}
-
-func (a *Agent) sendSTUN(msg *stun.Message, local, remote *Candidate) {
-	_, err := local.writeTo(msg.Pack(), remote)
-	if err != nil {
-		a.log.Tracef("failed to send STUN message: %s", err)
-	}
-}
diff --git a/internal/ice/candidaterelatedaddress.go b/internal/ice/candidaterelatedaddress.go
deleted file mode 100644
index 18cf3183..00000000
--- a/internal/ice/candidaterelatedaddress.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package ice
-
-import "fmt"
-
-// CandidateRelatedAddress convey transport addresses related to the
-// candidate, useful for diagnostics and other purposes.
-type CandidateRelatedAddress struct {
-	Address string
-	Port    int
-}
-
-// String makes CandidateRelatedAddress printable
-func (c *CandidateRelatedAddress) String() string {
-	if c == nil {
-		return ""
-	}
-
-	return fmt.Sprintf(" related %s:%d", c.Address, c.Port)
-}
-
-// Equal allows comparing two CandidateRelatedAddresses.
-// The CandidateRelatedAddress are allowed to be nil.
-func (c *CandidateRelatedAddress) Equal(other *CandidateRelatedAddress) bool {
-	if c == nil && other == nil {
-		return true
-	}
-	return c != nil && other != nil &&
-		c.Address == other.Address &&
-		c.Port == other.Port
-}
diff --git a/internal/ice/candidatetype.go b/internal/ice/candidatetype.go
deleted file mode 100644
index dd58d896..00000000
--- a/internal/ice/candidatetype.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package ice
-
-// CandidateType represents the type of candidate
-type CandidateType byte
-
-// CandidateType enum
-const (
-	CandidateTypeHost CandidateType = iota + 1
-	CandidateTypeServerReflexive
-	CandidateTypePeerReflexive
-	CandidateTypeRelay
-)
-
-// String makes CandidateType printable
-func (c CandidateType) String() string {
-	switch c {
-	case CandidateTypeHost:
-		return "host"
-	case CandidateTypeServerReflexive:
-		return "srflx"
-	case CandidateTypePeerReflexive:
-		return "prflx"
-	case CandidateTypeRelay:
-		return "relay"
-	}
-	return "Unknown candidate type"
-}
-
-// Preference returns the preference weight of a CandidateType
-//
-// 4.1.2.2.  Guidelines for Choosing Type and Local Preferences
-// The RECOMMENDED values are 126 for host candidates, 100
-// for server reflexive candidates, 110 for peer reflexive candidates,
-// and 0 for relayed candidates.
-func (c CandidateType) Preference() uint16 {
-	switch c {
-	case CandidateTypeHost:
-		return 126
-	case CandidateTypePeerReflexive:
-		return 110
-	case CandidateTypeServerReflexive:
-		return 100
-	}
-	return 0
-}
diff --git a/internal/ice/errors.go b/internal/ice/errors.go
deleted file mode 100644
index 57446d43..00000000
--- a/internal/ice/errors.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package ice
-
-import "errors"
-
-var (
-	// ErrUnknownType indicates an error with Unknown info.
-	ErrUnknownType = errors.New("Unknown")
-
-	// ErrSchemeType indicates the scheme type could not be parsed.
-	ErrSchemeType = errors.New("unknown scheme type")
-
-	// ErrSTUNQuery indicates query arguments are provided in a STUN URL.
-	ErrSTUNQuery = errors.New("queries not supported in stun address")
-
-	// ErrInvalidQuery indicates an malformed query is provided.
-	ErrInvalidQuery = errors.New("invalid query")
-
-	// ErrHost indicates malformed hostname is provided.
-	ErrHost = errors.New("invalid hostname")
-
-	// ErrPort indicates malformed port is provided.
-	ErrPort = errors.New("invalid port")
-
-	// ErrProtoType indicates an unsupported transport type was provided.
-	ErrProtoType = errors.New("invalid transport protocol type")
-
-	// ErrClosed indicates the agent is closed
-	ErrClosed = errors.New("the agent is closed")
-)
diff --git a/internal/ice/ice.go b/internal/ice/ice.go
deleted file mode 100644
index d7094f6d..00000000
--- a/internal/ice/ice.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package ice
-
-// ConnectionState is an enum showing the state of a ICE Connection
-type ConnectionState int
-
-// List of supported States
-const (
-	// ConnectionStateNew ICE agent is gathering addresses
-	ConnectionStateNew = iota + 1
-
-	// ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match
-	ConnectionStateChecking
-
-	// ConnectionStateConnected ICE agent has a pairing, but is still checking other pairs
-	ConnectionStateConnected
-
-	// ConnectionStateCompleted ICE agent has finished
-	ConnectionStateCompleted
-
-	// ConnectionStateFailed ICE agent never could successfully connect
-	ConnectionStateFailed
-
-	// ConnectionStateDisconnected ICE agent connected successfully, but has entered a failed state
-	ConnectionStateDisconnected
-
-	// ConnectionStateClosed ICE agent has finished and is no longer handling requests
-	ConnectionStateClosed
-)
-
-func (c ConnectionState) String() string {
-	switch c {
-	case ConnectionStateNew:
-		return "New"
-	case ConnectionStateChecking:
-		return "Checking"
-	case ConnectionStateConnected:
-		return "Connected"
-	case ConnectionStateCompleted:
-		return "Completed"
-	case ConnectionStateFailed:
-		return "Failed"
-	case ConnectionStateDisconnected:
-		return "Disconnected"
-	case ConnectionStateClosed:
-		return "Closed"
-	default:
-		return "Invalid"
-	}
-}
-
-// GatheringState describes the state of the candidate gathering process
-type GatheringState int
-
-const (
-	// GatheringStateNew indicates candidate gatering is not yet started
-	GatheringStateNew GatheringState = iota + 1
-
-	// GatheringStateGathering indicates candidate gatering is ongoing
-	GatheringStateGathering
-
-	// GatheringStateComplete indicates candidate gatering has been completed
-	GatheringStateComplete
-)
-
-func (t GatheringState) String() string {
-	switch t {
-	case GatheringStateNew:
-		return "new"
-	case GatheringStateGathering:
-		return "gathering"
-	case GatheringStateComplete:
-		return "complete"
-	default:
-		return ErrUnknownType.Error()
-	}
-}
diff --git a/internal/ice/networktype.go b/internal/ice/networktype.go
deleted file mode 100644
index 48f5cf6b..00000000
--- a/internal/ice/networktype.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package ice
-
-import (
-	"fmt"
-	"net"
-	"strings"
-)
-
-const (
-	udp = "udp"
-	tcp = "tcp"
-)
-
-var supportedNetworks = []string{
-	udp,
-	// tcp, // Not supported yet
-}
-
-var supportedNetworkTypes = []NetworkType{
-	NetworkTypeUDP4,
-	NetworkTypeUDP6,
-	// NetworkTypeTCP4, // Not supported yet
-	// NetworkTypeTCP6, // Not supported yet
-}
-
-// NetworkType represents the type of network
-type NetworkType int
-
-const (
-	// NetworkTypeUDP4 indicates UDP over IPv4.
-	NetworkTypeUDP4 NetworkType = iota + 1
-
-	// NetworkTypeUDP6 indicates UDP over IPv6.
-	NetworkTypeUDP6
-
-	// NetworkTypeTCP4 indicates TCP over IPv4.
-	NetworkTypeTCP4
-
-	// NetworkTypeTCP6 indicates TCP over IPv6.
-	NetworkTypeTCP6
-)
-
-func (t NetworkType) String() string {
-	switch t {
-	case NetworkTypeUDP4:
-		return "udp4"
-	case NetworkTypeUDP6:
-		return "udp6"
-	case NetworkTypeTCP4:
-		return "tcp4"
-	case NetworkTypeTCP6:
-		return "tcp6"
-	default:
-		return ErrUnknownType.Error()
-	}
-}
-
-// NetworkShort returns the short network description
-func (t NetworkType) NetworkShort() string {
-	switch t {
-	case NetworkTypeUDP4, NetworkTypeUDP6:
-		return udp
-	case NetworkTypeTCP4, NetworkTypeTCP6:
-		return tcp
-	default:
-		return ErrUnknownType.Error()
-	}
-}
-
-// IsReliable returns true if the network is reliable
-func (t NetworkType) IsReliable() bool {
-	switch t {
-	case NetworkTypeUDP4, NetworkTypeUDP6:
-		return false
-	case NetworkTypeTCP4, NetworkTypeTCP6:
-		return true
-	}
-	return false
-}
-
-// IsIPv4 returns whether the network type is IPv4 or not.
-func (t NetworkType) IsIPv4() bool {
-	switch t {
-	case NetworkTypeUDP4, NetworkTypeTCP4:
-		return true
-	case NetworkTypeUDP6, NetworkTypeTCP6:
-		return false
-	}
-	return false
-}
-
-// IsIPv6 returns whether the network type is IPv6 or not.
-func (t NetworkType) IsIPv6() bool {
-	switch t {
-	case NetworkTypeUDP4, NetworkTypeTCP4:
-		return false
-	case NetworkTypeUDP6, NetworkTypeTCP6:
-		return true
-	}
-	return false
-}
-
-// determineNetworkType determines the type of network based on
-// the short network string and an IP address.
-func determineNetworkType(network string, ip net.IP) (NetworkType, error) {
-	ipv4 := ip.To4() != nil
-
-	switch {
-	case strings.HasPrefix(strings.ToLower(network), udp):
-		if ipv4 {
-			return NetworkTypeUDP4, nil
-		}
-		return NetworkTypeUDP6, nil
-
-	case strings.HasPrefix(strings.ToLower(network), tcp):
-		if ipv4 {
-			return NetworkTypeTCP4, nil
-		}
-		return NetworkTypeTCP6, nil
-
-	}
-
-	return NetworkType(0), fmt.Errorf("unable to determine networkType from %s %s", network, ip)
-}
diff --git a/internal/ice/transport.go b/internal/ice/transport.go
deleted file mode 100644
index a0c529a4..00000000
--- a/internal/ice/transport.go
+++ /dev/null
@@ -1,117 +0,0 @@
-package ice
-
-import (
-	"context"
-	"errors"
-	"net"
-	"time"
-
-	"github.com/pions/stun"
-)
-
-// Dial connects to the remote agent, acting as the controlling ice agent.
-// Dial blocks until at least one ice candidate pair has successfully connected.
-func (a *Agent) Dial(ctx context.Context, remoteUfrag, remotePwd string) (*Conn, error) {
-	return a.connect(ctx, true, remoteUfrag, remotePwd)
-}
-
-// Accept connects to the remote agent, acting as the controlled ice agent.
-// Accept blocks until at least one ice candidate pair has successfully connected.
-func (a *Agent) Accept(ctx context.Context, remoteUfrag, remotePwd string) (*Conn, error) {
-	return a.connect(ctx, false, remoteUfrag, remotePwd)
-}
-
-// Conn represents the ICE connection.
-// At the moment the lifetime of the Conn is equal to the Agent.
-type Conn struct {
-	agent *Agent
-}
-
-func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, remotePwd string) (*Conn, error) {
-	err := a.ok()
-	if err != nil {
-		return nil, err
-	}
-	if a.opened {
-		return nil, errors.New("a connection is already opened")
-	}
-	err = a.startConnectivityChecks(isControlling, remoteUfrag, remotePwd)
-	if err != nil {
-		return nil, err
-	}
-
-	// block until pair selected
-	select {
-	case <-ctx.Done():
-		// TODO: Stop connectivity checks?
-		return nil, errors.New("connecting canceled by caller")
-	case <-a.onConnected:
-	}
-
-	return &Conn{
-		agent: a,
-	}, nil
-
-}
-
-// Read implements the Conn Read method.
-func (c *Conn) Read(p []byte) (int, error) {
-	err := c.agent.ok()
-	if err != nil {
-		return 0, err
-	}
-
-	return c.agent.buffer.Read(p)
-}
-
-// Write implements the Conn Write method.
-func (c *Conn) Write(p []byte) (int, error) {
-	err := c.agent.ok()
-	if err != nil {
-		return 0, err
-	}
-
-	if stun.IsSTUN(p) {
-		return 0, errors.New("the ICE conn can't write STUN messages")
-	}
-
-	pair, err := c.agent.getBestPair()
-	if err != nil {
-		return 0, err
-	}
-
-	return pair.Write(p)
-}
-
-// Close implements the Conn Close method. It is used to close
-// the connection. Any calls to Read and Write will be unblocked and return an error.
-func (c *Conn) Close() error {
-	return c.agent.Close()
-}
-
-// TODO: Maybe just switch to using io.ReadWriteCloser?
-
-// LocalAddr is a stub
-func (c *Conn) LocalAddr() net.Addr {
-	return nil
-}
-
-// RemoteAddr is a stub
-func (c *Conn) RemoteAddr() net.Addr {
-	return nil
-}
-
-// SetDeadline is a stub
-func (c *Conn) SetDeadline(t time.Time) error {
-	return nil
-}
-
-// SetReadDeadline is a stub
-func (c *Conn) SetReadDeadline(t time.Time) error {
-	return nil
-}
-
-// SetWriteDeadline is a stub
-func (c *Conn) SetWriteDeadline(t time.Time) error {
-	return nil
-}
diff --git a/internal/ice/url.go b/internal/ice/url.go
deleted file mode 100644
index 6f0196ed..00000000
--- a/internal/ice/url.go
+++ /dev/null
@@ -1,227 +0,0 @@
-package ice
-
-import (
-	"net"
-	"net/url"
-	"strconv"
-
-	"github.com/pions/webrtc/pkg/rtcerr"
-)
-
-// TODO: Migrate address parsing to STUN/TURN
-
-// SchemeType indicates the type of server used in the ice.URL structure.
-type SchemeType int
-
-// Unknown defines default public constant to use for "enum" like struct
-// comparisons when no value was defined.
-const Unknown = iota
-
-const (
-	// SchemeTypeSTUN indicates the URL represents a STUN server.
-	SchemeTypeSTUN SchemeType = iota + 1
-
-	// SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server.
-	SchemeTypeSTUNS
-
-	// SchemeTypeTURN indicates the URL represents a TURN server.
-	SchemeTypeTURN
-
-	// SchemeTypeTURNS indicates the URL represents a TURNS (secure) server.
-	SchemeTypeTURNS
-)
-
-// NewSchemeType defines a procedure for creating a new SchemeType from a raw
-// string naming the scheme type.
-func NewSchemeType(raw string) SchemeType {
-	switch raw {
-	case "stun":
-		return SchemeTypeSTUN
-	case "stuns":
-		return SchemeTypeSTUNS
-	case "turn":
-		return SchemeTypeTURN
-	case "turns":
-		return SchemeTypeTURNS
-	default:
-		return SchemeType(Unknown)
-	}
-}
-
-func (t SchemeType) String() string {
-	switch t {
-	case SchemeTypeSTUN:
-		return "stun"
-	case SchemeTypeSTUNS:
-		return "stuns"
-	case SchemeTypeTURN:
-		return "turn"
-	case SchemeTypeTURNS:
-		return "turns"
-	default:
-		return ErrUnknownType.Error()
-	}
-}
-
-// ProtoType indicates the transport protocol type that is used in the ice.URL
-// structure.
-type ProtoType int
-
-const (
-	// ProtoTypeUDP indicates the URL uses a UDP transport.
-	ProtoTypeUDP ProtoType = iota + 1
-
-	// ProtoTypeTCP indicates the URL uses a TCP transport.
-	ProtoTypeTCP
-)
-
-// NewProtoType defines a procedure for creating a new ProtoType from a raw
-// string naming the transport protocol type.
-func NewProtoType(raw string) ProtoType {
-	switch raw {
-	case "udp":
-		return ProtoTypeUDP
-	case "tcp":
-		return ProtoTypeTCP
-	default:
-		return ProtoType(Unknown)
-	}
-}
-
-func (t ProtoType) String() string {
-	switch t {
-	case ProtoTypeUDP:
-		return "udp"
-	case ProtoTypeTCP:
-		return "tcp"
-	default:
-		return ErrUnknownType.Error()
-	}
-}
-
-// URL represents a STUN (rfc7064) or TURN (rfc7065) URL
-type URL struct {
-	Scheme SchemeType
-	Host   string
-	Port   int
-	Proto  ProtoType
-}
-
-// ParseURL parses a STUN or TURN urls following the ABNF syntax described in
-// https://tools.ietf.org/html/rfc7064 and https://tools.ietf.org/html/rfc7065
-// respectively.
-func ParseURL(raw string) (*URL, error) {
-	rawParts, err := url.Parse(raw)
-	if err != nil {
-		return nil, &rtcerr.UnknownError{Err: err}
-	}
-
-	var u URL
-	u.Scheme = NewSchemeType(rawParts.Scheme)
-	if u.Scheme == SchemeType(Unknown) {
-		return nil, &rtcerr.SyntaxError{Err: ErrSchemeType}
-	}
-
-	var rawPort string
-	if u.Host, rawPort, err = net.SplitHostPort(rawParts.Opaque); err != nil {
-		if e, ok := err.(*net.AddrError); ok {
-			if e.Err == "missing port in address" {
-				nextRawURL := u.Scheme.String() + ":" + rawParts.Opaque
-				switch {
-				case u.Scheme == SchemeTypeSTUN || u.Scheme == SchemeTypeTURN:
-					nextRawURL += ":3478"
-					if rawParts.RawQuery != "" {
-						nextRawURL += "?" + rawParts.RawQuery
-					}
-					return ParseURL(nextRawURL)
-				case u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS:
-					nextRawURL += ":5349"
-					if rawParts.RawQuery != "" {
-						nextRawURL += "?" + rawParts.RawQuery
-					}
-					return ParseURL(nextRawURL)
-				}
-			}
-		}
-		return nil, &rtcerr.UnknownError{Err: err}
-	}
-
-	if u.Host == "" {
-		return nil, &rtcerr.SyntaxError{Err: ErrHost}
-	}
-
-	if u.Port, err = strconv.Atoi(rawPort); err != nil {
-		return nil, &rtcerr.SyntaxError{Err: ErrPort}
-	}
-
-	switch {
-	case u.Scheme == SchemeTypeSTUN:
-		qArgs, err := url.ParseQuery(rawParts.RawQuery)
-		if err != nil || (err == nil && len(qArgs) > 0) {
-			return nil, &rtcerr.SyntaxError{Err: ErrSTUNQuery}
-		}
-		u.Proto = ProtoTypeUDP
-	case u.Scheme == SchemeTypeSTUNS:
-		qArgs, err := url.ParseQuery(rawParts.RawQuery)
-		if err != nil || (err == nil && len(qArgs) > 0) {
-			return nil, &rtcerr.SyntaxError{Err: ErrSTUNQuery}
-		}
-		u.Proto = ProtoTypeTCP
-	case u.Scheme == SchemeTypeTURN:
-		proto, err := parseProto(rawParts.RawQuery)
-		if err != nil {
-			return nil, err
-		}
-
-		u.Proto = proto
-		if u.Proto == ProtoType(Unknown) {
-			u.Proto = ProtoTypeUDP
-		}
-	case u.Scheme == SchemeTypeTURNS:
-		proto, err := parseProto(rawParts.RawQuery)
-		if err != nil {
-			return nil, err
-		}
-
-		u.Proto = proto
-		if u.Proto == ProtoType(Unknown) {
-			u.Proto = ProtoTypeTCP
-		}
-	}
-
-	return &u, nil
-}
-
-func parseProto(raw string) (ProtoType, error) {
-	qArgs, err := url.ParseQuery(raw)
-	if err != nil || len(qArgs) > 1 {
-		return ProtoType(Unknown), &rtcerr.SyntaxError{Err: ErrInvalidQuery}
-	}
-
-	var proto ProtoType
-	if rawProto := qArgs.Get("transport"); rawProto != "" {
-		if proto = NewProtoType(rawProto); proto == ProtoType(0) {
-			return ProtoType(Unknown), &rtcerr.NotSupportedError{Err: ErrProtoType}
-		}
-		return proto, nil
-	}
-
-	if len(qArgs) > 0 {
-		return ProtoType(Unknown), &rtcerr.SyntaxError{Err: ErrInvalidQuery}
-	}
-
-	return proto, nil
-}
-
-func (u URL) String() string {
-	rawURL := u.Scheme.String() + ":" + net.JoinHostPort(u.Host, strconv.Itoa(u.Port))
-	if u.Scheme == SchemeTypeTURN || u.Scheme == SchemeTypeTURNS {
-		rawURL += "?transport=" + u.Proto.String()
-	}
-	return rawURL
-}
-
-// IsSecure returns whether the this URL's scheme describes secure scheme or not.
-func (u URL) IsSecure() bool {
-	return u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS
-}
diff --git a/internal/ice/util.go b/internal/ice/util.go
deleted file mode 100644
index 9de782a3..00000000
--- a/internal/ice/util.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package ice
-
-import (
-	"net"
-	"sync/atomic"
-)
-
-func localInterfaces(networkTypes []NetworkType) (ips []net.IP) {
-	ifaces, err := net.Interfaces()
-	if err != nil {
-		return ips
-	}
-
-	var IPv4Requested, IPv6Requested bool
-	for _, typ := range networkTypes {
-		if typ.IsIPv4() {
-			IPv4Requested = true
-		}
-
-		if typ.IsIPv6() {
-			IPv6Requested = true
-		}
-	}
-
-	for _, iface := range ifaces {
-		if iface.Flags&net.FlagUp == 0 {
-			continue // interface down
-		}
-		if iface.Flags&net.FlagLoopback != 0 {
-			continue // loopback interface
-		}
-
-		addrs, err := iface.Addrs()
-		if err != nil {
-			return ips
-		}
-
-		for _, addr := range addrs {
-			var ip net.IP
-			switch addr := addr.(type) {
-			case *net.IPNet:
-				ip = addr.IP
-			case *net.IPAddr:
-				ip = addr.IP
-
-			}
-			if ip == nil || ip.IsLoopback() {
-				continue
-			}
-
-			if ipv4 := ip.To4(); ipv4 == nil {
-				if !IPv6Requested {
-					continue
-				} else if !isSupportedIPv6(ip) {
-					continue
-				}
-			} else if !IPv4Requested {
-				continue
-			}
-
-			ips = append(ips, ip)
-		}
-	}
-	return ips
-}
-
-type atomicError struct{ v atomic.Value }
-
-func (a *atomicError) Store(err error) {
-	a.v.Store(struct{ error }{err})
-}
-func (a *atomicError) Load() error {
-	err, _ := a.v.Load().(struct{ error })
-	return err.error
-}
-
-// The conditions of invalidation written below are defined in
-// https://tools.ietf.org/html/rfc8445#section-5.1.1.1
-func isSupportedIPv6(ip net.IP) bool {
-	if len(ip) != net.IPv6len ||
-		!isZeros(ip[0:12]) || // !(IPv4-compatible IPv6)
-		ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 || // !(IPv6 site-local unicast)
-		ip.IsLinkLocalUnicast() ||
-		ip.IsLinkLocalMulticast() {
-		return false
-	}
-	return true
-}
-
-func isZeros(ip net.IP) bool {
-	for i := 0; i < len(ip); i++ {
-		if ip[i] != 0 {
-			return false
-		}
-	}
-	return true
-}
diff --git a/peerconnection.go b/peerconnection.go
index 81897acd..fe29d836 100644
--- a/peerconnection.go
+++ b/peerconnection.go
@@ -14,10 +14,10 @@ import (
 	"sync"
 	"time"
 
+	"github.com/pions/ice"
 	"github.com/pions/logging"
 	"github.com/pions/rtcp"
 	"github.com/pions/sdp/v2"
-	"github.com/pions/webrtc/internal/ice"
 	"github.com/pions/webrtc/internal/util"
 	"github.com/pions/webrtc/pkg/rtcerr"
 )
diff --git a/settingengine.go b/settingengine.go
index 7afd0506..6abef2ff 100644
--- a/settingengine.go
+++ b/settingengine.go
@@ -5,7 +5,7 @@ package webrtc
 import (
 	"time"
 
-	"github.com/pions/webrtc/internal/ice"
+	"github.com/pions/ice"
 )
 
 // SettingEngine allows influencing behavior in ways that are not


Test patch:
diff --git a/icecandidate_test.go b/icecandidate_test.go
index 536a11a4..14053725 100644
--- a/icecandidate_test.go
+++ b/icecandidate_test.go
@@ -4,8 +4,8 @@ import (
 	"net"
 	"testing"
 
+	"github.com/pions/ice"
 	"github.com/pions/sdp/v2"
-	"github.com/pions/webrtc/internal/ice"
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/iceserver_test.go b/iceserver_test.go
index 8aeb7b4e..b49cba7f 100644
--- a/iceserver_test.go
+++ b/iceserver_test.go
@@ -5,7 +5,7 @@ package webrtc
 import (
 	"testing"
 
-	"github.com/pions/webrtc/internal/ice"
+	"github.com/pions/ice"
 	"github.com/pions/webrtc/pkg/rtcerr"
 	"github.com/stretchr/testify/assert"
 )
diff --git a/icetransportstate_test.go b/icetransportstate_test.go
index cb2c62bf..e4465595 100644
--- a/icetransportstate_test.go
+++ b/icetransportstate_test.go
@@ -3,7 +3,7 @@ package webrtc
 import (
 	"testing"
 
-	"github.com/pions/webrtc/internal/ice"
+	"github.com/pions/ice"
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/internal/ice/agent_test.go b/internal/ice/agent_test.go
deleted file mode 100644
index 0ec47188..00000000
--- a/internal/ice/agent_test.go
+++ /dev/null
@@ -1,314 +0,0 @@
-package ice
-
-import (
-	"net"
-	"testing"
-	"time"
-
-	"github.com/pions/transport/test"
-)
-
-func TestPairSearch(t *testing.T) {
-	// Limit runtime in case of deadlocks
-	lim := test.TimeOut(time.Second * 10)
-	defer lim.Stop()
-
-	var config AgentConfig
-	a, err := NewAgent(&config)
-
-	if err != nil {
-		t.Fatalf("Error constructing ice.Agent")
-	}
-
-	if len(a.validPairs) != 0 {
-		t.Fatalf("TestPairSearch is only a valid test if a.validPairs is empty on construction")
-	}
-
-	cp, err := a.getBestPair()
-
-	if cp != nil {
-		t.Fatalf("No Candidate pairs should exist")
-	}
-
-	if err == nil {
-		t.Fatalf("An error should have been reported (with no available candidate pairs)")
-	}
-
-	err = a.Close()
-
-	if err != nil {
-		t.Fatalf("Close agent emits error %v", err)
-	}
-}
-
-func TestPairPriority(t *testing.T) {
-	// avoid deadlocks?
-	defer test.TimeOut(1 * time.Second).Stop()
-
-	a, err := NewAgent(&AgentConfig{})
-	if err != nil {
-		t.Fatalf("Failed to create agent: %s", err)
-	}
-
-	hostLocal, err := NewCandidateHost(
-		"udp",
-		net.ParseIP("192.168.1.1"), 19216,
-		1,
-	)
-	if err != nil {
-		t.Fatalf("Failed to construct local host candidate: %s", err)
-	}
-
-	relayRemote, err := NewCandidateRelay(
-		"udp",
-		net.ParseIP("1.2.3.4"), 12340,
-		1,
-		"4.3.2.1", 43210,
-	)
-	if err != nil {
-		t.Fatalf("Failed to construct remote relay candidate: %s", err)
-	}
-
-	srflxRemote, err := NewCandidateServerReflexive(
-		"udp",
-		net.ParseIP("10.10.10.2"), 19218,
-		1,
-		"4.3.2.1", 43212,
-	)
-	if err != nil {
-		t.Fatalf("Failed to construct remote srflx candidate: %s", err)
-	}
-
-	prflxRemote, err := NewCandidatePeerReflexive(
-		"udp",
-		net.ParseIP("10.10.10.2"), 19217,
-		1,
-		"4.3.2.1", 43211,
-	)
-	if err != nil {
-		t.Fatalf("Failed to construct remote prflx candidate: %s", err)
-	}
-
-	hostRemote, err := NewCandidateHost(
-		"udp",
-		net.ParseIP("1.2.3.5"), 12350,
-		1,
-	)
-	if err != nil {
-		t.Fatalf("Failed to construct remote host candidate: %s", err)
-	}
-
-	for _, remote := range []*Candidate{relayRemote, srflxRemote, prflxRemote, hostRemote} {
-		a.setValidPair(hostLocal, remote, false, false)
-		bestPair, err := a.getBestPair()
-		if err != nil {
-			t.Fatalf("Failed to get best candidate pair: %s", err)
-		}
-		if bestPair.String() != (&candidatePair{remote: remote, local: hostLocal}).String() {
-			t.Fatalf("Unexpected bestPair %s (expected remote: %s)", bestPair, remote)
-		}
-	}
-
-	if err := a.Close(); err != nil {
-		t.Fatalf("Error on agent.Close(): %s", err)
-	}
-}
-
-func TestOnSelectedCandidatePairChange(t *testing.T) {
-	// avoid deadlocks?
-	defer test.TimeOut(1 * time.Second).Stop()
-
-	a, err := NewAgent(&AgentConfig{})
-	if err != nil {
-		t.Fatalf("Failed to create agent: %s", err)
-	}
-	callbackCalled := make(chan struct{}, 1)
-	if err = a.OnSelectedCandidatePairChange(func(local, remote *Candidate) {
-		close(callbackCalled)
-	}); err != nil {
-		t.Fatalf("Failed to set agent OnCandidatePairChange callback: %s", err)
-	}
-
-	hostLocal, err := NewCandidateHost(
-		"udp",
-		net.ParseIP("192.168.1.1"), 19216,
-		1,
-	)
-	if err != nil {
-		t.Fatalf("Failed to construct local host candidate: %s", err)
-	}
-
-	relayRemote, err := NewCandidateRelay(
-		"udp",
-		net.ParseIP("1.2.3.4"), 12340,
-		1,
-		"4.3.2.1", 43210,
-	)
-	if err != nil {
-		t.Fatalf("Failed to construct remote relay candidate: %s", err)
-	}
-
-	// select the pair
-	if err = a.run(func(agent *Agent) {
-		agent.setValidPair(hostLocal, relayRemote, true, false)
-	}); err != nil {
-		t.Fatalf("Failed to setValidPair(): %s", err)
-	}
-	// ensure that the callback fired on setting the pair
-	<-callbackCalled
-	// set the same pair; this should not invoke the callback
-	// if the callback is invoked now it will panic due
-	// to second close of the channel
-	if err = a.run(func(agent *Agent) {
-		agent.setValidPair(hostLocal, relayRemote, true, false)
-	}); err != nil {
-		t.Fatalf("Failed to setValidPair(): %s", err)
-	}
-	if err := a.Close(); err != nil {
-		t.Fatalf("Error on agent.Close(): %s", err)
-	}
-}
-
-type BadAddr struct{}
-
-func (ba *BadAddr) Network() string {
-	return "xxx"
-}
-func (ba *BadAddr) String() string {
-	return "yyy"
-}
-
-func TestHandlePeerReflexive(t *testing.T) {
-	// Limit runtime in case of deadlocks
-	lim := test.TimeOut(time.Second * 2)
-	defer lim.Stop()
-
-	t.Run("UDP pflx candidate from handleInboud()", func(t *testing.T) {
-		var config AgentConfig
-		a, err := NewAgent(&config)
-
-		if err != nil {
-			t.Fatalf("Error constructing ice.Agent")
-		}
-
-		ip := net.ParseIP("192.168.0.2")
-		local, err := NewCandidateHost("udp", ip, 777, 1)
-		if err != nil {
-			t.Fatalf("failed to create a new candidate: %v", err)
-		}
-
-		remote := &net.UDPAddr{IP: net.ParseIP("172.17.0.3"), Port: 999}
-
-		a.handleInbound(nil, local, remote)
-
-		// length of remote candidate list must be one now
-		if len(a.remoteCandidates) != 1 {
-			t.Fatal("failed to add a network type to the remote candidate list")
-		}
-
-		// length of remote candidate list for a network type must be 1
-		set := a.remoteCandidates[local.NetworkType]
-		if len(set) != 1 {
-			t.Fatal("failed to add prflx candidate to remote candidate list")
-		}
-
-		c := set[0]
-
-		if c.Type != CandidateTypePeerReflexive {
-			t.Fatal("candidate type must be prflx")
-		}
-
-		if !c.IP.Equal(net.ParseIP("172.17.0.3")) {
-			t.Fatal("IP address mismatch")
-		}
-
-		if c.Port != 999 {
-			t.Fatal("Port number mismatch")
-		}
-
-		err = a.Close()
-		if err != nil {
-			t.Fatalf("Close agent emits error %v", err)
-		}
-	})
-
-	t.Run("Bad network type with handleInbound()", func(t *testing.T) {
-		var config AgentConfig
-		a, err := NewAgent(&config)
-
-		if err != nil {
-			t.Fatal("Error constructing ice.Agent")
-		}
-
-		ip := net.ParseIP("192.168.0.2")
-		local, err := NewCandidateHost("tcp", ip, 777, 1)
-		if err != nil {
-			t.Fatalf("failed to create a new candidate: %v", err)
-		}
-
-		remote := &BadAddr{}
-
-		a.handleInbound(nil, local, remote)
-
-		if len(a.remoteCandidates) != 0 {
-			t.Fatal("bad address should not be added to the remote candidate list")
-		}
-
-		err = a.Close()
-		if err != nil {
-			t.Fatalf("Close agent emits error %v", err)
-		}
-	})
-
-	t.Run("TCP prflx with handleNewPeerReflexiveCandidate()", func(t *testing.T) {
-		var config AgentConfig
-		a, err := NewAgent(&config)
-
-		if err != nil {
-			t.Fatal("Error constructing ice.Agent")
-		}
-
-		ip := net.ParseIP("192.168.0.2")
-		local, err := NewCandidateHost("tcp", ip, 777, 1)
-		if err != nil {
-			t.Fatalf("failed to create a new candidate: %v", err)
-		}
-
-		remote := &net.TCPAddr{IP: net.ParseIP("172.17.0.3"), Port: 999}
-
-		err = a.handleNewPeerReflexiveCandidate(local, remote)
-		if err != nil {
-			t.Fatalf("handleNewPeerReflexiveCandidate() should not fail: %v", err)
-		}
-
-		// length of remote candidate list must be one now
-		if len(a.remoteCandidates) != 1 {
-			t.Fatal("failed to add a network type to the remote candidate list")
-		}
-
-		// length of remote candidate list for a network type must be 1
-		set := a.remoteCandidates[local.NetworkType]
-		if len(set) != 1 {
-			t.Fatal("failed to add prflx candidate to remote candidate list")
-		}
-
-		c := set[0]
-
-		if c.Type != CandidateTypePeerReflexive {
-			t.Fatal("candidate type must be prflx")
-		}
-
-		if !c.IP.Equal(net.ParseIP("172.17.0.3")) {
-			t.Fatal("IP address mismatch")
-		}
-
-		if c.Port != 999 {
-			t.Fatal("Port number mismatch")
-		}
-
-		err = a.Close()
-		if err != nil {
-			t.Fatalf("Close agent emits error %v", err)
-		}
-	})
-}
diff --git a/internal/ice/ice_test.go b/internal/ice/ice_test.go
deleted file mode 100644
index 727ae5e1..00000000
--- a/internal/ice/ice_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package ice
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestConnectedState_String(t *testing.T) {
-	testCases := []struct {
-		connectionState ConnectionState
-		expectedString  string
-	}{
-		{ConnectionState(Unknown), "Invalid"},
-		{ConnectionStateNew, "New"},
-		{ConnectionStateChecking, "Checking"},
-		{ConnectionStateConnected, "Connected"},
-		{ConnectionStateCompleted, "Completed"},
-		{ConnectionStateFailed, "Failed"},
-		{ConnectionStateDisconnected, "Disconnected"},
-		{ConnectionStateClosed, "Closed"},
-	}
-
-	for i, testCase := range testCases {
-		assert.Equal(t,
-			testCase.expectedString,
-			testCase.connectionState.String(),
-			"testCase: %d %v", i, testCase,
-		)
-	}
-}
-
-func TestGatheringState_String(t *testing.T) {
-	testCases := []struct {
-		gatheringState GatheringState
-		expectedString string
-	}{
-		{GatheringState(Unknown), ErrUnknownType.Error()},
-		{GatheringStateNew, "new"},
-		{GatheringStateGathering, "gathering"},
-		{GatheringStateComplete, "complete"},
-	}
-
-	for i, testCase := range testCases {
-		assert.Equal(t,
-			testCase.expectedString,
-			testCase.gatheringState.String(),
-			"testCase: %d %v", i, testCase,
-		)
-	}
-}
diff --git a/internal/ice/networktype_test.go b/internal/ice/networktype_test.go
deleted file mode 100644
index 99a1dbb6..00000000
--- a/internal/ice/networktype_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package ice
-
-import (
-	"net"
-	"testing"
-)
-
-func TestNetworkTypeParsing_Success(t *testing.T) {
-	ipv4 := net.ParseIP("192.168.0.1")
-	ipv6 := net.ParseIP("fe80::a3:6ff:fec4:5454")
-
-	for _, test := range []struct {
-		name      string
-		inNetwork string
-		inIP      net.IP
-		expected  NetworkType
-	}{
-		{
-			"lowercase UDP4",
-			"udp",
-			ipv4,
-			NetworkTypeUDP4,
-		},
-		{
-			"uppercase UDP4",
-			"UDP",
-			ipv4,
-			NetworkTypeUDP4,
-		},
-		{
-			"lowercase UDP6",
-			"udp",
-			ipv6,
-			NetworkTypeUDP6,
-		},
-		{
-			"uppercase UDP6",
-			"UDP",
-			ipv6,
-			NetworkTypeUDP6,
-		},
-	} {
-		actual, err := determineNetworkType(test.inNetwork, test.inIP)
-		if err != nil {
-			t.Errorf("NetworkTypeParsing failed: %v", err)
-		}
-		if actual != test.expected {
-			t.Errorf("NetworkTypeParsing: '%s' -- input:%s expected:%s actual:%s",
-				test.name, test.inNetwork, test.expected, actual)
-		}
-	}
-}
-
-func TestNetworkTypeParsing_Failure(t *testing.T) {
-	ipv6 := net.ParseIP("fe80::a3:6ff:fec4:5454")
-
-	for _, test := range []struct {
-		name      string
-		inNetwork string
-		inIP      net.IP
-	}{
-		{
-			"invalid network",
-			"junkNetwork",
-			ipv6,
-		},
-	} {
-		actual, err := determineNetworkType(test.inNetwork, test.inIP)
-		if err == nil {
-			t.Errorf("NetworkTypeParsing should fail: '%s' -- input:%s actual:%s",
-				test.name, test.inNetwork, actual)
-		}
-	}
-}
diff --git a/internal/ice/transport_test.go b/internal/ice/transport_test.go
deleted file mode 100644
index a66ab5a0..00000000
--- a/internal/ice/transport_test.go
+++ /dev/null
@@ -1,297 +0,0 @@
-package ice
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"github.com/pions/transport/test"
-)
-
-func TestStressDuplex(t *testing.T) {
-	// Limit runtime in case of deadlocks
-	lim := test.TimeOut(time.Second * 20)
-	defer lim.Stop()
-
-	// Check for leaking routines
-	report := test.CheckRoutines(t)
-	defer report()
-
-	// Run the test
-	stressDuplex(t)
-}
-
-func testTimeout(t *testing.T, c *Conn, timeout time.Duration) {
-	const pollrate = 100 * time.Millisecond
-	statechan := make(chan ConnectionState)
-	ticker := time.NewTicker(pollrate)
-
-	for cnt := time.Duration(0); cnt <= timeout+taskLoopInterval; cnt += pollrate {
-		<-ticker.C
-		err := c.agent.run(func(agent *Agent) {
-			statechan <- agent.connectionState
-		})
-
-		if err != nil {
-			// we should never get here.
-			panic(err)
-		}
-
-		cs := <-statechan
-		if cs != ConnectionStateConnected {
-			if cnt < timeout {
-				t.Fatalf("Connection timed out early. (after %d ms)", cnt/time.Millisecond)
-			} else {
-				return
-			}
-		}
-	}
-	t.Fatalf("Connection failed to time out in time.")
-
-}
-
-func TestTimeout(t *testing.T) {
-	if testing.Short() {
-		t.Skip("skipping test in short mode.")
-	}
-
-	ca, cb := pipe()
-	err := cb.Close()
-
-	if err != nil {
-		// we should never get here.
-		panic(err)
-	}
-
-	testTimeout(t, ca, 30*time.Second)
-
-	ca, cb = pipeWithTimeout(5*time.Second, 3*time.Second)
-	err = cb.Close()
-
-	if err != nil {
-		// we should never get here.
-		panic(err)
-	}
-
-	testTimeout(t, ca, 5*time.Second)
-}
-
-func TestReadClosed(t *testing.T) {
-	ca, cb := pipe()
-
-	err := ca.Close()
-	if err != nil {
-		// we should never get here.
-		panic(err)
-	}
-
-	err = cb.Close()
-	if err != nil {
-		// we should never get here.
-		panic(err)
-	}
-
-	empty := make([]byte, 10)
-	_, err = ca.Read(empty)
-	if err == nil {
-		t.Fatalf("Reading from a closed channel should return an error")
-	}
-
-}
-
-func stressDuplex(t *testing.T) {
-	ca, cb := pipe()
-
-	defer func() {
-		err := ca.Close()
-		if err != nil {
-			t.Fatal(err)
-		}
-		err = cb.Close()
-		if err != nil {
-			t.Fatal(err)
-		}
-	}()
-
-	opt := test.Options{
-		MsgSize:  10,
-		MsgCount: 1, // Order not reliable due to UDP & potentially multiple candidate pairs.
-	}
-
-	err := test.StressDuplex(ca, cb, opt)
-	if err != nil {
-		t.Fatal(err)
-	}
-}
-
-func Benchmark(b *testing.B) {
-	ca, cb := pipe()
-	defer func() {
-		err := ca.Close()
-		check(err)
-		err = cb.Close()
-		check(err)
-	}()
-
-	b.ResetTimer()
-
-	opt := test.Options{
-		MsgSize:  128,
-		MsgCount: b.N,
-	}
-
-	err := test.StressDuplex(ca, cb, opt)
-	check(err)
-}
-
-func check(err error) {
-	if err != nil {
-		panic(err)
-	}
-}
-
-func connect(aAgent, bAgent *Agent) (*Conn, *Conn) {
-	// Manual signaling
-	aUfrag, aPwd := aAgent.GetLocalUserCredentials()
-	bUfrag, bPwd := bAgent.GetLocalUserCredentials()
-
-	candidates, err := aAgent.GetLocalCandidates()
-	check(err)
-	for _, c := range candidates {
-		check(bAgent.AddRemoteCandidate(copyCandidate(c)))
-	}
-
-	candidates, err = bAgent.GetLocalCandidates()
-	check(err)
-	for _, c := range candidates {
-		check(aAgent.AddRemoteCandidate(copyCandidate(c)))
-	}
-
-	accepted := make(chan struct{})
-	var aConn *Conn
-
-	go func() {
-		var acceptErr error
-		aConn, acceptErr = aAgent.Accept(context.TODO(), bUfrag, bPwd)
-		check(acceptErr)
-		close(accepted)
-	}()
-
-	bConn, err := bAgent.Dial(context.TODO(), aUfrag, aPwd)
-	check(err)
-
-	// Ensure accepted
-	<-accepted
-	return aConn, bConn
-}
-
-func pipe() (*Conn, *Conn) {
-	var urls []*URL
-
-	aNotifier, aConnected := onConnected()
-	bNotifier, bConnected := onConnected()
-
-	aAgent, err := NewAgent(&AgentConfig{
-		Urls:         urls,
-		NetworkTypes: supportedNetworkTypes,
-	})
-	if err != nil {
-		panic(err)
-	}
-	err = aAgent.OnConnectionStateChange(aNotifier)
-	if err != nil {
-		panic(err)
-	}
-
-	bAgent, err := NewAgent(&AgentConfig{
-		Urls:         urls,
-		NetworkTypes: supportedNetworkTypes,
-	})
-	if err != nil {
-		panic(err)
-	}
-	err = bAgent.OnConnectionStateChange(bNotifier)
-	if err != nil {
-		panic(err)
-	}
-
-	aConn, bConn := connect(aAgent, bAgent)
-
-	// Ensure pair selected
-	// Note: this assumes ConnectionStateConnected is thrown after selecting the final pair
-	<-aConnected
-	<-bConnected
-
-	return aConn, bConn
-}
-
-func pipeWithTimeout(iceTimeout time.Duration, iceKeepalive time.Duration) (*Conn, *Conn) {
-	var urls []*URL
-
-	aNotifier, aConnected := onConnected()
-	bNotifier, bConnected := onConnected()
-
-	aAgent, err := NewAgent(&AgentConfig{
-		Urls:              urls,
-		ConnectionTimeout: &iceTimeout,
-		KeepaliveInterval: &iceKeepalive,
-		NetworkTypes:      supportedNetworkTypes,
-	})
-	if err != nil {
-		panic(err)
-	}
-	err = aAgent.OnConnectionStateChange(aNotifier)
-	if err != nil {
-		panic(err)
-	}
-
-	bAgent, err := NewAgent(&AgentConfig{
-		Urls:              urls,
-		ConnectionTimeout: &iceTimeout,
-		KeepaliveInterval: &iceKeepalive,
-		NetworkTypes:      supportedNetworkTypes,
-	})
-	if err != nil {
-		panic(err)
-	}
-	err = bAgent.OnConnectionStateChange(bNotifier)
-	if err != nil {
-		panic(err)
-	}
-
-	aConn, bConn := connect(aAgent, bAgent)
-
-	// Ensure pair selected
-	// Note: this assumes ConnectionStateConnected is thrown after selecting the final pair
-	<-aConnected
-	<-bConnected
-
-	return aConn, bConn
-}
-
-func copyCandidate(orig *Candidate) *Candidate {
-	c := &Candidate{
-		Type:        orig.Type,
-		NetworkType: orig.NetworkType,
-		IP:          orig.IP,
-		Port:        orig.Port,
-	}
-
-	if orig.RelatedAddress != nil {
-		c.RelatedAddress = &CandidateRelatedAddress{
-			Address: orig.RelatedAddress.Address,
-			Port:    orig.RelatedAddress.Port,
-		}
-	}
-
-	return c
-}
-
-func onConnected() (func(ConnectionState), chan struct{}) {
-	done := make(chan struct{})
-	return func(state ConnectionState) {
-		if state == ConnectionStateConnected {
-			close(done)
-		}
-	}, done
-}
diff --git a/internal/ice/url_test.go b/internal/ice/url_test.go
deleted file mode 100644
index 2c156731..00000000
--- a/internal/ice/url_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package ice
-
-import (
-	"errors"
-	"testing"
-
-	"github.com/pions/webrtc/pkg/rtcerr"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestParseURL(t *testing.T) {
-	t.Run("Success", func(t *testing.T) {
-		testCases := []struct {
-			rawURL            string
-			expectedURLString string
-			expectedScheme    SchemeType
-			expectedSecure    bool
-			expectedHost      string
-			expectedPort      int
-			expectedProto     ProtoType
-		}{
-			{"stun:google.de", "stun:google.de:3478", SchemeTypeSTUN, false, "google.de", 3478, ProtoTypeUDP},
-			{"stun:google.de:1234", "stun:google.de:1234", SchemeTypeSTUN, false, "google.de", 1234, ProtoTypeUDP},
-			{"stuns:google.de", "stuns:google.de:5349", SchemeTypeSTUNS, true, "google.de", 5349, ProtoTypeTCP},
-			{"stun:[::1]:123", "stun:[::1]:123", SchemeTypeSTUN, false, "::1", 123, ProtoTypeUDP},
-			{"turn:google.de", "turn:google.de:3478?transport=udp", SchemeTypeTURN, false, "google.de", 3478, ProtoTypeUDP},
-			{"turns:google.de", "turns:google.de:5349?transport=tcp", SchemeTypeTURNS, true, "google.de", 5349, ProtoTypeTCP},
-			{"turn:google.de?transport=udp", "turn:google.de:3478?transport=udp", SchemeTypeTURN, false, "google.de", 3478, ProtoTypeUDP},
-			{"turns:google.de?transport=tcp", "turns:google.de:5349?transport=tcp", SchemeTypeTURNS, true, "google.de", 5349, ProtoTypeTCP},
-		}
-
-		for i, testCase := range testCases {
-			url, err := ParseURL(testCase.rawURL)
-			assert.Nil(t, err, "testCase: %d %v", i, testCase)
-			if err != nil {
-				return
-			}
-
-			assert.Equal(t, testCase.expectedScheme, url.Scheme, "testCase: %d %v", i, testCase)
-			assert.Equal(t, testCase.expectedURLString, url.String(), "testCase: %d %v", i, testCase)
-			assert.Equal(t, testCase.expectedSecure, url.IsSecure(), "testCase: %d %v", i, testCase)
-			assert.Equal(t, testCase.expectedHost, url.Host, "testCase: %d %v", i, testCase)
-			assert.Equal(t, testCase.expectedPort, url.Port, "testCase: %d %v", i, testCase)
-			assert.Equal(t, testCase.expectedProto, url.Proto, "testCase: %d %v", i, testCase)
-		}
-	})
-	t.Run("Failure", func(t *testing.T) {
-		testCases := []struct {
-			rawURL      string
-			expectedErr error
-		}{
-			{"", &rtcerr.SyntaxError{Err: ErrSchemeType}},
-			{":::", &rtcerr.UnknownError{Err: errors.New("parse :::: missing protocol scheme")}},
-			{"stun:[::1]:123:", &rtcerr.UnknownError{Err: errors.New("address [::1]:123:: too many colons in address")}},
-			{"stun:[::1]:123a", &rtcerr.SyntaxError{Err: ErrPort}},
-			{"google.de", &rtcerr.SyntaxError{Err: ErrSchemeType}},
-			{"stun:", &rtcerr.SyntaxError{Err: ErrHost}},
-			{"stun:google.de:abc", &rtcerr.SyntaxError{Err: ErrPort}},
-			{"stun:google.de?transport=udp", &rtcerr.SyntaxError{Err: ErrSTUNQuery}},
-			{"stuns:google.de?transport=udp", &rtcerr.SyntaxError{Err: ErrSTUNQuery}},
-			{"turn:google.de?trans=udp", &rtcerr.SyntaxError{Err: ErrInvalidQuery}},
-			{"turns:google.de?trans=udp", &rtcerr.SyntaxError{Err: ErrInvalidQuery}},
-			{"turns:google.de?transport=udp&another=1", &rtcerr.SyntaxError{Err: ErrInvalidQuery}},
-			{"turn:google.de?transport=ip", &rtcerr.NotSupportedError{Err: ErrProtoType}},
-		}
-
-		for i, testCase := range testCases {
-			_, err := ParseURL(testCase.rawURL)
-			assert.EqualError(t, err, testCase.expectedErr.Error(), "testCase: %d %v", i, testCase)
-		}
-	})
-}
diff --git a/peerconnection_go_test.go b/peerconnection_go_test.go
index e0546a23..02cb54f0 100644
--- a/peerconnection_go_test.go
+++ b/peerconnection_go_test.go
@@ -12,8 +12,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/pions/ice"
 	"github.com/pions/transport/test"
-	"github.com/pions/webrtc/internal/ice"
 	"github.com/pions/webrtc/internal/mux"
 	"github.com/pions/webrtc/pkg/rtcerr"
 	"github.com/stretchr/testify/assert"