Commit db754cf
authored
fix(realtime_client): detect dropped connections on iOS via WebSocket ping (#1451)
## What
Enable `pingInterval` on the native (`dart:io`) WebSocket so dropped
connections are detected consistently across platforms.
## Why
Fixes #1071. When the internet drops, Android emits a
`RealtimeSubscribeException` (close code 1002) but iOS emits nothing,
leaving the stream silently stale.
**Root cause:** `createWebSocketClient` created the native WebSocket
without a `pingInterval`, so a *silently* dropped connection was never
detected at the socket layer:
- On **Android**, the OS surfaces the dead TCP connection promptly (RST
→ close code 1002), so `onDone`/`onError` fires and `_triggerChanError`
propagates a `channelError`.
- On **iOS**, the OS buffers writes instead of surfacing a reset. The
only fallback was the app-level Phoenix heartbeat, whose recovery path
calls `conn.sink.close()`, which does not reliably fire `onDone` on a
dead iOS socket. So nothing propagated to the channel.
## How
Set `pingInterval` (new `Constants.defaultWebSocketPingInterval`, 25s,
aligned with the existing heartbeat cadence) on the native WebSocket.
`dart:io` now actively probes the peer and closes the connection
(`goingAway`) when a pong is not received, which flows through the same
`onDone → _onConnClose → _triggerChanError → reconnect` path Android
already uses.
## Scope / safety
- Only the default native transport changes. Web (`websocket_web.dart`)
is untouched, since browsers manage ping/pong internally.
- Custom transports injected via `RealtimeClientOptions.transport` are
unaffected.
- No public API change: the `WebSocketTransport` typedef is unchanged.
- A 25s pong window is generous for mobile round-trips.
## Testing
- `dart analyze` clean on the changed files.
- Realtime unit suite passes (`socket_test`, `mock_test`,
`channel_test`, including the existing
CHANNEL_ERROR-on-heartbeat-timeout test).
- Files formatted.
No unit test added: `pingInterval` is a property of the underlying
`dart:io` socket and is not observable through `IOWebSocketChannel`
without a live connection, and the codebase does not test the default
transport (tests inject mocks).1 parent c7b2598 commit db754cf
2 files changed
Lines changed: 79 additions & 3 deletions
Lines changed: 17 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
4 | 13 | | |
5 | 14 | | |
6 | | - | |
7 | | - | |
8 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
9 | 23 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
0 commit comments