Context
RawWebSocketClientImpl (lib/core/network/realtime/raw_websocket_client.dart,
untouched since its introducing commit 302490b) is the implementation the
provider doc tells adopters to inject for production
(lib/core/network/realtime/realtime_providers.dart:6-7: "Override this
provider to inject RawWebSocketClientImpl for production").
connect() (raw_websocket_client.dart:21-45) assigns
WebSocketChannel.connect(...) and returns. WebSocketChannel.connect
never throws for a network failure - the failure is reported through the
channel's ready future, which nothing awaits or handles. Two consequences,
both verified by running the class against a port that refuses connections:
$ dart --packages=.dart_tool/package_config.json scratchpad/ws_probe.dart
connect() returned normally: true
isConnected right after connect: true
Unhandled exception:
WebSocketChannelException: SocketException: Connection refused (OS Error: Connection refused, errno = 61), address = 127.0.0.1, port = 52118
(probe: await client.connect('ws://127.0.0.1:1'); print(client.isConnected);)
connect() reports success and isConnected reports true for a
connection that never opened (isConnected is just _channel != null,
:18). Calls to send() are then silently buffered into a dead sink.
- The connection failure escapes as an unhandled async exception from
the unawaited ready future. The try/catch around
WebSocketChannel.connect at :28-44 can never catch it (the
constructor does not throw for network errors), and the stream onError
handler at :31-35 does not cover the ready future.
The existing test file
(test/core/network/realtime/raw_websocket_client_test.dart) never
exercises a connection attempt, so nothing guards this. An adopter who
follows the provider doc inherits a realtime client whose failure mode on an
unreachable server is a lying isConnected plus an unhandled exception.
No prior issue covers this surface (gh issue list --state all --search "websocket" and --search "realtime" return nothing about it).
Acceptance criteria
All driveable with flutter test on the host VM using dart:io sockets -
no device, no backend.
- A test that calls
connect() against a port that refuses connections
(bind a ServerSocket on port 0, note the port, close it, connect)
observes a defined, documented failure: either connect() completes
with an error, or the error is delivered on stream - and in both cases
no unhandled async error escapes (assert via runZonedGuarded or by the
test simply not failing on an uncaught exception).
- After that failed connect,
isConnected is false.
- A companion happy-path test using an in-process server
(HttpServer + WebSocketTransformer) shows connect() completing,
isConnected true, a send() arriving at the server, and messages
from the server arriving on stream - proving the fix did not break the
working path.
IRealtimeClient.connect's doc comment
(lib/core/network/realtime/i_realtime_client.dart) states the error
contract implementations must meet.
Context
RawWebSocketClientImpl(lib/core/network/realtime/raw_websocket_client.dart,untouched since its introducing commit
302490b) is the implementation theprovider doc tells adopters to inject for production
(
lib/core/network/realtime/realtime_providers.dart:6-7: "Override thisprovider to inject
RawWebSocketClientImplfor production").connect()(raw_websocket_client.dart:21-45) assignsWebSocketChannel.connect(...)and returns.WebSocketChannel.connectnever throws for a network failure - the failure is reported through the
channel's
readyfuture, which nothing awaits or handles. Two consequences,both verified by running the class against a port that refuses connections:
(probe:
await client.connect('ws://127.0.0.1:1'); print(client.isConnected);)connect()reports success andisConnectedreportstruefor aconnection that never opened (
isConnectedis just_channel != null,:18). Calls tosend()are then silently buffered into a dead sink.the unawaited
readyfuture. Thetry/catcharoundWebSocketChannel.connectat:28-44can never catch it (theconstructor does not throw for network errors), and the stream
onErrorhandler at
:31-35does not cover thereadyfuture.The existing test file
(
test/core/network/realtime/raw_websocket_client_test.dart) neverexercises a connection attempt, so nothing guards this. An adopter who
follows the provider doc inherits a realtime client whose failure mode on an
unreachable server is a lying
isConnectedplus an unhandled exception.No prior issue covers this surface (
gh issue list --state all --search "websocket"and--search "realtime"return nothing about it).Acceptance criteria
All driveable with
flutter teston the host VM usingdart:iosockets -no device, no backend.
connect()against a port that refuses connections(bind a
ServerSocketon port 0, note the port, close it, connect)observes a defined, documented failure: either
connect()completeswith an error, or the error is delivered on
stream- and in both casesno unhandled async error escapes (assert via
runZonedGuardedor by thetest simply not failing on an uncaught exception).
isConnectedisfalse.(
HttpServer+WebSocketTransformer) showsconnect()completing,isConnectedtrue, asend()arriving at the server, and messagesfrom the server arriving on
stream- proving the fix did not break theworking path.
IRealtimeClient.connect's doc comment(
lib/core/network/realtime/i_realtime_client.dart) states the errorcontract implementations must meet.