Skip to content

Commit

Permalink
Connection and disconnection
Browse files Browse the repository at this point in the history
  • Loading branch information
PlugFox committed Jul 16, 2023
1 parent 56fcd75 commit c960137
Show file tree
Hide file tree
Showing 18 changed files with 1,186 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ coverage*

# Codegen
*.g.dart
!pubspec.yaml.g.dart

# Logs
l/
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ check: analyze

pana: check

generate:
generate: get
@dart pub global activate protoc_plugin
@protoc --proto_path=lib/src/model/protobuf --dart_out=lib/src/model/protobuf lib/src/model/protobuf/client.proto
@dart format -l 80 lib/src/model/protobuf/
@dart run build_runner build --delete-conflicting-outputs
@dart format -l 80 lib/src/model/pubspec.yaml.g.dart lib/src/model/protobuf/
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ Windows:
```ps1
$ choco install protoc
$ dart pub global activate protoc_plugin
$ dart pub get
$ protoc --proto_path=lib/src/model/protobuf --dart_out=lib/src/model/protobuf lib/src/model/protobuf/client.proto
$ dart format -l 80 lib/src/model/protobuf/
$ dart run build_runner build --delete-conflicting-outputs
$ dart format -l 80 lib/src/model/pubspec.yaml.g.dart lib/src/model/protobuf/
```

Linux:
Expand All @@ -29,8 +31,10 @@ $ sudo apt update
$ sudo apt install -y protobuf-compiler dart
$ export PATH="$PATH":"$HOME/.pub-cache/bin"
$ dart pub global activate protoc_plugin
$ dart pub get
$ protoc --proto_path=lib/src/model/protobuf --dart_out=lib/src/model/protobuf lib/src/model/protobuf/client.proto
$ dart format -l 80 lib/src/model/protobuf/
$ dart run build_runner build --delete-conflicting-outputs
$ dart format -l 80 lib/src/model/pubspec.yaml.g.dart lib/src/model/protobuf/
```

macOS:
Expand All @@ -40,8 +44,10 @@ $ brew update
$ brew install protobuf dart
$ export PATH="$PATH":"$HOME/.pub-cache/bin"
$ dart pub global activate protoc_plugin
$ dart pub get
$ protoc --proto_path=lib/src/model/protobuf --dart_out=lib/src/model/protobuf lib/src/model/protobuf/client.proto
$ dart format -l 80 lib/src/model/protobuf/
$ dart run build_runner build --delete-conflicting-outputs
$ dart format -l 80 lib/src/model/pubspec.yaml.g.dart lib/src/model/protobuf/
```

## Features and Roadmap
Expand Down
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ analyzer:
# Codegen
- "lib/**.g.dart"
- "lib/src/model/protobuf/*"
- "lib/src/model/pubspec.yaml.g.dart"
# Tests
- "test/**.mocks.dart"
- ".test_coverage.dart"
Expand Down
11 changes: 11 additions & 0 deletions build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Read about `build.yaml` at https://pub.dev/packages/build_config
targets:
$default:
sources:
- $package$
- lib/**
- pubspec.yaml
builders:
pubspec_generator:
options:
output: lib/src/model/pubspec.yaml.g.dart
4 changes: 4 additions & 0 deletions lib/centrifuge.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
library dev.plugfox.centrifuge;

export 'src/client/centrifuge.dart' show Centrifuge;
export 'src/model/config.dart';
export 'src/model/exception.dart';
export 'src/model/jwt.dart';
export 'src/model/state.dart';
4 changes: 4 additions & 0 deletions lib/interface.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
library dev.plugfox.centrifuge.interface;

export 'src/client/centrifuge_interface.dart';
export 'src/model/config.dart';
export 'src/model/exception.dart';
export 'src/model/jwt.dart';
export 'src/model/state.dart';
134 changes: 134 additions & 0 deletions lib/src/client/centrifuge.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import 'dart:async';

import 'package:centrifuge_dart/src/client/centrifuge_interface.dart';
import 'package:centrifuge_dart/src/model/config.dart';
import 'package:centrifuge_dart/src/model/exception.dart';
import 'package:centrifuge_dart/src/model/state.dart';
import 'package:meta/meta.dart';
import 'package:ws/ws.dart';

/// {@template centrifuge}
/// Centrifuge client.
/// {@endtemplate}
final class Centrifuge = CentrifugeBase with CentrifugeConnectionMixin;

/// {@nodoc}
@internal
abstract base class CentrifugeBase implements ICentrifuge {
/// {@nodoc}
CentrifugeBase([CentrifugeConfig? config])
: _stateController = StreamController<CentrifugeState>.broadcast(),
_webSocket = WebSocketClient(
reconnectTimeout: Duration.zero,
protocols: _$protocolsCentrifugeProtobuf,
),
_state = CentrifugeState$Disconnected(),
_config = config ?? CentrifugeConfig.defaultConfig() {
_initCentrifuge();
}

/// Protocols for websocket.
/// {@nodoc}
static const List<String> _$protocolsCentrifugeProtobuf = <String>[
'centrifuge-protobuf'
];

/// State controller.
/// {@nodoc}
final StreamController<CentrifugeState> _stateController;

/// Websocket client.
/// {@nodoc}
final WebSocketClient _webSocket;

@override
CentrifugeState get state => _state;

/// Current state of client.
/// {@nodoc}
CentrifugeState _state;

@override
late Stream<CentrifugeState> states = _stateController.stream;

/// Centrifuge config.
/// {@nodoc}
final CentrifugeConfig _config;

/// Init centrifuge client, override this method to add custom logic.
/// This method is called in constructor.
/// {@nodoc}
@protected
@mustCallSuper
void _initCentrifuge() {}

/// {@nodoc}
@protected
@nonVirtual
void _setState(CentrifugeState state) {
if (_state.type == state.type) return;
_stateController.add(_state = state);
}

@override
@mustCallSuper
Future<void> close() async {}
}

/// Mixin responsible for connection.
/// {@nodoc}
@internal
base mixin CentrifugeConnectionMixin on CentrifugeBase {
StreamSubscription<WebSocketClientState>? _webSocketStateSubscription;

@override
void _initCentrifuge() {
_webSocketStateSubscription = _webSocket.stateChanges.listen((state) {
switch (state) {
case WebSocketClientState$Connecting state:
_setState(CentrifugeState$Connecting(url: state.url));
case WebSocketClientState$Open _:
_setState(CentrifugeState$Connected(url: state.url));
case WebSocketClientState$Disconnecting _:
case WebSocketClientState$Closed _:
_setState(CentrifugeState$Disconnected());
}
});
super._initCentrifuge();
}

@override
Future<void> connect(String url) async {
_setState(CentrifugeState$Connecting(url: url));
try {
await _webSocket.connect(url);
} on Object catch (error, stackTrace) {
_setState(CentrifugeState$Disconnected());
Error.throwWithStackTrace(
CentrifugoConnectionException(error),
stackTrace,
);
}
}

@override
Future<void> disconnect() async {
try {
await _webSocket.disconnect();
} on Object catch (error, stackTrace) {
_setState(CentrifugeState$Disconnected());
Error.throwWithStackTrace(
CentrifugoConnectionException(error),
stackTrace,
);
}
}

@override
@mustCallSuper
Future<void> close() async {
await super.close();
await _webSocket.close();
await _webSocketStateSubscription?.cancel();
}
}
28 changes: 28 additions & 0 deletions lib/src/client/centrifuge_interface.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:centrifuge_dart/src/model/state.dart';

/// Centrifuge client interface.
abstract interface class ICentrifuge {
/// State of client.
CentrifugeState get state;

/// Stream of client states.
abstract final Stream<CentrifugeState> states;

/// Connect to the server.
/// [url] is a URL of endpoint.
Future<void> connect(String url);

/// Disconnect from the server.
Future<void> disconnect();

/// Client if not needed anymore.
/// Permanent close connection to the server and
/// free all allocated resources.
Future<void> close();

/// Send asynchronous message to the server.
/* Future<void> send( data); */

/// Send arbitrary RPC and wait for response.
/* Future<void> rpc(String method, data); */
}
1 change: 0 additions & 1 deletion lib/src/client/client_interface.dart

This file was deleted.

61 changes: 61 additions & 0 deletions lib/src/model/config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:centrifuge_dart/src/model/pubspec.yaml.g.dart';
import 'package:meta/meta.dart';

/// Token used for authentication
//typedef CentrifugeToken = String;

/// Callback to get/refresh tokens
//typedef CentrifugeTokenCallback = Future<CentrifugeToken> Function();

/// {@template centrifuge_config}
/// Centrifuge client common options.
///
/// There are several common options available when creating Client instance.
/// {@endtemplate}
@immutable
final class CentrifugeConfig {
/// {@macro centrifuge_config}
CentrifugeConfig({
this.data,
({Duration min, Duration max})? reconnectDelay,
({String name, String version})? client,
}) : reconnectDelay = reconnectDelay ??
(
min: const Duration(milliseconds: 500),
max: const Duration(seconds: 20),
),
client = client ??
(
name: Pubspec.name,
version: Pubspec.version.canonical,
);

/// Create a default config
///
/// {@macro centrifuge_config}
factory CentrifugeConfig.defaultConfig() = CentrifugeConfig;

/// The initial token used for authentication
//CentrifugeToken token;

/// Callback to get/refresh tokens
//final CentrifugeTokenCallback? getToken;

/// The connection timeout
//final Duration timeout;

/// Native WebSocket config
/// sealed class WebSocketConfig => WebSocketConfig$VM | WebSocketConfig$JS
/// The data send for the first request
final List<int>? data;

/// Reconnect backoff algorithm minimum/maximum delay.
final ({Duration min, Duration max}) reconnectDelay;

/// The user's client name and version.
final ({String name, String version}) client;

@override
String toString() => 'CentrifugeConfig{}';
}
43 changes: 43 additions & 0 deletions lib/src/model/exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:meta/meta.dart';

/// {@template exception}
/// Centrifugo exception.
/// {@endtemplate}
@immutable
sealed class CentrifugoException {
/// {@macro exception}
const CentrifugoException(
this.code,
this.message, [
this.error,
]);

/// Error code.
final String code;

/// Error message.
final String message;

/// Source error of exception if exists.
final Object? error;

@override
int get hashCode => code.hashCode;

@override
bool operator ==(Object other) => identical(this, other);

@override
String toString() => 'CentrifugoException{code: $code}';
}

/// {@macro exception}
final class CentrifugoConnectionException extends CentrifugoException {
/// {@macro exception}
const CentrifugoConnectionException([Object? error])
: super(
'centrifugo_connection_exception',
'Connection problem',
error,
);
}
3 changes: 3 additions & 0 deletions lib/src/model/jwt.dart
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,9 @@ final class _CentrifugoJWTImpl extends CentrifugoJWT {
// Return JWT
return '$encodedHeader.$encodedPayload.$encodedSignature';
}

@override
String toString() => 'CentrifugoJWT{sub: $sub}';
}

/// A converter that converts Base64-encoded strings
Expand Down
Loading

0 comments on commit c960137

Please sign in to comment.