// Command mwse is the MWSE engine: a WebSocket relay that virtualizes connected // peers so they can exchange data through rooms, pairings and data tunnels without // knowing one another's real identity. It is the Go rewrite of the original // Node.js engine; the on-the-wire SDK contract is unchanged. package main import ( "context" "errors" "log" "net/http" "os" "os/signal" "syscall" "time" "git.saqut.com/saqut/mwse/internal/config" "git.saqut.com/saqut/mwse/internal/httpserver" "git.saqut.com/saqut/mwse/internal/services" "git.saqut.com/saqut/mwse/internal/ws" ) func main() { cfg := config.Load() hub := ws.NewHub() reg := services.Register(hub) // The notify and datastore stores hold entries with a TTL; their janitors // reclaim expired ones so memory cannot grow without bound. They are stopped // during shutdown so no goroutine is leaked. stopNotify := reg.Notify.StartJanitor(time.Minute) stopData := reg.Data.StartJanitor(time.Minute) defer stopNotify() defer stopData() srv := httpserver.New(hub, cfg) // Run the listener in the background so main can wait for a shutdown signal. serverErr := make(chan error, 1) go func() { log.Printf("MWSE engine listening on %s", cfg.Addr()) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { serverErr <- err } }() // Wait for SIGINT/SIGTERM or a fatal listener error. stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) select { case err := <-serverErr: log.Fatalf("MWSE engine failed: %v", err) case sig := <-stop: log.Printf("received %s, shutting down gracefully", sig) } // Graceful shutdown: stop accepting new HTTP work, then close every live // WebSocket connection so peers receive a clean close frame. ctx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Printf("http shutdown error: %v", err) } hub.CloseAll() log.Printf("MWSE engine stopped") }