mirror of
https://codeberg.org/cunicu/cunicu.git
synced 2025-09-27 05:06:02 +08:00
feat(mcast-backend): add config parsing and update test
Signed-off-by: Adam Rizkalla <ajarizzo@gmail.com>
This commit is contained in:

committed by
Steffen Vogel

parent
b0ea76d023
commit
672f352e5f
@@ -14,7 +14,6 @@ import (
|
|||||||
signalingproto "cunicu.li/cunicu/pkg/proto/signaling"
|
signalingproto "cunicu.li/cunicu/pkg/proto/signaling"
|
||||||
"cunicu.li/cunicu/pkg/signaling"
|
"cunicu.li/cunicu/pkg/signaling"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/net/ipv4"
|
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,8 +27,7 @@ func init() { //nolint:gochecknoinits
|
|||||||
type Backend struct {
|
type Backend struct {
|
||||||
signaling.SubscriptionsRegistry
|
signaling.SubscriptionsRegistry
|
||||||
|
|
||||||
send_conn net.PacketConn
|
conn *net.UDPConn
|
||||||
recv_conn *net.UDPConn
|
|
||||||
mcast_addr *net.UDPAddr
|
mcast_addr *net.UDPAddr
|
||||||
config BackendConfig
|
config BackendConfig
|
||||||
|
|
||||||
@@ -42,49 +40,38 @@ func NewBackend(cfg *signaling.BackendConfig, logger *log.Logger) (signaling.Bac
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
//if err := b.config.Parse(cfg); err != nil {
|
|
||||||
// return nil, fmt.Errorf("failed to parse backend configuration: %w", err)
|
|
||||||
//}
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Parse multicast group
|
if err = b.config.Parse(cfg); err != nil {
|
||||||
if b.mcast_addr, err = net.ResolveUDPAddr("udp", "224.0.0.1:9999"); err != nil {
|
return nil, fmt.Errorf("failed to parse backend configuration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse multicast group address
|
||||||
|
if b.mcast_addr, err = net.ResolveUDPAddr("udp", b.config.Target); err != nil {
|
||||||
return nil, fmt.Errorf("Error parsing multicast address: %w", err)
|
return nil, fmt.Errorf("Error parsing multicast address: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind to any available local UDP port for sending to multicast group
|
|
||||||
if b.send_conn, err = net.ListenPacket("udp", ":0"); err != nil {
|
|
||||||
return nil, fmt.Errorf("Error binding to local address: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p := ipv4.NewPacketConn(b.send_conn)
|
|
||||||
|
|
||||||
if err := p.JoinGroup(nil, b.mcast_addr); err != nil {
|
|
||||||
return nil, fmt.Errorf("Error joining multicast group: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add listener for multicast group
|
// Add listener for multicast group
|
||||||
if b.recv_conn, err = net.ListenMulticastUDP("udp", nil, b.mcast_addr); err != nil {
|
if b.conn, err = net.ListenMulticastUDP("udp", b.config.Options.Interface, b.mcast_addr); err != nil {
|
||||||
return nil, fmt.Errorf("Error adding multicast listener: %w", err)
|
return nil, fmt.Errorf("Error adding multicast listener: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable multicast loopback
|
if b.config.Options.Loopback {
|
||||||
fd, _ := b.recv_conn.File()
|
// Enable multicast loopback
|
||||||
syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 1)
|
fd, _ := b.conn.File()
|
||||||
//syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 1)
|
syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 1)
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
buf := make([]byte, 4096)
|
buf := make([]byte, 4096)
|
||||||
for {
|
for {
|
||||||
n, _, err := b.recv_conn.ReadFrom(buf)
|
n, err := b.conn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == net.ErrClosed {
|
if err == net.ErrClosed {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
b.logger.Error("Error reading from UDPConn", zap.Error(err))
|
b.logger.Error("Error reading from UDPConn", zap.Error(err))
|
||||||
break
|
continue
|
||||||
//continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var env signalingproto.Envelope
|
var env signalingproto.Envelope
|
||||||
@@ -135,7 +122,7 @@ func (b *Backend) Publish(ctx context.Context, kp *crypto.KeyPair, msg *signalin
|
|||||||
return fmt.Errorf("Error marshaling protobuf: %w", err)
|
return fmt.Errorf("Error marshaling protobuf: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = b.send_conn.WriteTo(data, b.mcast_addr); err != nil {
|
if _, err = b.conn.WriteTo(data, b.mcast_addr); err != nil {
|
||||||
return fmt.Errorf("failed to publish message: %w", err)
|
return fmt.Errorf("failed to publish message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +130,9 @@ func (b *Backend) Publish(ctx context.Context, kp *crypto.KeyPair, msg *signalin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) Close() error {
|
func (b *Backend) Close() error {
|
||||||
//return fmt.Errorf("Close() called")
|
// NOTE: Do not close the connection; on certain OS (like Linux),
|
||||||
|
// The UDPConn.Read() will continue to block even if the connection
|
||||||
|
// is closed
|
||||||
//if err := b.conn.Close(); err != nil {
|
//if err := b.conn.Close(); err != nil {
|
||||||
// return fmt.Errorf("failed to close multicast connection: %w", err)
|
// return fmt.Errorf("failed to close multicast connection: %w", err)
|
||||||
//}
|
//}
|
||||||
|
@@ -22,7 +22,9 @@ func TestSuite(t *testing.T) {
|
|||||||
|
|
||||||
var _ = Describe("Multicast backend", func() {
|
var _ = Describe("Multicast backend", func() {
|
||||||
u := url.URL{
|
u := url.URL{
|
||||||
Scheme: "multicast",
|
Scheme: "multicast",
|
||||||
|
Host: "239.0.0.1:9999",
|
||||||
|
RawQuery: "interface=lo&loopback=true",
|
||||||
}
|
}
|
||||||
|
|
||||||
test.BackendTest(&u, 10)
|
test.BackendTest(&u, 10)
|
||||||
|
@@ -4,23 +4,31 @@
|
|||||||
package mcast
|
package mcast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"cunicu.li/cunicu/pkg/signaling"
|
"cunicu.li/cunicu/pkg/signaling"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BackendOptions struct {
|
||||||
|
Interface *net.Interface
|
||||||
|
Loopback bool
|
||||||
|
}
|
||||||
|
|
||||||
type BackendConfig struct {
|
type BackendConfig struct {
|
||||||
signaling.BackendConfig
|
signaling.BackendConfig
|
||||||
|
|
||||||
Target string
|
Target string
|
||||||
Loopback bool
|
Options BackendOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BackendConfig) Parse(cfg *signaling.BackendConfig) (err error) {
|
func (c *BackendConfig) Parse(cfg *signaling.BackendConfig) (err error) {
|
||||||
c.BackendConfig = *cfg
|
c.BackendConfig = *cfg
|
||||||
|
|
||||||
//c.Target, c.Loopback, err = ParseURL(c.BackendConfig.URI.String())
|
c.Target, c.Options, err = ParseURL(c.BackendConfig.URI.String())
|
||||||
//if err != nil {
|
if err != nil {
|
||||||
// return fmt.Errorf("failed to parse multicast URL: %w", err)
|
return fmt.Errorf("failed to parse multicast URL: %w", err)
|
||||||
//}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -3,3 +3,47 @@
|
|||||||
|
|
||||||
// Package mcast implements a signaling backend using multicast
|
// Package mcast implements a signaling backend using multicast
|
||||||
package mcast
|
package mcast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidAddress = errors.New("missing multicast address")
|
||||||
|
|
||||||
|
func ParseURL(urlStr string) (string, BackendOptions, error) {
|
||||||
|
|
||||||
|
o := BackendOptions{
|
||||||
|
Interface: nil,
|
||||||
|
Loopback: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", o, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := u.Query()
|
||||||
|
|
||||||
|
if q.Has("interface") {
|
||||||
|
if o.Interface, err = net.InterfaceByName(q.Get("interface")); err != nil {
|
||||||
|
return "", o, fmt.Errorf("failed to parse 'interface' option: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.Has("loopback") {
|
||||||
|
var err error
|
||||||
|
if o.Loopback, err = strconv.ParseBool(q.Get("loopback")); err != nil {
|
||||||
|
return "", o, fmt.Errorf("failed to parse 'loopback' option: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Host == "" {
|
||||||
|
return "", o, errInvalidAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.Host, o, nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user