readLoop blocks until the client disconnects or sends a close frame. It ignores client messages and sends periodic pings.
(conn *websocket.Conn, agent *identity.AgentIdentity)
| 195 | // readLoop blocks until the client disconnects or sends a close frame. |
| 196 | // It ignores client messages and sends periodic pings. |
| 197 | func (h *Handler) readLoop(conn *websocket.Conn, agent *identity.AgentIdentity) { |
| 198 | ctx, cancel := context.WithCancel(context.Background()) |
| 199 | defer cancel() |
| 200 | |
| 201 | // Ping goroutine: send a ping every 30 seconds, fail if no pong within 10 seconds. |
| 202 | go func() { |
| 203 | ticker := time.NewTicker(30 * time.Second) |
| 204 | defer ticker.Stop() |
| 205 | for { |
| 206 | select { |
| 207 | case <-ctx.Done(): |
| 208 | return |
| 209 | case <-ticker.C: |
| 210 | pingCtx, pingCancel := context.WithTimeout(ctx, 10*time.Second) |
| 211 | err := conn.Ping(pingCtx) |
| 212 | pingCancel() |
| 213 | if err != nil { |
| 214 | log.Printf("[ws] ping failed for %s: %v", agent.Email, err) |
| 215 | // Close the connection to unblock the Read below |
| 216 | conn.Close(websocket.StatusGoingAway, "ping timeout") |
| 217 | return |
| 218 | } |
| 219 | } |
| 220 | } |
| 221 | }() |
| 222 | |
| 223 | // Read loop: consume client messages and control frames until disconnect. |
| 224 | for { |
| 225 | _, _, err := conn.Read(ctx) |
| 226 | if err != nil { |
| 227 | return |
| 228 | } |
| 229 | } |
| 230 | } |