Implemented and fixed many issues
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
//go:build linux
|
||||
|
||||
package wshub
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// setMulticastIf pins a socket's outgoing multicast interface (IP_MULTICAST_IF),
|
||||
// which is exactly what UDPStreamer/UDPSServer does with its `Interface` key.
|
||||
func setMulticastIf(t *testing.T, conn *net.UDPConn, ip [4]byte) {
|
||||
t.Helper()
|
||||
rc, err := conn.SyscallConn()
|
||||
if err != nil {
|
||||
t.Fatalf("SyscallConn: %v", err)
|
||||
}
|
||||
var sockErr error
|
||||
if err := rc.Control(func(fd uintptr) {
|
||||
sockErr = syscall.SetsockoptInet4Addr(int(fd), syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, ip)
|
||||
}); err != nil {
|
||||
t.Fatalf("Control: %v", err)
|
||||
}
|
||||
if sockErr != nil {
|
||||
t.Fatalf("IP_MULTICAST_IF: %v", sockErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceForIPResolvesLoopback(t *testing.T) {
|
||||
ifi := interfaceForIP(net.ParseIP("127.0.0.1"))
|
||||
if ifi == nil {
|
||||
t.Fatal("no interface resolved for 127.0.0.1")
|
||||
}
|
||||
if ifi.Flags&net.FlagLoopback == 0 {
|
||||
t.Fatalf("resolved %q for 127.0.0.1, which is not a loopback interface", ifi.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceForIPUnknownAddressIsNil(t *testing.T) {
|
||||
// Unspecified and unassigned addresses must fall back to "let the kernel
|
||||
// choose" rather than resolving to an arbitrary interface.
|
||||
if ifi := interfaceForIP(net.IPv4zero); ifi != nil {
|
||||
t.Fatalf("0.0.0.0 resolved to %q, want nil", ifi.Name)
|
||||
}
|
||||
if ifi := interfaceForIP(nil); ifi != nil {
|
||||
t.Fatalf("nil IP resolved to %q, want nil", ifi.Name)
|
||||
}
|
||||
if ifi := interfaceForIP(net.ParseIP("203.0.113.42")); ifi != nil {
|
||||
t.Fatalf("unassigned address resolved to %q, want nil", ifi.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMulticastJoinOnControlInterfaceReceivesData is the regression test for the
|
||||
// bug that left the web UI permanently blank: the hub joined the group with a
|
||||
// nil interface, so imr_interface stayed INADDR_ANY and the kernel picked the
|
||||
// default-route interface. A UDPStreamer configured with Interface = "127.0.0.1"
|
||||
// sends out the loopback instead, and every datagram was silently dropped.
|
||||
//
|
||||
// The sender here mimics that server exactly (IP_MULTICAST_IF = 127.0.0.1); the
|
||||
// receiver joins the way runMulticastSession now does, via the interface that
|
||||
// owns the control connection's local address.
|
||||
func TestMulticastJoinOnControlInterfaceReceivesData(t *testing.T) {
|
||||
const group = "239.255.13.37"
|
||||
|
||||
ifi := interfaceForIP(net.ParseIP("127.0.0.1"))
|
||||
if ifi == nil {
|
||||
t.Skip("no loopback interface available")
|
||||
}
|
||||
|
||||
rx, err := net.ListenMulticastUDP("udp4", ifi, &net.UDPAddr{IP: net.ParseIP(group), Port: 0})
|
||||
if err != nil {
|
||||
t.Fatalf("join %s on %s: %v", group, ifi.Name, err)
|
||||
}
|
||||
defer rx.Close()
|
||||
port := rx.LocalAddr().(*net.UDPAddr).Port
|
||||
|
||||
tx, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.ParseIP("127.0.0.1")})
|
||||
if err != nil {
|
||||
t.Fatalf("sender socket: %v", err)
|
||||
}
|
||||
defer tx.Close()
|
||||
setMulticastIf(t, tx, [4]byte{127, 0, 0, 1})
|
||||
|
||||
payload := []byte("UDPS-multicast-probe")
|
||||
dst := &net.UDPAddr{IP: net.ParseIP(group), Port: port}
|
||||
|
||||
// Datagrams are lossy even on loopback if the join has not settled, so send
|
||||
// a few and accept the first that lands.
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
tx.WriteToUDP(payload, dst)
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
buf := make([]byte, 128)
|
||||
if err := rx.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, _, err := rx.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("no multicast received on %s within 3s: %v", ifi.Name, err)
|
||||
}
|
||||
if got := string(buf[:n]); got != string(payload) {
|
||||
t.Fatalf("payload = %q, want %q", got, payload)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user