fix(lib): add message checks and improve exceptions

This commit is contained in:
ksenia312
2024-02-01 12:06:16 +01:00
parent 093a23145f
commit 5870dc2577
8 changed files with 84 additions and 40 deletions
+25 -11
View File
@@ -53,15 +53,15 @@ class MyApp extends StatelessWidget {
),
if (service.currentDeviceInfo != null)
Text(
'Device Name: ${service.currentDeviceInfo!.displayName}\n'
'${Platform.isIOS ? 'Device ID: ${service.currentDeviceInfo!.id}' : ''}',
'Device Name: ${service.currentDeviceInfo!.displayName}'
'${Platform.isIOS ? '\nDevice ID: ${service.currentDeviceInfo!.id}' : ''}',
),
if (Platform.isIOS)
Text(
'You are ${service.isIOSBrowser ? 'going to find your friend' : 'waiting for another user to connect'}',
),
Text(
'Communication channel state: ${service.communicationChannelState.name.toUpperCase()}',
'Communication channel state: ${service.communicationChannelState.previewName}',
)
],
),
@@ -172,6 +172,16 @@ enum AppState {
}
}
extension on CommunicationChannelState {
String get previewName {
return switch (this) {
CommunicationChannelState.notConnected => 'Not connected',
CommunicationChannelState.loading => 'Connecting',
CommunicationChannelState.connected => 'Connected',
};
}
}
class AppService extends ChangeNotifier {
late final _nearbyService = NearbyService.getInstance()
..communicationChannelState.addListener(notifyListeners);
@@ -187,6 +197,12 @@ class AppService extends ChangeNotifier {
StreamSubscription? peersSubscription;
StreamSubscription? connectedDeviceSubscription;
@override
void dispose() {
stopListeningAll();
super.dispose();
}
CommunicationChannelState get communicationChannelState {
return _nearbyService.communicationChannelState.value;
}
@@ -328,7 +344,7 @@ class AppService extends ChangeNotifier {
final wasConnected = connectedDevice?.status.isConnected ?? false;
final nowConnected = event?.status.isConnected ?? false;
if (wasConnected && !nowConnected) {
restart();
stopListeningAll();
return;
}
connectedDevice = event;
@@ -358,7 +374,7 @@ class AppService extends ChangeNotifier {
Future<void> startCommunicationChannel({
ValueChanged<ReceivedNearbyMessage>? listener,
}) async {
final eventListener = NearbyServiceStreamListener<ReceivedNearbyMessage>(
final eventListener = NearbyServiceStreamListener(
onCreated: (_) {
updateState(AppState.communicationChannelCreated);
},
@@ -366,7 +382,7 @@ class AppService extends ChangeNotifier {
listener?.call(event);
},
onError: (e, [StackTrace? s]) {
restart();
stopListeningAll();
},
);
@@ -396,17 +412,15 @@ class AppService extends ChangeNotifier {
print(e);
}
} finally {
await restart();
await stopListeningAll();
}
notifyListeners();
}
Future<void> restart() async {
Future<void> stopListeningAll() async {
await stopListeningConnectedDevice();
await stopListeningPeers();
await stopDiscovery();
await discover();
}
void updateState(AppState state, {bool shouldNotify = true}) {
@@ -768,7 +782,7 @@ class _ConnectedSocketBodyState extends State<_ConnectedSocketBody> {
if (device == null) {
return Center(
child: _ActionButton(
onTap: service.restart,
onTap: service.stopListeningAll,
title: 'Restart',
),
);
+7
View File
@@ -12,6 +12,13 @@ abstract class NearbyMessage {
final String value;
///
/// Checks if [value] is not empty
///
bool get isValid {
return value.isNotEmpty;
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
@@ -27,6 +27,7 @@ class NearbySocketService {
String? _connectedDeviceId;
WebSocket? _socket;
HttpServer? _server;
StreamSubscription<ReceivedNearbyMessage>? _messagesSubscription;
///
@@ -72,21 +73,25 @@ class NearbySocketService {
/// Add [OutgoingNearbyMessage]'s JSON representation to [_socket].
///
Future<bool> send(OutgoingNearbyMessage message) async {
if (_socket != null && message.receiver.id == _connectedDeviceId) {
final sender = await _manager.getCurrentDeviceInfo();
if (sender != null) {
_socket!.add(
jsonEncode(
{
'message': message.value,
'sender': sender.toJson(),
},
),
);
if (message.isValid) {
if (_socket != null && message.receiver.id == _connectedDeviceId) {
final sender = await _manager.getCurrentDeviceInfo();
if (sender != null) {
_socket!.add(
jsonEncode(
{
'message': message.value,
'sender': sender.toJson(),
},
),
);
}
return true;
}
return true;
return false;
} else {
throw NearbyServiceException.invalidMessage(message.value);
}
return false;
}
///
@@ -96,8 +101,12 @@ class NearbySocketService {
try {
await _messagesSubscription?.cancel();
_messagesSubscription = null;
_socket?.close();
_socket = null;
_server?.close(force: true);
_server = null;
_connectedDeviceId = null;
state.value = CommunicationChannelState.notConnected;
return true;
} catch (e) {
return false;
@@ -142,11 +151,11 @@ class NearbySocketService {
required int port,
ValueChanged<HttpRequest>? serverListener,
}) async {
final httpServer = await _network.startServer(
_server = await _network.startServer(
ownerIpAddress: info.ownerIpAddress,
port: port,
);
httpServer?.listen(
_server?.listen(
(request) async {
serverListener?.call(request);
final isPing = await _pingManager.checkPing(request);
@@ -156,7 +165,7 @@ class NearbySocketService {
return;
}
if (request.uri.path == '/ws') {
if (request.uri.path == _Urls.ws) {
_socket = await WebSocketTransformer.upgrade(request);
_createSocketSubscription(socketListener);
} else {
@@ -5,6 +5,10 @@ class _Protocols {
static const ws = 'ws://';
}
class _Urls {
static const ws = '/ws';
}
class NearbyServiceNetwork {
final _httpClient = HttpClient();
final _random = Random();
@@ -20,7 +24,7 @@ class NearbyServiceNetwork {
final response = await request.close();
return response;
} catch (e) {
Logger.error('Server is unreachable');
Logger.error('Server is unreachable: $e');
return null;
}
}
@@ -37,7 +41,7 @@ class NearbyServiceNetwork {
required int port,
}) async {
try {
final url = 'ws://$ownerIpAddress:$port';
final url = '${_Protocols.ws}$ownerIpAddress:$port';
Logger.debug('Starting server on $url');
var server = await HttpServer.bind(
ownerIpAddress,
@@ -47,8 +51,7 @@ class NearbyServiceNetwork {
Logger.info('Server running on $url');
return server;
} catch (e) {
Logger.error('Error starting socket: $e');
return null;
throw NearbyServiceException('Error starting socket: $e');
}
}
@@ -58,14 +61,14 @@ class NearbyServiceNetwork {
}) async {
try {
final connectionId = _random.nextInt(1000) + 100;
final url = '${_Protocols.ws}$ownerIpAddress:$port/ws?as=$connectionId';
final url =
'${_Protocols.ws}$ownerIpAddress:$port${_Urls.ws}?as=$connectionId';
Logger.debug('Connecting to $url');
final socket = await WebSocket.connect(url);
Logger.info('Connected to $url');
return socket;
} catch (e) {
Logger.error('Error connecting to server: $e');
return null;
throw NearbyServiceException('Error connecting to server: $e');
}
}
}
@@ -201,6 +201,7 @@ class NearbyIOSService extends NearbyService {
FutureOr<bool> endCommunicationChannel() async {
await _messagesSubscription?.cancel();
_messagesSubscription = null;
_state.value = CommunicationChannelState.notConnected;
Logger.debug('Communication channel was cancelled');
return true;
}
@@ -211,7 +212,10 @@ class NearbyIOSService extends NearbyService {
///
@override
Future<bool> send(OutgoingNearbyMessage message) {
return NearbyServiceIOSPlatform.instance.send(message);
if (message.isValid) {
return NearbyServiceIOSPlatform.instance.send(message);
}
throw NearbyServiceException.invalidMessage(message.value);
}
///
@@ -242,7 +246,7 @@ class NearbyIOSService extends NearbyService {
if (value) {
Logger.info(onSuccess);
} else {
Logger.error(onError);
throw NearbyServiceException(onError);
}
}
@@ -28,7 +28,7 @@ class NearbyCommunicationChannelData {
///
/// Listener for message stream changes.
///
final NearbyServiceStreamListener<ReceivedNearbyMessage> eventListener;
final NearbyServiceStreamListener eventListener;
///
/// Android-specific connection data.
@@ -1,11 +1,12 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:nearby_service/nearby_service.dart';
///
/// Stream Subscription Listener.
///
class NearbyServiceStreamListener<T> {
class NearbyServiceStreamListener {
///
/// It is required to pass the [onData] parameter to process the
/// data that came through the stream.
@@ -18,8 +19,8 @@ class NearbyServiceStreamListener<T> {
this.cancelOnError,
});
final ValueChanged<T> onData;
final ValueChanged<StreamSubscription<T>>? onCreated;
final ValueChanged<ReceivedNearbyMessage> onData;
final ValueChanged<StreamSubscription<ReceivedNearbyMessage>>? onCreated;
final VoidCallback? onDone;
final void Function(Object, [StackTrace])? onError;
final bool? cancelOnError;
+6
View File
@@ -30,5 +30,11 @@ class NearbyServiceException implements Exception {
);
}
factory NearbyServiceException.invalidMessage(String value) {
return NearbyServiceException(
'The message="$value" is not valid',
);
}
final Object? error;
}