Autobahn|JS implements The Web Application Messaging Protocol V2 in JavaScript.
WAMP provides asynchronous Remote Procedure Calls and Publish & Subscribe for applications in one protocol running over WebSocket.
Autobahn|JS runs on both Web browsers and Node.js, and implements the following WAMP roles:
- Caller
- Callee
- Publisher
- Subscriber
Autobahn|JS is part of the Autobahn project, MIT licensed, and the full source code can be found on GitHub.
Here is what programming with Autobahn|JS looks like (identical code runs in browsers and on Node.js):
var autobahn = require('autobahn');
var connection = new autobahn.Connection({url: 'ws://127.0.0.1:9000/', realm: 'realm1'});
connection.onopen = function (session) {
// 1) subscribe to a topic
function onevent(args) {
console.log("Event:", args[0]);
}
session.subscribe('com.myapp.hello', onevent);
// 2) publish an event
session.publish('com.myapp.hello', ['Hello, world!']);
// 3) register a procedure for remoting
function add2(args) {
return args[0] + args[1];
}
session.register('com.myapp.add2', add2);
// 4) call a remote procedure
session.call('com.myapp.add2', [2, 3]).then(
function (res) {
console.log("Result:", res);
}
);
};
connection.open();
With Autobahn|JS, you can develop application components in JavaScript, and those components can be hosted inside browsers and Node.js as well as PostgreSQL in a future release.
To provide the communication between the components of your application, you need a WAMP v2 compatible application router.
The application router is responsible for call and event routing between your application's components. The router itself will not run any application code.
WAMP implementations need to catch up with V2 of WAMP, and currently, the only WAMP v2 compatible routers are Autobahn|Python and Crossbar.io.
Crossbar.io is an integrated server package that can run from configuration files and acts as a generic WAMP router. To get started with Crossbar.io, please see the project GitHub wiki.
You can find complete examples for code running in both the browser and Node.js, using an AutobahnPython application router, here.
You can get Autobahn|JS for Node.js using the Node Package Manager:
npm install autobahn
and then, in your code
var autobahn = require('autobahn')
Ignore any potential error messages regarding missing Visual C++ components. These have no influence on the actual result of the install.
You can get the latest (= WAMPv2 only) prebuilt Autobahn|JS release from here:
and use it in your HTML like so
<!DOCTYPE html>
<html>
<body>
<script src="https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min.jgz">
</script>
<script>
console.log("Ok, Autobahn loaded", autobahn.version);
</script>
</body>
</html>
You can use above via direct linking for development purposes, but do not hotlink for production. This will not work, since we place restrictions on HTTP referers.
The old Autobahn|JS for WAMPv1 is still available from here:
If you are using a module system like RequireJS, you can use Autobahn|JS like so:
<!DOCTYPE html>
<html>
<body>
<script src="http://requirejs.org/docs/release/2.1.11/minified/require.js"></script>
<script>
require.config({
baseUrl: ".",
paths: {
"autobahn": "https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min",
"when": "https://cdnjs.cloudflare.com/ajax/libs/when/2.7.1/when"
},
shim: {
"autobahn": {
deps: ["when"]
}
}
});
require(["autobahn"], function(autobahn) {
console.log("Ok, Autobahn loaded", autobahn.version);
});
</script>
</body>
</html>
The library can be included
try {
// for Node.js
var autobahn = require('autobahn');
} catch (e) {
// for browsers (where AutobahnJS is available globally)
}
Autobahn bundles whenjs and cryptojs, and the bundled libraries can be accessed like this
try {
var autobahn = require('autobahn');
var when = require('when');
var crypto = require('crypto-js');
} catch (e) {
var when = autobahn.when;
var crypto = autobahn.crypto;
}
Autobahn|JS library version is available (read-only):
autobahn.version
To enable debug mode, define a global variable
AUTOBAHN_DEBUG = true;
before including Autobahn|JS. E.g.
<!DOCTYPE html>
<html>
<body>
<script>
AUTOBAHN_DEBUG = true;
</script>
<script src="https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min.jgz">
</script>
</body>
</html>
A new connection is created by
var connection = new autobahn.Connection(<options|dict>);
Here, options
provides additional connection options (see below).
Example: Create a new connection
try {
// for Node.js
var autobahn = require('autobahn');
} catch (e) {
// for browsers (where AutobahnJS is available globally)
}
var connection = new autobahn.Connection({url: 'ws://127.0.0.1:9000/', realm: 'realm1'});
To open a connection:
autobahn.Connection.open();
Starts the connection, which will establish a transport and create a new session running over the transport. If the transport is lost, automatic reconnection will be done. The latter can be configured using the options
provided to the constructor of the Connection
(see below).
To close a connection:
autobahn.Connection.close(<reason|string>, <message|string>);
where
reason
is an optional WAMP URI providing a closing reason, e.g.com.myapp.close.signout
.message
is an optional (human readable) closing message.
When a connection was closed explicitly, no automatic reconnection will happen.
autobahn.Connection
provides two callbacks:
autobahn.Connection.onopen
autobahn.Connection.onclose
The connection open callback
autobahn.Connection.onopen = function (session) {
// Underlying connection to WAMP router established
// and new WAMP session started.
// session is an instance of autobahn.Session
};
is fired when the connection has been established and a new session was created. This is the main callback where application code will hook into.
The connection close callback
autobahn.Connection.onclose = function (<reason|string>, <details|dict>) {
// connection closed, lost or unable to connect
};
is fired when the connection has been closed explicitly, was lost or could not be established in the first place.
Here, the possible values for reason are:
"closed"
: The connection was closed explicitly (by the application or server). No automatic reconnection will be tried."lost"
: The connection had been formerly established at least once, but now was lost. Automatic reconnection will happen unless you return falsy from this callback."unreachable"
: The connection could not be established in the first place. No automatic reattempt will happen, since most often the cause is fatal (e.g. invalid server URL or server unreachable)
url|string
(required): the WebSocket URL of the WAMP router to connect torealm|string
(required): the WAMP realm to joinuse_es6_promises|bool
(optional): use deferreds based on ES6 promises *use_deferred|callable
(optional): if provided, use this deferred constructor, e.g.jQuery.Deferred
orQ.defer
max_retries
: Not yet implemented.retry_delay
: Not yet implemented.skip_subprotocol_check
: Not yet implemented.skip_subprotocol_announce
: Not yet implemented.
*: Using ES6-based promises has certain restrictions. E.g. no progressive call results are supported.
A read-only property with an instance of autobahn.Session
if there is a session currently running over the connection:
Connection.session
A Deferred factory for the type of Deferreds (whenjs, ES6, jQuery or Q) in use with the connection:
Connection.defer
To check whether the connection (the transport underlying) is established:
Connection.isOpen
To check whether the connection is currently in a "try to reconnect" cycle:
Connection.isRetrying
A read-only property that signals if the session is open and attached to a realm.
Session.isOpen
A read-only property with the session's ID.
Session.id
A read-only property with the realm the session is attached to.
Session.realm
A read-only property with all roles and features supported by both peers of the session.
Session.features
A factory function to create new Deferreds of the same class as used by the library itself.
Session.defer
A read-only property with a list of all subscriptions currently active on the session.
Session.subscriptions
A read-only property with a list of all registrations currently active on the session.
Session.registrations
Establish a prefix:
session.prefix('api', 'com.myapp.service');
You can then use CURIEs in addition to URIs:
session.call('api:add2').then(...);
To remove a prefix:
session.prefix('api', null);
To resolve a prefix (normally not needed in user code):
session.resolve('api:add2');
To subscribe to a topic on a session
:
var d = session.subscribe(<topic|uri>, <handler|callable>, <options|dict>);
where
topic
(required): is the URI of the topic to subscribe tohandler
(required): is the event handler that should consume eventsoptions
(optional) specifies options for subscription (see below).
and returns a promise that resolves to an instance of autobahn.Subscription
when successful, or rejects with an instance of autobahn.Error
when unsuccessful.
The handler
must be a callable
function (args, kwargs, details)
where
args
is the (positional) event payloadkwargs
is the (keyword) event payloaddetails
provides event metadata
Example: Subscribe to a topic
function on_event1(args, kwargs, details) {
// event received, do something ..
}
session.subscribe('com.myapp.topic1', on_event1).then(
function (subscription) {
// subscription succeeded, subscription is an instance of autobahn.Subscription
},
function (error) {
// subscription failed, error is an instance of autobahn.Error
}
);
A list of subscriptions (in no particular order) currently active on a session
may be accessed like this:
<autobahn.Session>.subscriptions
This returns a list of autobahn.Subscription
objects. E.g.
var subs = session.subscriptions;
for (var i = 0; i < subs.length; ++i) {
console.log("Active subscription with ID " + subs[i].id);
}
Caution: This property and the subscription objects returned should be considered read-only. DO NOT MODIFY.
You can unsubscribe a previously established subscription
var d = <autobahn.Subscription>.unsubscribe();
which returns a promise that resolves with no result value when successful or rejects with an instance of autobahn.Error
when unsuccessful.
Example: Unsubscribing a subscription
var sub1;
session.subscribe('com.myapp.topic1', on_event1).then(
function (subscription) {
sub1 = subscription;
}
);
...
sub1.unsubscribe().then(
function () {
// successfully unsubscribed sub1
},
function (error) {
// unsubscribe failed
}
);
Complete Examples:
To publish an event on a session
:
var d = session.publish(<topic|uri>, <args|list>, <kwargs|dict>, <options|dict>);
where
topic
(required): is the URI of the topic to publish toargs
(optional): is application event payload (a list giving the positional arguments)kwargs
(optional): is application event payload (a dictionary giving the keyword arguments)options
(optional) specifies options for publication (see below).
and returns either nothing or a promise if options.acknowledge
is set.
Example: Publish an event
session.publish('com.myapp.hello', ['Hello, world!']);
Complete Examples:
By default, a publish is not acknowledged by the Broker, and the Publisher receives no feedback whether the publish was indeed successful or not.
If supported by the Broker, a Publisher may request acknowledgement of a publish via the option acknowledge|bool
.
With acknowledged publish, the publish method will return a promise that will resolve to an instance of autobahn.Publication
when the publish was successful, or reject with an autobahn.Error
when the publish was unsuccessful.
Example: Publish with acknowledge
session.publish('com.myapp.hello', ['Hello, world!'], {}, {acknowledge: true}).then(
function (publication) {
// publish was successful
},
function (error) {
// publish failed
};
);
If the feature is supported by the Broker, a Publisher may restrict the actual receivers of an event beyond those subscribed via the options
exclude|list
eligible|list
exclude
is a list of WAMP session IDs providing an explicit list of (potential) Subscribers that won't receive a published event, even though they might be subscribed. In other words, exclude
is a blacklist of (potential) Subscribers.
eligible
is a list of WAMP session IDs providing an explicit list of (potential) Subscribers that are allowed to receive a published event. In other words, eligible
is a whitelist of (potential) Subscribers.
The Broker will dispatch events published only to *Subscribers+ that are not explicitly excluded via exclude
and which are explicitly eligible via eligible
.
Example: Publish with exclude
session.publish('com.myapp.hello', ['Hello, world!'], {}, {exclude: [123, 456]});
The event will be received by all Subscribers to topic com.myapp.hello
, but not the sessions with IDs 123
and 456
(if those sessions are subscribed anyway).
Example: Publish with eligible
session.publish('com.myapp.hello', ['Hello, world!'], {}, {eligible: [123, 456]});
The event will be received by the sessions with IDs 123
and 456
, if those sessions are subscribed to topic com.myapp.hello
.
By default, a Publisher of an event will not itself receive an event published, even when subscribed to the topic the Publisher is publishing to.
If supported by the Broker, this behavior can be overridden via the option exclude_me|bool
.
Example: Publish without excluding publisher
session.publish('com.myapp.hello', ['Hello, world!'], {}, {exclude_me: false});
If the feature is supported by the Broker, a Publisher may request the disclosure of its identity (it's WAMP session ID) to receivers of a published event via the option disclose_me|bool
.
Example: Publish with publisher disclosure
session.publish('com.myapp.hello', ['Hello, world!'], {}, {disclose_me: true});
If the Broker allows the disclosure, receivers can consume the Publisher's session ID like this:
function on_event(args, kwargs, details) {
// details.publisher provides the Publisher's WAMP session ID
// details.publication provides the event ID
}
session.subscribe(on_event, 'com.myapp.topic1');
To register a procedure on a session
for remoting:
var d = session.register(<procedure|uri>, <endpoint|callable>, <options|dict>);
where
procedure
(required): the URI of the procedure to registerendpoint
(required): the function that provides the procedure implementationoptions
(optional): specifies options for registration (see below)
and returns a promise that resolves to an instance of autobahn.Registration
when successful, or rejects with an instance of autobahn.Error
when unsuccessful.
The endpoint
must be a callable
function (args, kwargs, details) => result
where
args
are the (positional) call argumentskwargs
are the (keyword) call argumentsdetails
provides call metadata
and which returns either a plain value or a promise, and the value is serializable or an instance of autobahn.Result
.
The autobahn.Result
wrapper is used when returning a complex value (multiple positional return values and/or keyword return values).
Example: Register a procedure
function myproc1(args, kwargs, details) {
// invocation .. do something and return a plain value or a promise ..
}
session.register('com.myapp.proc1', myproc1).then(
function (registration) {
// registration succeeded, registration is an instance of autobahn.Registration
},
function (error) {
// registration failed, error is an isntance of autobahn.Error
}
);
Complete Examples:
A list of registrations (in no particular order) currently active on a session
may be accessed like this:
<autobahn.Session>.registrations
This returns a list of autobahn.Registration
objects. E.g.
var regs = session.registrations;
for (var i = 0; i < regs.length; ++i) {
console.log("Active registration with ID " + regs[i].id);
}
Caution: This property and the registration objects returned should be considered read-only. DO NOT MODIFY.
Write me.
To call a remote procedure from a session
:
var d = session.call(<procedure|uri>, <args|list>, <kwargs|dict>, <options|dict>);
where
topic
(required): is the URI of the procedure to callargs
(optional): are (positional) call argumentskwargs
(optional): are (keyword) call argumentsoptions
(optional) specifies options for the call (see below).
and returns a promise that will resolve to the call result if successful (either a plain value or an instance of autobahn.Result
) or reject with an instance of autobahn.Error
.
Example: Call a procedure
session.call('com.arguments.add2', [2, 3]).then(
function (result) {
// call was successful
},
function (error) {
// call failed
}
);
Complete Examples:
Write me.
Complete Examples:
Write me.
Complete Examples:
To build Autobahn|JS for use in browsers, you will need
To install NodeJS (here shown for Ubuntu):
sudo apt-get install nodejs nodejs-legacy npm
SCons is a Python based build tool, so you will need Python as well.
Taschenmesser is an SCons toolbelt also written in Python. To install Taschenmesser:
sudo pip install --upgrade taschenmesser[aws,svg]
Set environment variables (here shown for Windows):
-
JAVA_HOME
pointing to your Java run-timeC:\Program Files\Java\jre7
-
Add Python and Python script to
PATH
C:\Python27;C:\Python27\Scripts;
-
Set
JS_COMPILER
pointing to the Google Closurecompiler.jar
C:\Program Files\Google Closure\compiler.jar
Set environment variables (here shown for Ubuntu):
export JS_COMPILER=$HOME/compiler.jar
export JAVA_HOME=/usr/lib/jvm/default-java
Now clone the repo:
git clone [email protected]:tavendo/AutobahnJS.git
cd autobahnjs
To install JavaScript dependencies
npm install ws when crypto-js
Then start the build:
scons
When using a bash shell under Windows (e.g. git shell), use 'scons.py'.
This will produce 3 files inside the build
directory:
build/autobahn.js
build/autobahn.min.js
build/autobahn.min.jgz
To clean up your build:
scons -uc
For more information, including getting started, tutorials and reference documentation, please visit the project's homepage.
Get in touch on IRC #autobahn
on chat.freenode.net
or the mailing list.
Autobahn|JS includes code from the following open-source projects
Special thanks to the Coders with an Unhealthy Javascript Obsession for creating when.js - A lightweight Promise and when() implementation, plus other async goodies.