first commit

This commit is contained in:
Hamza-Ayed
2026-06-09 08:40:31 +03:00
commit d8901e1a87
3161 changed files with 536187 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
import 'dart:convert';
/// Signature for [SocketNotifier.addCloses].
typedef CloseSocket = void Function(Close);
/// Signature for [SocketNotifier.addMessages].
typedef MessageSocket = void Function(dynamic val);
/// Signature for [SocketNotifier.open].
typedef OpenSocket = void Function();
/// Wrapper class to message and reason from SocketNotifier
class Close {
final String? message;
final int? reason;
Close(this.message, this.reason);
@override
String toString() {
return 'Closed by server [$reason => $message]!';
}
}
/// This class manages the transmission of messages over websockets using
/// GetConnect
class SocketNotifier {
List<void Function(dynamic)>? _onMessages = <MessageSocket>[];
Map<String, void Function(dynamic)>? _onEvents = <String, MessageSocket>{};
List<void Function(Close)>? _onCloses = <CloseSocket>[];
List<void Function(Close)>? _onErrors = <CloseSocket>[];
late OpenSocket open;
/// subscribe to close events
void addCloses(CloseSocket socket) {
_onCloses!.add(socket);
}
/// subscribe to error events
void addErrors(CloseSocket socket) {
_onErrors!.add((socket));
}
/// subscribe to named events
void addEvents(String event, MessageSocket socket) {
_onEvents![event] = socket;
}
/// subscribe to message events
void addMessages(MessageSocket socket) {
_onMessages!.add((socket));
}
/// Dispose messages, events, closes and errors subscriptions
void dispose() {
_onMessages = null;
_onEvents = null;
_onCloses = null;
_onErrors = null;
}
/// Notify all subscriptions on [addCloses]
void notifyClose(Close err) {
for (var item in _onCloses!) {
item(err);
}
}
/// Notify all subscriptions on [addMessages]
void notifyData(dynamic data) {
for (var item in _onMessages!) {
item(data);
}
if (data is String) {
_tryOn(data);
}
}
/// Notify all subscriptions on [addErrors]
void notifyError(Close err) {
// rooms.removeWhere((key, value) => value.contains(_ws));
for (var item in _onErrors!) {
item(err);
}
}
void _tryOn(String message) {
try {
var msg = jsonDecode(message);
final event = msg['type'];
final data = msg['data'];
if (_onEvents!.containsKey(event)) {
_onEvents![event]!(data);
}
// ignore: avoid_catches_without_on_clauses
} catch (_) {
return;
}
}
}

View File

@@ -0,0 +1,116 @@
import 'dart:async';
import 'dart:convert';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html';
import '../../../get_core/get_core.dart';
import 'socket_notifier.dart';
class BaseWebSocket {
String url;
WebSocket? socket;
SocketNotifier? socketNotifier = SocketNotifier();
Duration ping;
bool isDisposed = false;
bool allowSelfSigned;
ConnectionStatus? connectionStatus;
Timer? _t;
BaseWebSocket(
this.url, {
this.ping = const Duration(seconds: 5),
this.allowSelfSigned = true,
}) {
url = url.startsWith('https')
? url.replaceAll('https:', 'wss:')
: url.replaceAll('http:', 'ws:');
}
void close([int? status, String? reason]) {
socket?.close(status, reason);
}
// ignore: use_setters_to_change_properties
void connect() {
try {
connectionStatus = ConnectionStatus.connecting;
socket = WebSocket(url);
socket!.onOpen.listen((e) {
socketNotifier?.open();
_t = Timer?.periodic(ping, (t) {
socket!.send('');
});
connectionStatus = ConnectionStatus.connected;
});
socket!.onMessage.listen((event) {
socketNotifier!.notifyData(event.data);
});
socket!.onClose.listen((e) {
_t?.cancel();
connectionStatus = ConnectionStatus.closed;
socketNotifier!.notifyClose(Close(e.reason, e.code));
});
socket!.onError.listen((event) {
_t?.cancel();
socketNotifier!.notifyError(Close(event.toString(), 0));
connectionStatus = ConnectionStatus.closed;
});
} on Exception catch (e) {
_t?.cancel();
socketNotifier!.notifyError(Close(e.toString(), 500));
connectionStatus = ConnectionStatus.closed;
// close(500, e.toString());
}
}
void dispose() {
socketNotifier!.dispose();
socketNotifier = null;
isDisposed = true;
}
void emit(String event, dynamic data) {
send(jsonEncode({'type': event, 'data': data}));
}
void on(String event, MessageSocket message) {
socketNotifier!.addEvents(event, message);
}
void onClose(CloseSocket fn) {
socketNotifier!.addCloses(fn);
}
void onError(CloseSocket fn) {
socketNotifier!.addErrors(fn);
}
void onMessage(MessageSocket fn) {
socketNotifier!.addMessages(fn);
}
// ignore: use_setters_to_change_properties
void onOpen(OpenSocket fn) {
socketNotifier!.open = fn;
}
void send(dynamic data) {
if (connectionStatus == ConnectionStatus.closed) {
connect();
}
if (socket != null && socket!.readyState == WebSocket.OPEN) {
socket!.send(data);
} else {
Get.log('WebSocket not connected, message $data not sent');
}
}
}
enum ConnectionStatus {
connecting,
connected,
closed,
}

View File

@@ -0,0 +1,138 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import '../../../get_core/get_core.dart';
import 'socket_notifier.dart';
class BaseWebSocket {
String url;
WebSocket? socket;
SocketNotifier? socketNotifier = SocketNotifier();
bool isDisposed = false;
Duration ping;
bool allowSelfSigned;
ConnectionStatus? connectionStatus;
BaseWebSocket(
this.url, {
this.ping = const Duration(seconds: 5),
this.allowSelfSigned = true,
});
void close([int? status, String? reason]) {
socket?.close(status, reason);
}
// ignore: use_setters_to_change_properties
Future connect() async {
if (isDisposed) {
socketNotifier = SocketNotifier();
}
try {
connectionStatus = ConnectionStatus.connecting;
socket = allowSelfSigned
? await _connectForSelfSignedCert(url)
: await WebSocket.connect(url);
socket!.pingInterval = ping;
socketNotifier?.open();
connectionStatus = ConnectionStatus.connected;
socket!.listen((data) {
socketNotifier!.notifyData(data);
}, onError: (err) {
socketNotifier!.notifyError(Close(err.toString(), 1005));
}, onDone: () {
connectionStatus = ConnectionStatus.closed;
socketNotifier!
.notifyClose(Close('Connection Closed', socket!.closeCode));
}, cancelOnError: true);
return;
} on SocketException catch (e) {
connectionStatus = ConnectionStatus.closed;
socketNotifier!
.notifyError(Close(e.osError!.message, e.osError!.errorCode));
return;
}
}
void dispose() {
socketNotifier!.dispose();
socketNotifier = null;
isDisposed = true;
}
void emit(String event, dynamic data) {
send(jsonEncode({'type': event, 'data': data}));
}
void on(String event, MessageSocket message) {
socketNotifier!.addEvents(event, message);
}
void onClose(CloseSocket fn) {
socketNotifier!.addCloses(fn);
}
void onError(CloseSocket fn) {
socketNotifier!.addErrors(fn);
}
void onMessage(MessageSocket fn) {
socketNotifier!.addMessages(fn);
}
// ignore: use_setters_to_change_properties
void onOpen(OpenSocket fn) {
socketNotifier!.open = fn;
}
void send(dynamic data) async {
if (connectionStatus == ConnectionStatus.closed) {
await connect();
}
if (socket != null) {
socket!.add(data);
}
}
Future<WebSocket> _connectForSelfSignedCert(String url) async {
try {
var r = Random();
var key = base64.encode(List<int>.generate(8, (_) => r.nextInt(255)));
var client = HttpClient(context: SecurityContext());
client.badCertificateCallback = (cert, host, port) {
Get.log(
'BaseWebSocket: Allow self-signed certificate => $host:$port. ');
return true;
};
var request = await client.getUrl(Uri.parse(url));
request.headers.add('Connection', 'Upgrade');
request.headers.add('Upgrade', 'websocket');
request.headers.add('Sec-WebSocket-Version', '13');
request.headers.add('Sec-WebSocket-Key', key.toLowerCase());
var response = await request.close();
// ignore: close_sinks
var socket = await response.detachSocket();
var webSocket = WebSocket.fromUpgradedSocket(
socket,
serverSide: false,
);
return webSocket;
} on Exception catch (_) {
rethrow;
}
}
}
enum ConnectionStatus {
connecting,
connected,
closed,
}

View File

@@ -0,0 +1,54 @@
import './socket_notifier.dart';
class BaseWebSocket {
String url;
Duration ping;
bool allowSelfSigned;
BaseWebSocket(
this.url, {
this.ping = const Duration(seconds: 5),
this.allowSelfSigned = true,
}) {
throw 'To use sockets you need dart:io or dart:html';
}
Future connect() async {
throw 'To use sockets you need dart:io or dart:html';
}
void onOpen(OpenSocket fn) {
throw 'To use sockets you need dart:io or dart:html';
}
void onClose(CloseSocket fn) {
throw 'To use sockets you need dart:io or dart:html';
}
void onError(CloseSocket fn) {
throw 'To use sockets you need dart:io or dart:html';
}
void onMessage(MessageSocket fn) {
throw 'To use sockets you need dart:io or dart:html';
}
void on(String event, MessageSocket message) {
throw 'To use sockets you need dart:io or dart:html';
}
void close([int? status, String? reason]) {
throw 'To use sockets you need dart:io or dart:html';
}
void send(dynamic data) async {
throw 'To use sockets you need dart:io or dart:html';
}
void dispose() {
throw 'To use sockets you need dart:io or dart:html';
}
void emit(String event, dynamic data) {
throw 'To use sockets you need dart:io or dart:html';
}
}