🎉 live server seems to be working now
This commit is contained in:
14
third_party/sendspin-go/pkg/discovery/doc.go
vendored
Normal file
14
third_party/sendspin-go/pkg/discovery/doc.go
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// ABOUTME: mDNS service discovery package
|
||||
// ABOUTME: Discover and advertise Resonate servers on local network
|
||||
// Package discovery provides mDNS service discovery for Resonate servers.
|
||||
//
|
||||
// Allows discovering servers on the local network and advertising
|
||||
// server availability.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// services, err := discovery.Discover(5 * time.Second)
|
||||
// for _, svc := range services {
|
||||
// fmt.Printf("Found: %s at %s:%d\n", svc.Name, svc.Address, svc.Port)
|
||||
// }
|
||||
package discovery
|
||||
169
third_party/sendspin-go/pkg/discovery/mdns.go
vendored
Normal file
169
third_party/sendspin-go/pkg/discovery/mdns.go
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
// ABOUTME: mDNS service discovery for Sendspin Protocol
|
||||
// ABOUTME: Handles both advertisement (server-initiated) and browsing (client-initiated)
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/hashicorp/mdns"
|
||||
)
|
||||
|
||||
// silentLogger discards hashicorp/mdns internal logs
|
||||
var silentLogger = log.New(io.Discard, "", 0)
|
||||
|
||||
type Config struct {
|
||||
ServiceName string
|
||||
Port int
|
||||
ServerMode bool // If true, advertise as _sendspin-server._tcp, otherwise _sendspin._tcp
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
config Config
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
servers chan *ServerInfo
|
||||
}
|
||||
|
||||
type ServerInfo struct {
|
||||
Name string
|
||||
Host string
|
||||
Port int
|
||||
}
|
||||
|
||||
func NewManager(config Config) *Manager {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
return &Manager{
|
||||
config: config,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
servers: make(chan *ServerInfo, 10),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Advertise() error {
|
||||
ips, err := getLocalIPs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get local IPs: %w", err)
|
||||
}
|
||||
|
||||
serviceType := "_sendspin._tcp"
|
||||
if m.config.ServerMode {
|
||||
serviceType = "_sendspin-server._tcp"
|
||||
}
|
||||
|
||||
service, err := mdns.NewMDNSService(
|
||||
m.config.ServiceName,
|
||||
serviceType,
|
||||
"",
|
||||
"",
|
||||
m.config.Port,
|
||||
ips,
|
||||
[]string{"path=/sendspin"},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create service: %w", err)
|
||||
}
|
||||
|
||||
server, err := mdns.NewServer(&mdns.Config{Zone: service, Logger: silentLogger})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create mdns server: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Advertising mDNS service: %s on port %d (type: %s)", m.config.ServiceName, m.config.Port, serviceType)
|
||||
|
||||
go func() {
|
||||
<-m.ctx.Done()
|
||||
server.Shutdown()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Browse() error {
|
||||
go m.browseLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) browseLoop() {
|
||||
for {
|
||||
select {
|
||||
case <-m.ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
entries := make(chan *mdns.ServiceEntry, 10)
|
||||
|
||||
go func() {
|
||||
for entry := range entries {
|
||||
server := &ServerInfo{
|
||||
Name: entry.Name,
|
||||
Host: entry.AddrV4.String(),
|
||||
Port: entry.Port,
|
||||
}
|
||||
|
||||
log.Printf("Discovered server: %s at %s:%d", server.Name, server.Host, server.Port)
|
||||
|
||||
select {
|
||||
case m.servers <- server:
|
||||
case <-m.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
params := &mdns.QueryParam{
|
||||
Service: "_sendspin-server._tcp",
|
||||
Domain: "local",
|
||||
Timeout: 3,
|
||||
Entries: entries,
|
||||
Logger: silentLogger,
|
||||
}
|
||||
|
||||
mdns.Query(params)
|
||||
close(entries)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Servers() <-chan *ServerInfo {
|
||||
return m.servers
|
||||
}
|
||||
|
||||
func (m *Manager) Stop() {
|
||||
m.cancel()
|
||||
}
|
||||
|
||||
func getLocalIPs() ([]net.IP, error) {
|
||||
var ips []net.IP
|
||||
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
ips = append(ips, ipnet.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ips, nil
|
||||
}
|
||||
196
third_party/sendspin-go/pkg/discovery/mdns_test.go
vendored
Normal file
196
third_party/sendspin-go/pkg/discovery/mdns_test.go
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
// ABOUTME: Tests for mDNS service discovery
|
||||
// ABOUTME: Validates Manager creation, configuration, and lifecycle
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewManager(t *testing.T) {
|
||||
config := Config{
|
||||
ServiceName: "test-service",
|
||||
Port: 8080,
|
||||
ServerMode: false,
|
||||
}
|
||||
|
||||
manager := NewManager(config)
|
||||
|
||||
if manager == nil {
|
||||
t.Fatal("NewManager returned nil")
|
||||
}
|
||||
|
||||
if manager.config.ServiceName != "test-service" {
|
||||
t.Errorf("Expected ServiceName 'test-service', got '%s'", manager.config.ServiceName)
|
||||
}
|
||||
|
||||
if manager.config.Port != 8080 {
|
||||
t.Errorf("Expected Port 8080, got %d", manager.config.Port)
|
||||
}
|
||||
|
||||
if manager.config.ServerMode != false {
|
||||
t.Errorf("Expected ServerMode false, got %v", manager.config.ServerMode)
|
||||
}
|
||||
|
||||
if manager.servers == nil {
|
||||
t.Error("servers channel should not be nil")
|
||||
}
|
||||
|
||||
if manager.ctx == nil {
|
||||
t.Error("ctx should not be nil")
|
||||
}
|
||||
|
||||
if manager.cancel == nil {
|
||||
t.Error("cancel should not be nil")
|
||||
}
|
||||
|
||||
manager.Stop()
|
||||
}
|
||||
|
||||
func TestManagerServerMode(t *testing.T) {
|
||||
config := Config{
|
||||
ServiceName: "test-server",
|
||||
Port: 9090,
|
||||
ServerMode: true,
|
||||
}
|
||||
|
||||
manager := NewManager(config)
|
||||
defer manager.Stop()
|
||||
|
||||
if !manager.config.ServerMode {
|
||||
t.Error("Expected ServerMode to be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerServersChannel(t *testing.T) {
|
||||
config := Config{
|
||||
ServiceName: "test",
|
||||
Port: 8080,
|
||||
ServerMode: false,
|
||||
}
|
||||
|
||||
manager := NewManager(config)
|
||||
defer manager.Stop()
|
||||
|
||||
serversChan := manager.Servers()
|
||||
|
||||
if serversChan == nil {
|
||||
t.Fatal("Servers() returned nil channel")
|
||||
}
|
||||
|
||||
if serversChan != manager.servers {
|
||||
t.Error("Servers() should return the manager's servers channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerStop(t *testing.T) {
|
||||
config := Config{
|
||||
ServiceName: "test",
|
||||
Port: 8080,
|
||||
ServerMode: false,
|
||||
}
|
||||
|
||||
manager := NewManager(config)
|
||||
|
||||
manager.Stop()
|
||||
|
||||
select {
|
||||
case <-manager.ctx.Done():
|
||||
// Expected - context should be cancelled
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Error("Context should be cancelled after Stop()")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLocalIPs(t *testing.T) {
|
||||
ips, err := getLocalIPs()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("getLocalIPs failed: %v", err)
|
||||
}
|
||||
|
||||
// We should have at least one non-loopback IPv4 address on most systems
|
||||
// This test may be environment-dependent, so we just verify it doesn't crash
|
||||
// and returns a non-nil slice
|
||||
if ips == nil {
|
||||
t.Error("getLocalIPs returned nil slice")
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
if ip.To4() == nil {
|
||||
t.Errorf("getLocalIPs returned non-IPv4 address: %v", ip)
|
||||
}
|
||||
if ip.IsLoopback() {
|
||||
t.Errorf("getLocalIPs returned loopback address: %v", ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerInfo(t *testing.T) {
|
||||
info := &ServerInfo{
|
||||
Name: "test-server",
|
||||
Host: "192.168.1.100",
|
||||
Port: 8080,
|
||||
}
|
||||
|
||||
if info.Name != "test-server" {
|
||||
t.Errorf("Expected Name 'test-server', got '%s'", info.Name)
|
||||
}
|
||||
|
||||
if info.Host != "192.168.1.100" {
|
||||
t.Errorf("Expected Host '192.168.1.100', got '%s'", info.Host)
|
||||
}
|
||||
|
||||
if info.Port != 8080 {
|
||||
t.Errorf("Expected Port 8080, got %d", info.Port)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config Config
|
||||
}{
|
||||
{
|
||||
name: "client mode",
|
||||
config: Config{
|
||||
ServiceName: "client",
|
||||
Port: 8080,
|
||||
ServerMode: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "server mode",
|
||||
config: Config{
|
||||
ServiceName: "server",
|
||||
Port: 9090,
|
||||
ServerMode: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "different port",
|
||||
config: Config{
|
||||
ServiceName: "test",
|
||||
Port: 12345,
|
||||
ServerMode: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
manager := NewManager(tt.config)
|
||||
defer manager.Stop()
|
||||
|
||||
if manager.config.ServiceName != tt.config.ServiceName {
|
||||
t.Errorf("ServiceName: expected %s, got %s", tt.config.ServiceName, manager.config.ServiceName)
|
||||
}
|
||||
if manager.config.Port != tt.config.Port {
|
||||
t.Errorf("Port: expected %d, got %d", tt.config.Port, manager.config.Port)
|
||||
}
|
||||
if manager.config.ServerMode != tt.config.ServerMode {
|
||||
t.Errorf("ServerMode: expected %v, got %v", tt.config.ServerMode, manager.config.ServerMode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user