From 64b8b9689d089c056e1f1665df749aa21b893aad Mon Sep 17 00:00:00 2001 From: Tyler <18113850+dshukertjr@users.noreply.github.com> Date: Sun, 10 Sep 2023 22:52:01 +0900 Subject: [PATCH] fix(realtime_client): No exception is thrown when connection is closed. (#620) * fix: realtime throwing error when connection is closed * fix: adjust test case to align with js client --- .../lib/src/realtime_channel.dart | 2 +- .../lib/src/realtime_client.dart | 28 +++++++++++++++++-- packages/realtime_client/test/mock_test.dart | 4 +-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/realtime_client/lib/src/realtime_channel.dart b/packages/realtime_client/lib/src/realtime_channel.dart index 0557b7fa..dfe9bb72 100644 --- a/packages/realtime_client/lib/src/realtime_channel.dart +++ b/packages/realtime_client/lib/src/realtime_channel.dart @@ -328,7 +328,7 @@ class RealtimeChannel { /// Registers a callback that will be executed when the channel encounteres an error. void onError(void Function(String?) callback) { onEvents(ChannelEvents.error.eventName(), ChannelFilter(), - (reason, [ref]) => callback(reason.toString())); + (reason, [ref]) => callback(reason?.toString())); } RealtimeChannel on( diff --git a/packages/realtime_client/lib/src/realtime_client.dart b/packages/realtime_client/lib/src/realtime_client.dart index c85b8299..5b85a78c 100644 --- a/packages/realtime_client/lib/src/realtime_client.dart +++ b/packages/realtime_client/lib/src/realtime_client.dart @@ -26,6 +26,26 @@ typedef RealtimeDecode = void Function( void Function(dynamic result) callback, ); +/// Event details for when the connection closed. +class RealtimeCloseEvent { + /// Web socket protocol status codes for when a connection is closed. + /// + /// The full list can be found at the following: + /// + /// https://datatracker.ietf.org/doc/html/rfc6455#section-7.4 + final int code; + + /// Connection closed reason sent from the server + /// + /// https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6 + final String? reason; + + const RealtimeCloseEvent({ + required this.code, + required this.reason, + }); +} + class RealtimeClient { String? accessToken; List channels = []; @@ -387,13 +407,17 @@ class RealtimeClient { /// communication has been closed void _onConnClose() { - final event = conn?.closeReason ?? ''; + final statusCode = conn?.closeCode; + RealtimeCloseEvent? event; + if (statusCode != null) { + event = RealtimeCloseEvent(code: statusCode, reason: conn?.closeReason); + } log('transport', 'close', event); /// SocketStates.disconnected: by user with socket.disconnect() /// SocketStates.closed: NOT by user, should try to reconnect if (connState == SocketStates.closed) { - _triggerChanError(event); + _triggerChanError(); reconnectTimer.scheduleTimeout(); } if (heartbeatTimer != null) heartbeatTimer!.cancel(); diff --git a/packages/realtime_client/test/mock_test.dart b/packages/realtime_client/test/mock_test.dart index ecde69df..64272e70 100644 --- a/packages/realtime_client/test/mock_test.dart +++ b/packages/realtime_client/test/mock_test.dart @@ -289,9 +289,9 @@ void main() { }); test("correct CHANNEL_ERROR data on heartbeat timeout", () async { - final subscribeCallback = expectAsync2((event, [data]) { + final subscribeCallback = expectAsync2((event, [error]) { if (event == "CHANNEL_ERROR") { - expect(data, "heartbeat timeout"); + expect(error, isNull); } else { expect(event, "CLOSED"); }