HTTP Bridge Publisher

Introduction

The HTTP Publisher (formerly HTTP Pusher) feature is available starting with Crossbar 0.9.5.

The HTTP Publisher is a service that allows clients to submit PubSub events via HTTP/POST requests. Crossbar will receive the event data via the request and forward the event via standard WAMP to any connected subscribers in real-time.

Try it

Clone the Crossbar.io examples repository, and go to the rest/publisher subdirectory.

Now start Crossbar:

crossbar start

and open http://localhost:8080 in your browser. Open the JavaScript console to see events received.

To submit events via HTTP/POST, you can use curl:

curl -H "Content-Type: application/json" \
   -d '{"topic": "com.myapp.topic1", "args": ["Hello, world"]}' \
   http://127.0.0.1:8080/publish

…or any other HTTP/POST capable tool or library.

Using Python

To make using the HTTP Publisher service even easier, we’ve created a (trivial) library which you can install by doing:

pip install crossbarconnect

``crossbarconnect`` does *not* depend on ``crossbar``, ``autobahn``,
``twisted`` or ``asyncio``. It only uses the Python standard
library. It only does HTTP/POST requests.

You can publish events from Python like this:

import crossbarconnect

client = crossbarconnect.Client("http://127.0.0.1:8080/publish")
client.publish("com.myapp.topic1", "Hello, world!", 23)

The example also contains two Python scripts for testing unsigned requests:

python publish.py

and signed requests:

python publish_signed.py

Configuration

The HTTP Publisher is configured on a path of a Web transport - here is part of a Crossbar configuration:

{
   "workers": [
      {
         "type": "router",
         ...
         "transports": [
            {
               "type": "web",
               ...
               "paths": {
                  ...
                  "publish": {
                     "type": "publisher",
                     "realm": "realm1",
                     "role": "anonymous"
                  }
               }
            }
         ]
      }
   ]
}

The service dictionary has the following parameters:

option

description

type

MUST be “publisher” (required)

realm

The realm to which the forwarding session is attached that will inject the submitted events, e.g. “realm1”(required)

role

The fixed (authentication) role the forwarding session is authenticated as when attaching to the router-realm, e.g. “role1” (required)

options

A dictionary of options (optional, see below).

The options dictionary has the following configuration parameters:

option

description

key

A string that when present provides the key from which request signatures are computed. If present, the secret must also be provided. E.g. “myapp1”.

secret

A string with the secret from which request signatures are computed. If present, the keymust also be provided. E.g. “kkjH68GiuUZ”).

post_body_limit

An integer when present limits the length (in bytes) of a HTTP/POST body that will be accepted. If the request body exceed this limit, the request is rejected. If 0, accept unlimited length. (default: 0)

timestamp_delta_limit

An integer when present limits the difference (in seconds) between a signature’s timestamp and current time. If 0, allow any divergence. (default: 0).

require_ip

A list of strings with single IP addresses or IP networks. When given, only clients with an IP from the designated list are accepted. Otherwise a request is denied. E.g. [“192.168.1.1/255.255.255.0”, “127.0.0.1”] (default: -).

require_tls

A flag that indicates if only requests running over TLS are accepted. (default: false).

debug

A boolean that activates debug output for this service. (default: false).

Running Standalone

If you only want to run WebSocket and the HTTP Publisher Service (and no other Web path services), here is an example configuration:

{
   "version": 2,
   "workers": [
      {
         "type": "router",
         "realms": [
            {
               "name": "realm1",
               "roles": [
                  {
                     "name": "anonymous",
                     "permissions": [
                        {
                           "uri": "*",
                           "allow": {
                              "call": true,
                              "register": true,
                              "publish": true,
                              "subscribe": true
                           }
                        }
                     ]
                  }
               ]
            }
         ],
         "transports": [
            {
               "type": "websocket",
               "endpoint": {
                  "type": "tcp",
                  "port": 9000
               }
            },
            {
               "type": "web",
               "endpoint": {
                  "type": "tcp",
                  "port": 8080
               },
               "paths": {
                  "/": {
                     "type": "publisher",
                     "realm": "realm1",
                     "role": "anonymous"
                  }
               }
            }
         ]
      }
   ]
}

This will run:

  1. a WAMP-over-WebSocket endpoint on ws://localhost:9000

  2. a HTTP Push Bridge endpoint on http://localhost:8080

You can test this using

<!DOCTYPE html>
<html>
   <body>
      <script src="autobahn.min.js"></script>
      <script>
         var connection = new autobahn.Connection({
            url: "ws://127.0.0.1:9000",
            realm: "realm1"
         });

         connection.onopen = function (session) {

            console.log("Connected");

            function onevent (args, kwargs) {
               console.log("Got event:", args, kwargs);
            }

            session.subscribe('com.myapp.topic1', onevent);
         };

         connection.onclose = function () {
            console.log("Connection lost", arguments);
         }

         connection.open();
      </script>
   </body>
</html>

and publishing from curl:

curl -H "Content-Type: application/json" \
   -d '{"topic": "com.myapp.topic1", "args": ["Hello, world"]}' \
   http://127.0.0.1:8080/
   ```

## Making Requests

To submit events through Crossbar, issue a HTTP/POST request to the URL of the Crossbar HTTP Publisher service with:

1. Content type `application/json`
2. Body containing a JSON object
3. Two query parameters: `timestamp` and `seq`

For a call to a HTTP Publisher service, the body MUST be a JSON object with the following attributes:

* `topic`: A string with the URI of the topic to publish to.
* `args`: An (optional) list of positional event payload arguments.
* `kwargs`: An (optional) dictionary of keyword event payload arguments.
* `options`: An (optional) dictionary of WAMP publication options (see below).

### Signed Requests

Signed requests work like unsigned requests, but have the following additional query parameters. All query parameters (below and above) are mandatory for signed requests.

* `key`: The key to be used for computing the signature.
* `nonce`: A random integer from [0, 2^53]
* `signature`: See below.

The signature computed as the Base64 encoding of the following value:

HMAC[SHA256]_{secret} (key | timestamp | seq | nonce | body)

Here, `secret` is the secret shared between the publishing application and Crossbar. This value will never travel over the wire.

The **HMAC[SHA256]** is computed w.r.t. the `secret`, and over the concatenation

key | timestamp | seq | nonce | body ```

The body is the JSON serialized event.

PHP - Symfony Publisher Bundle

For PHP/Symfony users, there is a bundle which makes publishing via HTTP comfortable - Crossbar HTTP Publisher Bundle (thanks to peelandsee for providing this).