ws.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package main
  2. import (
  3. "net/http"
  4. "sync"
  5. "time"
  6. "github.com/gin-gonic/gin"
  7. "github.com/gorilla/websocket"
  8. )
  9. // wsClient wraps a websocket connection with synchronized writes.
  10. type wsClient struct {
  11. conn *websocket.Conn
  12. mu sync.Mutex
  13. }
  14. // wsHub tracks active websocket clients and broadcasts updates to them.
  15. type wsHub struct {
  16. mu sync.Mutex
  17. clients map[*wsClient]struct{}
  18. }
  19. // wsEnvelope is the websocket payload shape used for live service updates.
  20. type wsEnvelope struct {
  21. Type string `json:"type"`
  22. GeneratedAt time.Time `json:"generated_at"`
  23. Service *serviceStatus `json:"service,omitempty"`
  24. }
  25. var upgrader = websocket.Upgrader{
  26. CheckOrigin: func(r *http.Request) bool { return true },
  27. }
  28. // writeJSON writes a JSON payload to the websocket connection safely.
  29. func (c *wsClient) writeJSON(payload any) error {
  30. c.mu.Lock()
  31. defer c.mu.Unlock()
  32. return c.conn.WriteJSON(payload)
  33. }
  34. // newWSHub creates a websocket hub for tracking connected clients.
  35. func newWSHub() *wsHub {
  36. return &wsHub{clients: make(map[*wsClient]struct{})}
  37. }
  38. // add registers a websocket client with the hub.
  39. func (h *wsHub) add(client *wsClient) {
  40. h.mu.Lock()
  41. defer h.mu.Unlock()
  42. h.clients[client] = struct{}{}
  43. }
  44. // remove unregisters a websocket client and closes its connection.
  45. func (h *wsHub) remove(client *wsClient) {
  46. h.mu.Lock()
  47. defer h.mu.Unlock()
  48. if _, ok := h.clients[client]; ok {
  49. delete(h.clients, client)
  50. _ = client.conn.Close()
  51. }
  52. }
  53. // broadcast sends a payload to all connected websocket clients.
  54. func (h *wsHub) broadcast(payload any) {
  55. h.mu.Lock()
  56. clients := make([]*wsClient, 0, len(h.clients))
  57. for client := range h.clients {
  58. clients = append(clients, client)
  59. }
  60. h.mu.Unlock()
  61. for _, client := range clients {
  62. if err := client.writeJSON(payload); err != nil {
  63. h.remove(client)
  64. }
  65. }
  66. }
  67. // serveWebSocket upgrades an HTTP request to a websocket and tracks the client.
  68. func serveWebSocket(c *gin.Context, hub *wsHub) {
  69. conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
  70. if err != nil {
  71. return
  72. }
  73. client := &wsClient{conn: conn}
  74. hub.add(client)
  75. go func() {
  76. defer hub.remove(client)
  77. for {
  78. if _, _, err := conn.ReadMessage(); err != nil {
  79. return
  80. }
  81. }
  82. }()
  83. }