From a5749e76d8349da3da662f9fbafc6110ff63ceb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Blondel?= Date: Tue, 20 Apr 2021 16:44:19 +0200 Subject: [PATCH 1/4] Fix autoreconnect (#84) The disconnected event was not thrown in case of a manual disconnect. Also the auto reconnection logic was not working. I moved the logic of the reconnection from the abstract protocol class to the actual websocket protocol and now call the reconnected event. --- lib/src/kuzzle.dart | 13 +++---- lib/src/protocols/abstract.dart | 36 +++++-------------- lib/src/protocols/websocket_browser.dart | 6 ++-- lib/src/protocols/websocket_io.dart | 46 ++++++++++++++++++++---- 4 files changed, 54 insertions(+), 47 deletions(-) diff --git a/lib/src/kuzzle.dart b/lib/src/kuzzle.dart index 4a3e7949..c1c8db50 100644 --- a/lib/src/kuzzle.dart +++ b/lib/src/kuzzle.dart @@ -51,8 +51,8 @@ class Kuzzle extends KuzzleEventEmitter { } globalVolatile ??= {}; - queueTTL ??= Duration(minutes: 2); - replayInterval ??= Duration(milliseconds: 10); + queueTTL ??= const Duration(minutes: 2); + replayInterval ??= const Duration(milliseconds: 10); server = ServerController(this); bulk = BulkController(this); @@ -153,12 +153,7 @@ class Kuzzle extends KuzzleEventEmitter { /// Common volatile data, will be sent to all future requests Map globalVolatile; - final List _requests = List(); - - bool get autoReconnect => protocol.autoReconnect; - set autoReconnect(bool value) { - protocol.autoReconnect = value; - } + final List _requests = []; final Map _controllers = {}; @@ -174,7 +169,7 @@ class Kuzzle extends KuzzleEventEmitter { if (protocol.state == KuzzleProtocolState.connecting) { final completer = Completer(); - // todo: handle reconnect event + protocol.once(ProtocolEvents.RECONNECT, completer.complete); protocol.once(ProtocolEvents.CONNECT, completer.complete); return completer.future; diff --git a/lib/src/protocols/abstract.dart b/lib/src/protocols/abstract.dart index af871e90..ea18e1ac 100644 --- a/lib/src/protocols/abstract.dart +++ b/lib/src/protocols/abstract.dart @@ -20,24 +20,16 @@ enum KuzzleProtocolState { abstract class KuzzleProtocol extends KuzzleEventEmitter { KuzzleProtocol( - this.uri, { - this.autoReconnect = true, - Duration reconnectionDelay, - }) : _state = KuzzleProtocolState.offline, - _reconnectionDelay = reconnectionDelay ?? Duration(seconds: 1), - id = _uuid.v4(); - - bool autoReconnect; + this.uri, + ) : _state = KuzzleProtocolState.offline, + id = _uuid.v4(); + final Uri uri; final String id; - final Duration _reconnectionDelay; @protected bool wasConnected = false; - @protected - bool stopRetryingToConnect = false; - @protected bool retrying = false; @@ -59,7 +51,6 @@ abstract class KuzzleProtocol extends KuzzleEventEmitter { KuzzleProtocolState state = KuzzleProtocolState.connected, }) { _state = state; - stopRetryingToConnect = false; emit(wasConnected ? ProtocolEvents.RECONNECT : ProtocolEvents.CONNECT); wasConnected = true; @@ -74,27 +65,16 @@ abstract class KuzzleProtocol extends KuzzleEventEmitter { void clientNetworkError([dynamic error]) { _state = KuzzleProtocolState.offline; - emit(ProtocolEvents.NETWORK_ERROR, - [ - KuzzleError('Unable to connect to kuzzle server at ${uri.toString()}') - ]); - - if (autoReconnect && !retrying && !stopRetryingToConnect) { - retrying = true; - - Timer(_reconnectionDelay, () async { - retrying = false; - await connect().catchError(clientNetworkError); - }); - } else { - emit(ProtocolEvents.DISCONNECT); - } + emit(ProtocolEvents.NETWORK_ERROR, [ + KuzzleError('Unable to connect to kuzzle server at ${uri.toString()}') + ]); } /// Called when the client's connection is closed @mustCallSuper void close() { _state = KuzzleProtocolState.offline; + clientDisconnected(); } // todo: implement query options diff --git a/lib/src/protocols/websocket_browser.dart b/lib/src/protocols/websocket_browser.dart index 07a6d1e5..b4da8cea 100644 --- a/lib/src/protocols/websocket_browser.dart +++ b/lib/src/protocols/websocket_browser.dart @@ -15,8 +15,9 @@ class KuzzleWebSocket extends KuzzleProtocol { bool autoReconnect = true, Duration reconnectionDelay, this.pingInterval, - }) : super(uri, - autoReconnect: autoReconnect, reconnectionDelay: reconnectionDelay); + }) : super( + uri, + ); String _lastUrl; WebSocket _webSocket; @@ -64,7 +65,6 @@ class KuzzleWebSocket extends KuzzleProtocol { super.close(); removeAllListeners(); - stopRetryingToConnect = true; wasConnected = false; _subscription?.cancel(); diff --git a/lib/src/protocols/websocket_io.dart b/lib/src/protocols/websocket_io.dart index 9bf35d57..cf8ad174 100644 --- a/lib/src/protocols/websocket_io.dart +++ b/lib/src/protocols/websocket_io.dart @@ -13,20 +13,32 @@ import 'events.dart'; class KuzzleWebSocket extends KuzzleProtocol { KuzzleWebSocket( Uri uri, { - bool autoReconnect = true, + this.autoReconnect = true, Duration reconnectionDelay, this.pingInterval, - }) : - super(uri, - autoReconnect: autoReconnect, reconnectionDelay: reconnectionDelay); + }) : super(uri) { + _reconnectionDelay = reconnectionDelay ?? Duration(seconds: 1); + } String _lastUrl; WebSocket _webSocket; StreamSubscription _subscription; Duration pingInterval; + Duration _reconnectionDelay; + bool autoReconnect; + bool _stopRetryingToConnect = false; + bool _hasBeenClosed = false; @override Future connect() async { + // If a reconnection is going on + // and the enduser called disconnect in between + // then don't try to connect + if (_hasBeenClosed && retrying) { + return; + } + _hasBeenClosed = false; + final url = '${uri.scheme}://${uri.host}:${uri.port}'; await super.connect(); @@ -42,12 +54,15 @@ class KuzzleWebSocket extends KuzzleProtocol { await _webSocket?.close(); _webSocket = null; + _stopRetryingToConnect = false; + try { _webSocket = await WebSocket.connect(url); } on IOException { - if (wasConnected) { + if (wasConnected || autoReconnect) { clientNetworkError( KuzzleError('WebSocketProtocol: Unable to connect to $url')); + _handleAutoReconnect(); return; } @@ -80,8 +95,10 @@ class KuzzleWebSocket extends KuzzleProtocol { void close() { super.close(); + _hasBeenClosed = true; + removeAllListeners(); - stopRetryingToConnect = true; + _stopRetryingToConnect = true; wasConnected = false; _subscription?.cancel(); @@ -103,12 +120,27 @@ class KuzzleWebSocket extends KuzzleProtocol { } } + void _handleAutoReconnect() { + if (autoReconnect && !retrying && !_stopRetryingToConnect) { + retrying = true; + + Timer(_reconnectionDelay, () async { + await connect().catchError(clientNetworkError); + retrying = false; + }); + } else { + emit(ProtocolEvents.DISCONNECT); + } + } + void _handleDone() { if (_webSocket.closeCode == 1000) { clientDisconnected(); - } else if (wasConnected) { + } else if (wasConnected || autoReconnect) { + clientDisconnected(); clientNetworkError(KuzzleError( 'clientNetworkError', _webSocket.closeReason, _webSocket.closeCode)); + _handleAutoReconnect(); } } } From dd93e650276d9ba0bfff770e5a1436efea349fb3 Mon Sep 17 00:00:00 2001 From: Yoann Abbes Date: Tue, 20 Apr 2021 17:50:58 +0200 Subject: [PATCH 2/4] Release 2.4.2 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 284fdc2f..03d7b6fd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: kuzzle -version: 2.4.1 +version: 2.4.2 description: A library to interact with kuzzle API. A backend software, self-hostable and ready to use to power modern cross-platform apps. homepage: https://github.com/kuzzleio/sdk-dart From f31bfcbb2ab5be417eab60ab4f59e592dc926d8d Mon Sep 17 00:00:00 2001 From: Yoann Abbes Date: Tue, 20 Apr 2021 17:54:13 +0200 Subject: [PATCH 3/4] update --- CHANGELOG.md | 4 +++ pubspec.lock | 97 ++++++++++++++++++++-------------------------------- 2 files changed, 42 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc9bc59b..8a5fc746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [2.4.2] + +- Fix auto-reconnect + ## [2.4.1] - Decode http response with utf8 diff --git a/pubspec.lock b/pubspec.lock index fbfd4297..8bd6bcdd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,7 +14,7 @@ packages: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.41.1" + version: "0.41.2" args: dependency: transitive description: @@ -28,35 +28,35 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.2" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "1.2.0" cli_util: dependency: transitive description: name: cli_util url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.3.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.13" + version: "1.15.0" convert: dependency: transitive description: @@ -84,14 +84,14 @@ packages: name: file url: "https://pub.dartlang.org" source: hosted - version: "5.2.1" + version: "6.1.0" glob: dependency: transitive description: name: glob url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.1" http: dependency: "direct main" description: @@ -105,7 +105,7 @@ packages: name: http_multi_server url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "3.0.1" http_parser: dependency: transitive description: @@ -113,27 +113,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.4" - intl: - dependency: transitive - description: - name: intl - url: "https://pub.dartlang.org" - source: hosted - version: "0.16.1" io: dependency: transitive description: name: io url: "https://pub.dartlang.org" source: hosted - version: "0.3.4" + version: "1.0.0" js: dependency: transitive description: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.3" logging: dependency: transitive description: @@ -147,42 +140,28 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.9" + version: "0.12.10" meta: dependency: "direct main" description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.2.4" + version: "1.3.0" mime: dependency: transitive description: name: mime url: "https://pub.dartlang.org" source: hosted - version: "0.9.7" - node_interop: - dependency: transitive - description: - name: node_interop - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.1" - node_io: - dependency: transitive - description: - name: node_io - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" + version: "1.0.0" node_preamble: dependency: transitive description: name: node_preamble url: "https://pub.dartlang.org" source: hosted - version: "1.4.12" + version: "1.4.13" package_config: dependency: transitive description: @@ -196,28 +175,28 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" pedantic: dependency: "direct main" description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.9.2" + version: "1.11.0" pool: dependency: transitive description: name: pool url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.5.0" pub_semver: dependency: transitive description: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "1.4.4" + version: "2.0.0" shelf: dependency: transitive description: @@ -231,98 +210,98 @@ packages: name: shelf_packages_handler url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" shelf_static: dependency: transitive description: name: shelf_static url: "https://pub.dartlang.org" source: hosted - version: "0.2.8" + version: "0.2.9+2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket url: "https://pub.dartlang.org" source: hosted - version: "0.2.3" + version: "0.2.4+1" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" source_maps: dependency: transitive description: name: source_maps url: "https://pub.dartlang.org" source: hosted - version: "0.10.9" + version: "0.10.10" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.1" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.6" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" test: dependency: "direct dev" description: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.15.7" + version: "1.16.5" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.18+1" + version: "0.2.19" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.3.11+4" + version: "0.3.15" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" uuid: dependency: "direct main" description: @@ -343,27 +322,27 @@ packages: name: watcher url: "https://pub.dartlang.org" source: hosted - version: "0.9.7+15" + version: "1.0.0" web_socket_channel: dependency: transitive description: name: web_socket_channel url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol url: "https://pub.dartlang.org" source: hosted - version: "0.7.4" + version: "0.7.5" yaml: dependency: transitive description: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "3.1.0" sdks: - dart: ">=2.10.0 <3.0.0" + dart: ">=2.12.0 <3.0.0" From f0cfdf235e3361be0d7e85ee4b291650294aa254 Mon Sep 17 00:00:00 2001 From: Yoann Abbes Date: Wed, 21 Apr 2021 17:13:25 +0200 Subject: [PATCH 4/4] update CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a5fc746..31e6198d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## [2.4.2] -- Fix auto-reconnect +- Fix autoreconnect + +- Print deprecation warning from Kuzzle ## [2.4.1]