Relates to CODAGT-115
Adds metric `coderd_api_websocket_probes_total`. Every successful
heartbeat for a given path will increment the metric.
Comparing this with `coderd_api_concurrent_websockets` will give an
indication of how many websocket connections are open but in a 'wedged'
state (when heartbeats stopped versus when we closed the connection).
The `websocketPair` test helper was not calling `CloseRead` on either
side of the connection. Without `CloseRead`, the websocket library does
not process control frames (ping/pong), so the heartbeat tests were
passing only because no pings had yet failed, not because pings were
actually succeeding.
Add `CloseRead` on both the client and server connections so that pong
frames are delivered in response to pings.
Split out from #25012.
> 🤖 Generated with [Coder Agents](https://coder.com)
- `coderd/httpapi/websocket.go`: add `net.ErrClosed` +
`websocket.CloseStatus` checks; extract `heartbeatCloseWith` with
`quartz.Clock` parameter for testability
- `coderd/httpapi/websocket_internal_test.go`: new test file