Add multi-port wrappers for UDPMux and TCPMux

These wrappers allow a caller to provide UDPMux and TCPMux instances
to the ICE agent that represent multiple open ports. This can be
desirable in what would otherwise be single-port deployments, as it
increases the chance that one of the fixed ports will not be blocked
by a users firewall.
This commit is contained in:
Kevin Caffrey
2022-09-04 17:21:20 -04:00
parent be69d2c2ae
commit 169ff6a7b4
8 changed files with 592 additions and 67 deletions

128
tcp_mux_multi_test.go Normal file
View File

@@ -0,0 +1,128 @@
//go:build !js
// +build !js
package ice
import (
"io"
"net"
"testing"
"github.com/pion/logging"
"github.com/pion/stun"
"github.com/pion/transport/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMultiTCPMux_Recv(t *testing.T) {
for name, buffersize := range map[string]int{
"no buffer": 0,
"buffered 4MB": 4 * 1024 * 1024,
} {
bufSize := buffersize
t.Run(name, func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
loggerFactory := logging.NewDefaultLoggerFactory()
var muxInstances []TCPMux
for i := 0; i < 3; i++ {
listener, err := net.ListenTCP("tcp", &net.TCPAddr{
IP: net.IP{127, 0, 0, 1},
Port: 0,
})
require.NoError(t, err, "error starting listener")
defer func() {
_ = listener.Close()
}()
tcpMux := NewTCPMuxDefault(TCPMuxParams{
Listener: listener,
Logger: loggerFactory.NewLogger("ice"),
ReadBufferSize: 20,
WriteBufferSize: bufSize,
})
muxInstances = append(muxInstances, tcpMux)
require.NotNil(t, tcpMux.LocalAddr(), "tcpMux.LocalAddr() is nil")
}
multiMux := NewMultiTCPMuxDefault(muxInstances...)
defer func() {
_ = multiMux.Close()
}()
pktConns, err := multiMux.GetAllConns("myufrag", false)
require.NoError(t, err, "error retrieving muxed connection for ufrag")
for _, pktConn := range pktConns {
defer func() {
_ = pktConn.Close()
}()
conn, err := net.DialTCP("tcp", nil, pktConn.LocalAddr().(*net.TCPAddr))
require.NoError(t, err, "error dialing test tcp connection")
msg := stun.New()
msg.Type = stun.MessageType{Method: stun.MethodBinding, Class: stun.ClassRequest}
msg.Add(stun.AttrUsername, []byte("myufrag:otherufrag"))
msg.Encode()
n, err := writeStreamingPacket(conn, msg.Raw)
require.NoError(t, err, "error writing tcp stun packet")
recv := make([]byte, n)
n2, raddr, err := pktConn.ReadFrom(recv)
require.NoError(t, err, "error receiving data")
assert.Equal(t, conn.LocalAddr(), raddr, "remote tcp address mismatch")
assert.Equal(t, n, n2, "received byte size mismatch")
assert.Equal(t, msg.Raw, recv, "received bytes mismatch")
// check echo response
n, err = pktConn.WriteTo(recv, conn.LocalAddr())
require.NoError(t, err, "error writing echo stun packet")
recvEcho := make([]byte, n)
n3, err := readStreamingPacket(conn, recvEcho)
require.NoError(t, err, "error receiving echo data")
assert.Equal(t, n2, n3, "received byte size mismatch")
assert.Equal(t, msg.Raw, recvEcho, "received bytes mismatch")
}
})
}
}
func TestMultiTCPMux_NoDeadlockWhenClosingUnusedPacketConn(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
loggerFactory := logging.NewDefaultLoggerFactory()
var tcpMuxInstances []TCPMux
for i := 0; i < 3; i++ {
listener, err := net.ListenTCP("tcp", &net.TCPAddr{
IP: net.IP{127, 0, 0, 1},
Port: 0,
})
require.NoError(t, err, "error starting listener")
defer func() {
_ = listener.Close()
}()
tcpMux := NewTCPMuxDefault(TCPMuxParams{
Listener: listener,
Logger: loggerFactory.NewLogger("ice"),
ReadBufferSize: 20,
})
tcpMuxInstances = append(tcpMuxInstances, tcpMux)
}
muxMulti := NewMultiTCPMuxDefault(tcpMuxInstances...)
_, err := muxMulti.GetAllConns("test", false)
require.NoError(t, err, "error getting conn by ufrag")
require.NoError(t, muxMulti.Close(), "error closing tcpMux")
conn, err := muxMulti.GetAllConns("test", false)
assert.Nil(t, conn, "should receive nil because mux is closed")
assert.Equal(t, io.ErrClosedPipe, err, "should receive error because mux is closed")
}