// ignore_for_file: prefer_final_fields import 'dart:async'; import 'dart:io'; import 'package:protobuf/protobuf.dart'; import 'packets/message_common.pb.dart'; import 'ws_protobuf_client_io.dart'; abstract class WsProtobufServer { List _clientList = []; bool _started = false; HttpServer? _server; final String _host; final int _port; final SecurityContext? _securityContext; final WsProtobufClient Function(WebSocket) _createNewClient; bool get started => _started; bool get isSecure => _securityContext != null; /// Plain WebSocket server (ws://). WsProtobufServer(this._host, this._port, this._createNewClient) : _securityContext = null; /// Secure WebSocket server (wss://). /// /// [securityContext] must have a certificate and private key loaded: /// ```dart /// final ctx = SecurityContext() /// ..useCertificateChain('server.crt') /// ..usePrivateKey('server.key'); /// final server = MyServer.secure('0.0.0.0', 443, ctx, (ws) => MyClient(ws)); /// ``` WsProtobufServer.secure(this._host, this._port, SecurityContext securityContext, this._createNewClient) : _securityContext = securityContext; Future start() async { final ctx = _securityContext; if (ctx != null) { _server = await HttpServer.bindSecure(_host, _port, ctx); } else { _server = await HttpServer.bind(_host, _port); } _started = true; _server!.listen((HttpRequest request) async { if (WebSocketTransformer.isUpgradeRequest(request)) { final ws = await WebSocketTransformer.upgrade( request, compression: CompressionOptions.compressionOff, ); _onClientWebSocket(ws); } }); return Future.value(); } void _onClientWebSocket(WebSocket ws) { var client = _createNewClient(ws); client.addDisconnectListener(onDisconnectedClient); client.addListener((_) => onClientHeartBeat(client)); _clientList.add(client); onClientConnected(client); } // implement in child void onClientConnected(WsProtobufClient client); void onClientHeartBeat(WsProtobufClient client) {} void onDisconnectedClient(WsProtobufClient client) { _clientList.remove(client); unawaited(client.close()); } Future broadcast(T data) async { for (var client in _clientList) { await client.send(data); } return Future.value(); } Future stop() async { final clients = List.from(_clientList); _clientList.clear(); await _server?.close(force: true); for (final client in clients) { await client.close(); } _started = false; return Future.value(); } }