diff --git a/README.md b/README.md index 69cc7b87..1247832c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # angel_websocket -[![1.0.0-dev+7](https://img.shields.io/badge/version-1.0.0--dev+7-red.svg)](https://pub.dartlang.org/packages/angel_websocket) +[![1.0.0-dev+8](https://img.shields.io/badge/pub-1.0.0--dev+8-red.svg)](https://pub.dartlang.org/packages/angel_websocket) [![build status](https://travis-ci.org/angel-dart/websocket.svg)](https://travis-ci.org/angel-dart/websocket) WebSocket plugin for Angel. @@ -27,6 +27,15 @@ main() async { ``` +Filtering events is easy with services. Just return a `bool`, whether +synchronously or asynchronously. + +```dart +myService.properties['ws:filter'] = (WebSocketContext socket) async { + return true; +} +``` + **Adding Handlers within a Controller** `WebSocketController` extends a normal `Controller`, but also listens to WebSockets. @@ -48,6 +57,12 @@ class MyController extends WebSocketController { void sendMessage(WebSocketContext socket, Db db) async { socket.send("found_message", db.collection("messages").findOne(where.id("..."))); } + + // Event filtering + @ExposeWs("foo") + void foo() { + broadcast(new WebSocketEvent(...), filter: (socket) async => ...); + } } ``` diff --git a/lib/server.dart b/lib/server.dart index b82f2dd2..75323e4d 100644 --- a/lib/server.dart +++ b/lib/server.dart @@ -20,7 +20,7 @@ typedef AngelWebSocketRegisterer(Angel app, RequestHandler handler); /// Broadcasts events from [HookedService]s, and handles incoming [WebSocketAction]s. class AngelWebSocket extends AngelPlugin { Angel _app; - List _clients = []; + List _clients = []; final List _servicesAlreadyWired = []; final StreamController _onAction = @@ -38,7 +38,7 @@ class AngelWebSocket extends AngelPlugin { final AngelWebSocketRegisterer register; /// A list of clients currently connected to this server via WebSockets. - List get clients => new List.unmodifiable(_clients); + List get clients => new List.unmodifiable(_clients); /// Services that have already been hooked to fire socket events. List get servicesAlreadyWired => @@ -65,15 +65,26 @@ class AngelWebSocket extends AngelPlugin { return (HookedServiceEvent e) async { var event = await transformEvent(e); event.eventName = "$path::${event.eventName}"; - await batchEvent(event); + + _filter(WebSocketContext socket) { + if (e.service.properties.containsKey('ws:filter')) + return e.service.properties['ws:filter'](socket); + else + return true; + } + + await batchEvent(event, filter: _filter); }; } /// Slates an event to be dispatched. - Future batchEvent(WebSocketEvent event) async { + Future batchEvent(WebSocketEvent event, + {filter(WebSocketContext socket)}) async { // Default implementation will just immediately fire events - _clients.forEach((client) { - client.add(god.serialize(event)); + _clients.forEach((client) async { + var result = true; + if (filter != null) result = await filter(client); + if (result == true) client.io.add(god.serialize(event)); }); } @@ -240,13 +251,15 @@ class AngelWebSocket extends AngelPlugin { ..end(); var ws = await WebSocketTransformer.upgrade(req.io); - _clients.add(ws); - var socket = new WebSocketContext(ws, req, res); + _clients.add(socket); await handleConnect(socket); _onConnection.add(socket); - req.properties['socket'] = socket; + + req + ..properties['socket'] = socket + ..inject(WebSocketContext, socket); ws.listen((data) { _onData.add(data); diff --git a/lib/websocket_controller.dart b/lib/websocket_controller.dart index 7b3e1361..eb0fbc75 100644 --- a/lib/websocket_controller.dart +++ b/lib/websocket_controller.dart @@ -1,28 +1,38 @@ part of angel_websocket.server; +/// Marks a method as available to WebSockets. class ExposeWs { final String eventName; const ExposeWs(this.eventName); } +/// A special controller that also supports WebSockets. class WebSocketController extends Controller { Map _handlers = {}; Map _handlerSymbols = {}; - AngelWebSocket ws; + + /// The plug-in instance powering this controller. + AngelWebSocket plugin; WebSocketController() : super(); - void broadcast(String eventName, data) { - ws.batchEvent(new WebSocketEvent(eventName: eventName, data: data)); + /// Sends an event to all clients. + void broadcast(String eventName, data, {filter(WebSocketContext socket)}) { + plugin.batchEvent(new WebSocketEvent(eventName: eventName, data: data), + filter: filter); } + /// Fired on new connections. onConnect(WebSocketContext socket) {} + /// Fired on disconnections. onDisconnect(WebSocketContext socket) {} + /// Fired on all incoming actions. onAction(WebSocketAction action, WebSocketContext socket) async {} + /// Fired on arbitrary incoming data. onData(data, WebSocketContext socket) {} @override diff --git a/pubspec.yaml b/pubspec.yaml index c3132635..d979743c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: angel_websocket description: WebSocket plugin for Angel environment: sdk: ">=1.19.0" -version: 1.0.0-dev+7 +version: 1.0.0-dev+8 author: Tobe O homepage: https://github.com/angel-dart/angel_websocket dependencies: diff --git a/test/controller/io_test.dart b/test/controller/io_test.dart index c80a4314..505c2582 100644 --- a/test/controller/io_test.dart +++ b/test/controller/io_test.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:angel_diagnostics/angel_diagnostics.dart' as srv; +import 'package:angel_diagnostics/angel_diagnostics.dart'; import 'package:angel_framework/angel_framework.dart' as srv; import 'package:angel_websocket/io.dart' as ws; import 'package:angel_websocket/server.dart' as srv; @@ -23,9 +23,9 @@ main() { await app.configure(websockets); await app.configure(new GameController()); + await app.configure(logRequests(new File('log.txt'))); - server = - await new srv.DiagnosticsServer(app, new File('log.txt')).startServer(); + server = await app.startServer(); url = 'ws://${server.address.address}:${server.port}/ws'; client = new ws.WebSockets(url);