Simple Message Queue JavaScript Client

SMQ.wsURL(path)

A utility function that simplifies constructing WebSocket URLs for WebSocket connections destined for the same server from which the web page was loaded.

Examples:
-- HTTP
-- The page URL is http://realtimelogic.com/somepage
var wsURL = SMQ.wsURL("/a/b/c/");
-- wsURL is ws://realtimelogic.com/a/b/c/

-- HTTPS
-- The page URL is https://realtimelogic.com/somepage
var wsURL = SMQ.wsURL("/a/b/c/");
-- wsURL is wss://realtimelogic.com/a/b/c/
SMQ.Client([wsURL] [,op])

Create an SMQ client instance and initiate the connection with the broker.

Example:

The following example initiates a connection back to the same URL from where the HTML page was loaded.

var smq = SMQ.Client(); // No args: connect back to 'origin'.
smq.publish("I am Alice", "chat", "hello");
smq.subscribe("chat", "hello",
              {datatype:"text",
               onmsg:function(message,ptid) {
                   if(ptid != smq.gettid()) { //Ignore messages from 'self'
                       // Respond to publisher (ptid)
                       smq.publish("I am Bob",ptid,"hello");
                   }
               }
              });
smq.onconnect = function() {
    console.log("Connected");
});

In the above example, we establish a WebSocket connection back to where the page was origionally loaded. We then immediately both publish and subscribe to topic "chat' and sub-topic "hello" before the client is connected. This is OK since all actions are performed on the SMQ client while disconnected and will remain queued until a connection is established.

We register an on-connect event callback function on line 12. The callback is initiated as soon as the server connection is established and when the client is authenticated, if required.

A chat implementation may, for example, publish all messages on the topic 'chat' and use sub-topics for the various message types sent on the chat channel. The sub-topic "hello" is sent when we first connect. It is also received in parallel as a response from new clients that subscibe to the same subtopic "hello" message. This is illustrated on line 8, where we send a message directly to the publisher. The example demonstrates how a new client can broadcast a message to all connected clients and receive individual notifications from all connected clients that subsribed to the sub-topic "hello". On line 8, a response message is sent directly to the publisher's ephemeral topic ID.

SMQ Object Methods

SMQ Object Methods

The following provides a list of methods available in an SMQ JavaScript client instance. Most functions do not immediately return a value, but are instead reporting the return value in optional callback functions. Only methods that include a description for a return value are, in fact, returning a value.

create(topic [,subtopic] [,onack])

Create a topic an fetch the topic ID (tid). The SMQ protocol is optimized and does not directly use a string when publishing, but a number. The server randomly creates a 32 bit number and persistently stores the topic name and number. The 'create' method is typically used prior to publishing a message on a specific topic if the server logic implements authorization. Otherwise, the publish method can be used directly with a topic string since the publish method takes care of first creating a topic if you publish to a topic unknown to the client stack.

  • string topic - The topic name where you plan on publishing messages.
  • string subtopic - Provide the second level topic name if you plan on using this feature.
  • function onack(accepted, topic, tid, [subtopic, subtid]) - The callback is called when the broker sends the response message.
    • boolean accepted - set to 'true' if the broker accepted the (create) request, and 'false' if the broker's optional authorization callback function has denied access.
    • string topic - the topic name requested.
    • number tid - the unique and persistent 32 bit topic ID (tid) created by the server.
    • string subtopic - the sub-topic name requested, or 'undefined' if you did not request a sub-topic.
    • number subtid - the unique and persistent 32 bit sub-topic ID (tid) created by the server or 'undefined' if you did not request a sub-topic.
createsub(subtopic [,onsuback])

Create a sub-topic and fetch the subtopic ID. The createsub method is typically used prior to publishing a message on a specific topic, and sub-topic, (if) the server logic implements "authorization". Alternatively, the publish method may be used directly with topic and sub-topic strings, respectively. The publish method will manage the sequence and creation of a topic, then sub-topic, if you publish to a topic and/or sub-topic that is unknown to the client stack.

  • string subtopic - The sub-topic name.
  • function onsuback(accepted,subtopic,subtid) - The callback is called when the broker sends the response message.
    • boolean accepted - set to 'true' if the broker accepted the (createsub) request, and 'false' if the broker's optional authorization callback function has denied access.
    • string subtopic - the sub-topic name requested.
    • number subtid - the unique and persistent 32 bit sub-topic ID.
disconnect()

Gracefully close the connection. You cannot publish any messages after calling this method.

gettid()

Get the client's ephemeral topic ID. Each client is assigned a unique topic ID, and this topic ID is included when publishing a message. All subscribers receiving the message published by a client can use this (tid) for identification purposes or for sending messages directly to the "publisher" of the message. See the subscribe onmsg for example code.

publish(data, topic [,subtopic])

Publish messages to a topic and optionally to a sub-topic. Topics may be topic names (strings), topic IDs (numbers), or ephemeral topic IDs (numbers). Messages publishing unresolved topic names are temporarily queued. Pending topic names are resolved by calling create and/or createsub and result in the message being de-queued and published, (if) create and/or createsub reports are "accepted" by the server. Once the server denies a create and/or createsub report request, all messages sent to (unresolved) topic names are de-queued and silently discarded. You may consider using create and/or createsub prior to calling "publish" if the server solution implements topic authorization. Note: max payload size is 0xFFF0.

  • string|Uint8Array data - The data can be one of JavaScript string or an Uint8Array. A JavaScript string is encoded as UTF8 and sent as an Uint8Array.
  • string|number topic - A topic name or a topic ID fetched from a previous call to method create. The topic ID can also be an ephemeral topic ID received in a previous call to an onmsg callback function. See callback function onmsg's argument ptid for details.
  • string|number subtopic - An optional sub-topic name or a sub-topic ID fetched from a previous call to method createsub.
pubjson(value, topic [,subtopic])

Similar to method publish, except the value is first encoded as JSON and then encoded as UTF8 before being sent on the wire. See method publish for details.

Method pubjson is typically used in conjunction with subscribers listening on the topic and where the subscriber has the datatype set to "json". See subscribe - datatype for details.

  • object value - Encode the argument "value", which must be a JavaScript object, as JSON and publish the message.
  • string|number topic - Same as for method publish.
  • string|number subtopic - Same as for method publish.
subscribe(topic [,subtopic] [,settings])

Subscribe to a topic and optionally to a sub-topic. You can subscribe multiple times to the same topic if you use sub-topics. Subscribing to a topic without providing a sub-topic introduces a "catch all" for sub-topics that do not correspond to any subscribed sub-topics.

The topic name "self" is interpreted as subscribing to the client's own Ephemeral Topic ID -- in other words, it means subscribing to the (tid) returned by method gettid. Subscribing to your own Topic ID makes it possible for other connected clients to send a message directly to this client.

Subscription requests are stored in an internal queue and later processed if the server connection is not established. Subscription requests are also stored temporarily if the sub-topic name must be resolved prior to subscribing.

  • string topic - The topic to subscribe to. The topic name 'self' means subscribing to the client's own Ephemeral Topic ID.
  • string|number subtopic - An optional sub-topic name or a sub-topic ID fetched via method createsub.
  • object settings - 'settings' is a JavaScript object with the following optional properties:
    • datatype - The type of data that you are expecting to receive on the subscribed topic. If none is specified, the 'onmsg' callback will receive raw data as an Uint8Array. The available datatypes are:
      • text - Converts the response from UTF-8 to JavaScript UTF-16 string representation; any malformed UTF-8 is rejected.
      • json - Evaluates the response first as text and then as JSON and returns a JavaScript object. The JSON data is parsed in a strict manner; any malformed JSON is rejected.
      The "onmsg" callback will not be called if the data cannot be converted to the requested data-type. Instead, the data will be sent to the global onmsg event handler function, if installed.
    • onack(accepted, topic, tid [,subtopic, subtid]) -
      • boolean accepted - Set to 'true' if the broker accepted the subscribe request, and 'false' if the broker's optional authorization callback function denied access.
      • string topic - The topic name requested.
      • number tid - The unique and persistent 32 bit topic ID (tid) created by the server.
      • string subtopic - The sub-topic name requested, or 'undefined' if you did not request a sub-topic.
      • number subtid - The unique and persistent 32 bit sub-topic ID created by the server, or 'undefined' if you did not request a sub-topic.
    • onmsg(message,ptid,tid,subtid) - The callback function is called when the client receives data on the subscribed topic and optional sub-topic. You can install multiple callbacks for the same topic if you use sub-topics. The callback will then be called for the correct topic and sub-topic. Sub-topics not subscribed to will be sent to the callback with no subtopic or to the global onmsg event handler if no callback is provided.
      • Uint8Array|string|object message - The data received from the publisher. The message type is controlled by the datatype parameter in the optional settings. The data will be a Uint8Array (raw data) if you did not specify a datatype, a string if you specified 'text', or an object if you specified 'json'.
      • number ptid - Publisher ephemeral topic ID. The (ptid) enables you to send a message directly to the publisher without having to create dynamically named topics, which is common in a traditional pub/sub protocol.
      • number tid - The topic ID.
      • number subtid - The sub-topic ID, or 'undefined' if you did not request a sub-topic when you subscribed to the topic.
      You normally do not need the (tid) and (subtid) in the callback since you provide a callback per topic and sub-topic. An 'onmsg' callback is therefore typically created as follows:
      function myonmsg(message,ptid) {
          console.log(message);
          //Send ACK message to 'publisher' -- simulate an RPC response
          smq.publish("Thanks, got it", ptid, "ACK"); 
      };
      
unsubscribe(topic)

Requests the server to unsubscribe the client from a topic. All registered onmsg callback functions, including all callbacks for sub-topics, will be removed from the client stack.

  • string|number topic - Topic name or topic ID.
observe(topic,onchange)

Requests the broker to provide change notification events when the number of subscribers to a specific topic changes. Ephemeral topic IDs can also be observed. The number of connected subscribers for an ephemeral ID can only be one, which means the client is connected. Receiving a change notification for an ephemeral ID means the client has disconnected and that you no longer will get any change notifications for the observed topic ID. The client stack automatically "unobserves" observed ephemeral topic IDs when it receives a change notification.

  • string|number topic - Topic name or topic ID.
  • function onchange(subscribers, topic) - The callback function is called when the broker sends a change notification event.
    • number subscribers - The number of clients subscribed to the topic.
    • string|number topic - The topic name requested or the ephemeral topic ID requested to be observed.
unobserve(topic)

Stop receiving change notifications for a topic or ephemeral topic ID.

  • string|number topic - Topic name or topic ID.
tid2topic(tid)

Translates topic ID to topic name.

  • number tid - Topic ID.
  • Returns: string|null - Returns the topic name if the topic name is registered in the client.
topic2tid(topic)

Translates topic name to topic ID.

  • string topic - Topic name.
  • Returns: number|null - Returns the topic ID if the topic name is registered in the client.
tid2subtopic(tid)

Translates sub-topic ID to sub-topic name.

  • number tid - Sub-topic ID.
  • Returns: string|null - Returns the sub-topic name if the sub-topic name is registered in the client.
subtopic2tid(subtopic)

Translates sub-topic name to sub-topic ID.

  • string subtopic - Sub-topic name.
  • Returns: number|null - Returns the sub-topic ID if the sub-topic name is registered in the client.

SMQ Object Event Handlers

SMQ Object Event Handler Functions

You can set the following event callback functions on an SMQ instance.

onauth(rnd, ipaddr)

The onauth function must be set if the server requires authentication. The event function is called during the SMQ handshaking phase and just before the client SMQ stack sends the CONNECT event to the broker.

  • number rnd - A random number created by the server that can be used for securing hash based password encryption.
  • string ipaddr - The client's IP address as seen by the server. This address may not be the same as the client's IP address if the client is behind a NAT router. The client's IP address can be used by hash based authentication to further strengthen the hash value.

The following example sends the combined username and password in clear-text. Sending passwords in clear-text is OK if the WebSocket connection is encrypted.

smq.onauth=function() {
    var credentials = "username:password";
    return credentials;
};

It is not recommended sending passwords in clear-text unless the connection is encrypted and the server provides a certificate trusted by the client. Non secure connections and secure connections where the server uses self signed certificates should instead calculate a hash value. The following example shows one possible method for calculating the hash value.

smq.onauth=function(rnd, ipaddr) {
    var username="username";
    var password="password";
    //Create a hash value using SHA512
    var hash = CryptoJS.SHA3(rnd + ipaddr + username + password);
    var credentials = username + ":" + hash.toString(CryptoJS.enc.Base64);
    return credentials;
};

In the above example, we use the cryptoJS library.

There are no requirements for how to create the hash value. The only requirement is that you must install an authentication callback function on the server and calculate the hash the same way you do in the client. The password is deemed correct if the code you create for the server side creates a hash value that is identical to the hash value created by the client.

Adding the random number and the IP address to the hash value prevents eavesdroppers from using rainbow tables for cracking the password. The same random number and IP address is available in the server's authentication callback function.

onconnect(tid, rnd, ipaddr)

The onconnect function is called after a successful connection sequence and if the server accepted the credentials. If the connection was unsuccessful or if the connection was not accepted by the broker, the onclose function is called instead.

  • number tid - The client's unique topic ID created by the server. This tid is referred to as the ephemeral tid and is the same tid returned by method gettid.
  • number rnd - The same random number as provided to the onauth function.
  • string ipaddr - The same IP address as provided to the onauth function.
onmsg(data,ptid,tid,subtid)

The onmsg function is called if you subscribe to a topic and do not provide a callback function when subscribing. The onmsg function is also called if a "topic" callback function fails to decode the data as specified in the datatype.

  • Uint8Array data - The raw data provided by the publisher.
  • number ptid - Publisher ephemeral topic ID.
  • number tid - The topic ID.
  • number subtid - The sub-topic ID.
onclose(message,canreconnect)

The onclose function is called if the connection cannot be established, the server denied access, the server gracefully closed the connection, or if the connection unexpectedly closed.

The onclose function can request the SMQ client to attempt to reconnect. Function onreconnect is called if the re-connect attempt is successful. The onclose function is called again if the re-connection attempt is unsuccessful.

  • string message - The reason for why the connection was closed.
  • boolean canreconnect - If the client can re-connect, for example, by reconnect with new credentials.
  • Returns: number|null - If the client can re-connect, the callback function can return the number of milliseconds the client should wait until it attempts to reconnect.
onreconnect(tid, rnd, ipaddr)

The onreconnect function is called if the connection closed, the onclose function requested the SMQ client to reconnect, and if the re-connect was successful.

The SMQ client will re-establish all subscriptions, re-issue "observe events" except "ephemeral observe events", and send any pending published messages prior to calling the onreconnect function.

  • tid - The new ephemeral topic ID.
  • rnd - A new random number.
  • ipaddr - The client's IP address. This address may not be the same as the address received in "onconnect". For example, a mobile phone that loses wifi signals and switches to a cellular network will have a new IP address when it reconnects.