pub_sub 2.0.0

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 59

pub_sub

Pub build status

Keep application instances in sync with a simple pub/sub API.

Installation

Add pub_sub as a dependency in your pubspec.yaml file:

dependencies:
  pub_sub: ^1.0.0

Then, be sure to run pub get in your terminal.

Usage

pub_sub is your typical pub/sub API. However, pub_sub enforces authentication of every request. It is very possible that pub_sub will run on both servers and in the browser, or on a platform like Flutter. Thus, there are provisions available to limit access.

Be careful to not leak any pub_sub client ID's if operating over a network. If you do, you risk malicious users injecting events into your application, which could ultimately spell disaster.

A pub_sub server can operate across multiple adapters, which take care of interfacing data over different media. For example, a single server can handle pub/sub between multiple Isolates and TCP Sockets, as well as WebSockets, simultaneously.

import 'package:pub_sub/pub_sub.dart' as pub_sub;

main() async {
  var server = new pub_sub.Server([
    new FooAdapter(...),
    new BarAdapter(...)
  ]);

  server.addAdapter(new BazAdapter(...));

  // Call `start` to activate adapters, and begin handling requests.
  server.start();
}

The ID's of all clients who will connect to the server must be known at start-up time. You may not register new clients after the server has started. This is mostly a security consideration; if it is impossible to register new clients, then malicious users cannot grant themselves additional privileges within the system.

import 'package:pub_sub/pub_sub.dart' as pub_sub;

main() async {
  // ...
  server.registerClient(const ClientInfo('<client-id>'));

  // Create a user who can subscribe, but not publish.
  server.registerClient(const ClientInfo('<client-id>', canPublish: false));

  // Create a user who can publish, but not subscribe.
  server.registerClient(const ClientInfo('<client-id>', canSubscribe: false));

  // Create a user with no privileges whatsoever.
  server.registerClient(const ClientInfo('<client-id>', canPublish: false, canSubscribe: false));

  server.start();
}

Isolates

If you are just running multiple instances of a server, use package:pub_sub/isolate.dart.

You'll need one isolate to be the master. Typically this is the first isolate you create.

import 'dart:io';
import 'dart:isolate';
import 'package:pub_sub/isolate.dart' as pub_sub;
import 'package:pub_sub/pub_sub.dart' as pub_sub;

main() async {
  // Easily bring up a server.
  var adapter = new pub_sub.IsolateAdapter();
  var server = new pub_sub.Server([adapter]);

  // You then need to create a client that will connect to the adapter.
  // Each isolate in your application should contain a client.
  for (int i = 0; i < Platform.numberOfProcessors - 1; i++) {
    server.registerClient(new pub_sub.ClientInfo('client$i'));
  }

  // Start the server.
  server.start();

  // Next, let's start isolates that interact with the server.
  //
  // Fortunately, we can send SendPorts over Isolates, so this is no hassle.
  for (int i = 0; i < Platform.numberOfProcessors - 1; i++)
    Isolate.spawn(isolateMain, [i, adapter.receivePort.sendPort]);

  // It's possible that you're running your application in the server isolate as well:
  isolateMain([0, adapter.receivePort.sendPort]);
}

void isolateMain(List args) {
  var client =
      new pub_sub.IsolateClient('client${args[0]}', args[1] as SendPort);

  // The client will connect automatically. In the meantime, we can start subscribing to events.
  client.subscribe('user::logged_in').then((sub) {
    // The `ClientSubscription` class extends `Stream`. Hooray for asynchrony!
    sub.listen((msg) {
      print('Logged in: $msg');
    });
  });
}

JSON RPC 2.0

If you are not running on isolates, you need to import package:pub_sub/json_rpc_2.dart. This library leverages package:json_rpc_2 and package:stream_channel to create clients and servers that can hypothetically run on any medium, i.e. WebSockets, or TCP Sockets.

Check out test/json_rpc_2_test.dart for an example of serving pub_sub over TCP sockets.

Protocol

pub_sub is built upon a simple RPC, and this package includes an implementation that runs via SendPorts and ReceivePorts, as well as one that runs on any StreamChannel<String>.

Data sent over the wire looks like the following:

// Sent by a client to initiate an exchange.
interface Request {
  // This is an arbitrary string, assigned by your client, but in every case,
  // the client uses this to match your requests with asynchronous responses.
  request_id: string,
  
  // The ID of the client to authenticate as.
  // 
  // As you can imagine, this should be kept secret, to prevent breaches.
  client_id: string,

  // Required for *every* request.
  params: {
    // A value to be `publish`ed.
    value?: any,

    // The name of an event to `publish`.
    event_name?: string,

    // The ID of a subscription to be cancelled.
    subscription_id?: string
  }
}

/// Sent by the server in response to a request.
interface Response {
  // `true` for success, `false` for failures.
  status: boolean,
  
  // Only appears if `status` is `false`; explains why an operation failed.
  error_message?: string,

  // Matches the request_id sent by the client.
  request_id: string,

  result?: {
    // The number of other clients to whom an event was `publish`ed.
    listeners:? number,

    // The ID of a created subscription.
    subscription_id?: string
  }
}

When sending via JSON_RPC 2.0, the params of a Request are simply folded into the object itself, for simplicity's sake. In this case, a response will be sent as a notification whose name is the request_id.

In the case of Isolate clients/servers, events will be simply sent as Lists:

['<event-name>', value]

Clients can send the following (3) methods:

  • subscribe (event_name:string): Subscribe to an event.
  • unsubscribe (subscription_id:string): Unsubscribe from an event you previously subscribed to.
  • publish (event_name:string, value:any): Publish an event to all other clients who are subscribed.

The client and server in package:pub_sub/isolate.dart must make extra provisions to keep track of client ID's. Since SendPorts and ReceivePorts do not have any sort of guaranteed-unique ID's, new clients must send their SendPort to the server before sending any requests. The server then responds with an id that must be used to identify a SendPort to send a response to.

2.0.0

  • Dart 2 updates.

example/main.dart

import 'dart:io';
import 'dart:isolate';
import 'package:pub_sub/isolate.dart' as pub_sub;
import 'package:pub_sub/pub_sub.dart' as pub_sub;

main() async {
  // Easily bring up a server.
  var adapter = new pub_sub.IsolateAdapter();
  var server = new pub_sub.Server([adapter]);

  // You then need to create a client that will connect to the adapter.
  // Each isolate in your application should contain a client.
  for (int i = 0; i < Platform.numberOfProcessors - 1; i++) {
    server.registerClient(new pub_sub.ClientInfo('client$i'));
  }

  // Start the server.
  server.start();

  // Next, let's start isolates that interact with the server.
  //
  // Fortunately, we can send SendPorts over Isolates, so this is no hassle.
  for (int i = 0; i < Platform.numberOfProcessors - 1; i++)
    Isolate.spawn(isolateMain, [i, adapter.receivePort.sendPort]);

  // It's possible that you're running your application in the server isolate as well:
  isolateMain([0, adapter.receivePort.sendPort]);
}

void isolateMain(List args) {
  var client =
      new pub_sub.IsolateClient('client${args[0]}', args[1] as SendPort);

  // The client will connect automatically. In the meantime, we can start subscribing to events.
  client.subscribe('user::logged_in').then((sub) {
    // The `ClientSubscription` class extends `Stream`. Hooray for asynchrony!
    sub.listen((msg) {
      print('Logged in: $msg');
    });
  });
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  pub_sub: ^2.0.0

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter packages get

Alternatively, your editor might support pub get or flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:pub_sub/pub_sub.dart';
  
Version Uploaded Documentation Archive
2.0.0 Sep 4, 2018 Go to the documentation of pub_sub 2.0.0 Download pub_sub 2.0.0 archive
1.0.0 Aug 8, 2017 Go to the documentation of pub_sub 1.0.0 Download pub_sub 1.0.0 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
17
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
59
Learn more about scoring.

We analyzed this package on Oct 10, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0
  • pana: 0.12.4

Platforms

Detected platforms: Flutter, web, other

No platform restriction found in primary library package:pub_sub/pub_sub.dart.

Health suggestions

Format lib/isolate.dart.

Run dartfmt to format lib/isolate.dart.

Format lib/json_rpc_2.dart.

Run dartfmt to format lib/json_rpc_2.dart.

Format lib/pub_sub.dart.

Run dartfmt to format lib/pub_sub.dart.

Fix additional 7 files with analysis or formatting issues.

Additional issues in the following files:

  • lib/src/json_rpc/server.dart (Run dartfmt to format lib/src/json_rpc/server.dart.)
  • lib/src/protocol/client/sync_client.dart (Run dartfmt to format lib/src/protocol/client/sync_client.dart.)
  • lib/src/protocol/protocol.dart (Run dartfmt to format lib/src/protocol/protocol.dart.)
  • lib/src/protocol/server/adapter.dart (Run dartfmt to format lib/src/protocol/server/adapter.dart.)
  • lib/src/protocol/server/publish.dart (Run dartfmt to format lib/src/protocol/server/publish.dart.)
  • lib/src/protocol/server/subscription.dart (Run dartfmt to format lib/src/protocol/server/subscription.dart.)
  • lib/src/protocol/server/sync_server.dart (Run dartfmt to format lib/src/protocol/server/sync_server.dart.)

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev <3.0.0
json_rpc_2 ^2.0.0 2.0.9
stream_channel ^1.0.0 1.6.8
uuid ^1.0.0 1.0.3
Transitive dependencies
async 2.0.8
charcode 1.1.2
collection 1.14.11
convert 2.0.2
crypto 2.0.6
path 1.6.2
stack_trace 1.9.3
typed_data 1.1.6
Dev dependencies
test ^1.0.0