// Diagnostic probe: advertise _sendspin._tcp via mDNS, listen on /sendspin, // and log exactly what the server (Music Assistant) sends after it dials us — // WITHOUT sending anything first. Reveals whether MA expects to speak first // (role flip) or waits for our client/hello. package main import ( "flag" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/Sendspin/sendspin-go/pkg/discovery" "github.com/gorilla/websocket" ) func main() { name := flag.String("name", hostname(), "advertised name") port := flag.Int("port", 8928, "listen + mDNS port") speakFirst := flag.Bool("speak-first", false, "send a server/hello before reading (test role-flip hypothesis)") flag.Parse() disc := discovery.NewManager(discovery.Config{ServiceName: *name, Port: *port}) if err := disc.Advertise(); err != nil { log.Printf("mdns advertise: %v", err) } else { defer disc.Stop() } upgrader := websocket.Upgrader{CheckOrigin: func(*http.Request) bool { return true }} mux := http.NewServeMux() mux.HandleFunc("/sendspin", func(w http.ResponseWriter, r *http.Request) { log.Printf("=== incoming dial from %s (headers: subproto=%q origin=%q UA=%q) ===", r.RemoteAddr, r.Header.Get("Sec-WebSocket-Protocol"), r.Header.Get("Origin"), r.Header.Get("User-Agent")) conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Printf("upgrade failed: %v", err) return } defer conn.Close() if *speakFirst { hello := `{"type":"server/hello","payload":{"server_id":"probe","name":"probe","version":1,"active_roles":["player@v1"],"connection_reason":"discovery"}}` log.Printf("-> sending server/hello: %s", hello) if err := conn.WriteMessage(websocket.TextMessage, []byte(hello)); err != nil { log.Printf("write failed: %v", err) } } log.Printf("listening for first message from server (10s)...") for { conn.SetReadDeadline(time.Now().Add(10 * time.Second)) mt, data, err := conn.ReadMessage() if err != nil { log.Printf("read ended: %v", err) if ce, ok := err.(*websocket.CloseError); ok { log.Printf("CLOSE code=%d text=%q", ce.Code, ce.Text) } return } if mt == websocket.BinaryMessage { log.Printf("<- SERVER binary %d bytes", len(data)) } else { log.Printf("<- SERVER text: %s", string(data)) } } }) srv := &http.Server{Addr: fmt.Sprintf(":%d", *port), Handler: mux} go func() { log.Printf("probe listening on :%d/sendspin as %q", *port, *name) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("listen: %v", err) } }() sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGTERM) <-sig } func hostname() string { h, _ := os.Hostname() if h == "" { return "sendspin-probe" } return h + "-probe" }