Vibe has been renamed to Cettia and is no longer maintained! Use Cettia. ×
flowersinthesand wrote Vibe 3.0.0-Alpha12 released on February 5, 2015.

Reference


Table of Contents


Installation

As browser client

Download vibe.js the way you want.

Then load or link it by using either script tag or Asynchronous Module Definition loader.

Script tag

<script src="/vibe/vibe.min.js"></script>
<script>
var socket = vibe.open("/vibe");
</script>

AMD loader

require(["vibe"], function(vibe) {
    var socket = vibe.open("/vibe");
});

As Node.js client

vibe.js is available on npm under the name of vibe-client. Install the module.

npm install vibe-client --save

It will install the latest version adding it to dependencies entry in package.json in the current project folder. If you are on Windows, you may have trouble in installing Contextify. See a installation guide from jsdom.

Then load it as a Node.js module.

var vibe = require("vibe-client");
var socket = vibe.open("http://localhost:8080/vibe");

Socket

The feature-rich and flexible interface for two-way communication.

Life Cycle

Socket always is in a specific state that can be accessed by state() method. According to the status of connection, transition between states occurs and this circulating transition makes a life cycle. The following list is a list of state which a socket can be in. In any case, if connection is closed due to error, error event will be fired before transition to closed.

  • preparing

    As an initial state of life cycle, it's internally used during reinitializing socket. State transition occurs to connecting without exception.

  • connecting

    The selected transports start connecting to the server and the connecting event is fired. Each transport should establish a connection in time which is set to timeout option. The connecting event is an initial event where you can handle the socket during the life cycle.

    State transition occurs to

    • opened: if one of available transports succeeds in establishing a connection.
    • closed: if close() method is called.
    • closed: if every transport fails to connect in time.

  • opened

    The connection is established successfully and communication is possible. The open event is fired. Only in this state, the socket can send and receive events via connection.

    State transition occurs to

    • closed: if close() method is called.
    • closed: if connection is closed cleanly.
    • closed: if heartbeat fails.
    • closed: if connection is disconnected due to some error.

  • closed

    The connection has been closed, has been regarded as closed or could not be opened. If the reconnect handler is false or returns false, the socket's life cycle ends here.

    State transition occurs to

    • waiting: if the reconnect handler returns a positive number.

  • waiting

    The socket waits out the reconnection delay. The waiting event is fired with the reconnection delay in milliseconds and the total number of reconnection attempts.

    State transition occurs to

    • preparing: after the reconnection delay.
    • closed: if close() method is called.

Error Handling

To capture any error happening in the socket, use error event. If any error is thrown by the socket, error event will be fired with Error object in question. In most cases, there is nothing you should do in error event.

Note

  • Errors thrown by user created event handler are not propagated to error event.
vibe.open("http://localhost:8080/vibe", {reconnect: false})
.on("error", function(error) {
    console.error(error);
});

Sending and Receiving Event

You can send event using send(event: string, data?: any) and receive event using on(event: string, onEvent: (data?: any) => void). Any event name can be used, except connecting, open, error, close and waiting and any data can be sent and received but it should be able to be marshalled/unmarshalled to JSON.

Note

  • Socket must be in opened state.
  • To manage a lot of events easily, use URI as event name format like /account/update.

The client sends events and the server echoes back to the client.

Client

vibe.open("http://localhost:8080/vibe", {reconnect: false})
.on("open", function() {
    this.send("echo", Math.PI)
    .send("echo", "pi")
    .send("echo", {"p": "i"})
    .send("echo", ["p", "i"]);
})
.on("echo", function(data) {
    console.log(data);
});

Server

server.on("socket", function(socket) {
    socket.on("echo", function(data) {
        console.log(data);
        this.send("echo", data);
    });
});

The server sends events and the client echoes back to the server.

Client

vibe.open("http://localhost:8080/vibe", {reconnect: false})
.on("echo", function(data) {
    console.log(data);
    this.send("echo", data);
})

Server

server.on("socket", function(socket) {
    socket.on("echo", function(data) {
      console.log(data);
    })
    .send("echo", Math.PI)
    .send("echo", "pi")
    .send("echo", {"p": "i"})
    .send("echo", ["p", "i"]);
});

Getting and Setting Result of Event Processing

You can get the result of event processing from the server in sending event using send(event: string, data?: any, onResolved?: (data?: any) => void, onRejected?: (data?: any) => void) and set the result of event processing to the server in receiving event using on(event: string, handler:(data?: any, reply?: {resolve: (data?: any) => void; reject: (data?: any) => void}) => void). Either resolved or rejected callback is executed once when the server executes it.

You can apply this functionality to sending events in order, Acknowledgements, Remote Procedure Call and so on.

Note

  • Socket must be in opened state.
  • Beforehand determine whether to use rejected callback or not to avoid writing unnecessary rejected callbacks.

The client sends events attaching callbacks and the server executes one of them with the result of event processing.

Client

vibe.open("http://localhost:8080/vibe", {reconnect: false})
.on("open", function(data) {
    this.send("/account/find", "donghwan", function(data) {
        console.log("resolved with ", data);
    }, function(data) {
        console.log("rejected with ", data);
    })
    .send("/account/find", "flowersits", function(data) {
        console.log("resolved with ", data);
    }, function(data) {
        console.log("rejected with ", data);
    });
});

Server

server.on("socket", function(socket) {
    socket.on("/account/find", function(id, reply) {
      console.log(id);
      if (id === "donghwan") {
          reply.resolve({name: "Donghwan Kim"});
      } else {
          reply.reject("¯\(°_o)/¯");
      }
    });
});

The server sends events attaching callbacks and the client executes one of them with the result of event processing.

Client

vibe.open("http://localhost:8080/vibe", {reconnect: false})
.on("/account/find", function(id, reply) {
    console.log(id);
    if (id === "donghwan") {
        reply.resolve({name: "Donghwan Kim"});
    } else {
        reply.reject("¯\(°_o)/¯");
    }
});

Server

server.on("socket", function(socket) {
    socket.send("/account/find", "donghwan", function(data) {
        console.log("resolved with ", data);
    }, function(data) {
        console.log("rejected with ", data);
    })
    .send("/account/find", "flowersits", function(data) {
        console.log("resolved with ", data);
    }, function(data) {
        console.log("rejected with ", data);
    });
});

Heartbeat

Considering a multitude of browsers, transports, servers, networks and their combination, heartbeat is essential to maintain stable connection. For that reason, a socket starts heartbeat communication every 20 seconds on the open event by default. It can be configured only through the server.

Eavesdropping heartbeat of client and server.

Client

vibe.open("http://localhost:8080/vibe", {reconnect: false})
.on("heartbeat", function() {
    console.log("heartbeat at ", Date.now());
});

Server

server.on("socket", function(socket) {
    socket.on("heartbeat", function() {
        console.log("heartbeat at ", Date.now());
    });
});

Reconnection

Reconnection has been disabled in the above code snippets for convenience of test, but it's essential for production so that it's enabled by default. The default strategy generates a geometric progression with initial delay 500 and ratio 2 (500, 1000, 2000, 4000 ...). To change it, set reconnect (delay: number, attempts: number): number function which receives the last delay in ms or null at first and the total number of reconnection attempts and should return a delay in ms or false not to reconnect.

Note

  • Don't add event handler by on method during dispatch including open event. Because reconnection doesn't remove existing event handlers, it will be duplicated in next life cycle.

Transport

WebSocket is clearly a best transport but in the real world, many coporate proxies, firewalls, antivirus softwares and cloud application platforms block it for some reason. Of course, many users still use browsers not supporting WebSocket, which we can't compel to upgrade it. Moreover, other transports have their own similar restrictions as well.

Such transports are configurable by URI which is set from vibe.open(uri: string). If a URI whose scheme is http or https have no transport param, it is translated into three URIs which have ws, stream and longpoll transport param in order. To change that order, you can use vibe.open(uris: string[]) with URI having transport param. A translated URI corresponds to a transport, and transport's availability is calculated by feature detection determining if it is available on the browser and by try and error determining if it really establishes a connection to the server.

For example, if a given URI is http://host/vibe, first, it is translated into http://host/vibe?transport=ws, http://host/vibe?transport=stream and http://host/vibe?transport=longpoll corresponding to ws, stream, longpoll transport respectively. Second, ws's availability is calculated by checking if the browser supports WebSocket and trying connection. If WebSocket is not supported in that browser or the connection isn't established in time which is set to timeout: number option, the same way applies to stream and longpoll. In other words, it will work if it can work so that you don't need to even know what transport is.


Transport

An interface that used to establish a connection, send data, receive data and close the connection. Therefore, transport doesn't have an influence on socket's functionalities.

Implementation

According to the protocol and the technology, the following implementations are available:

WebSocket

It is a protocol designed for a full-duplex communications over a TCP connection. However, many coporate proxies, firewalls and antivirus softwares blocks it for some reason. A given URI should have ws or wss scheme or http or https scheme and ws transport param. And then it works if browser implements WebSocket regardless of its protocol version.

HTTP Streaming

The client performs a HTTP persistent connection and watches changes in response text and the server prints chunk as message over the connection. A given URI should have http or https scheme and stream transport param. To establish read-only channel through GET method, the following host object is used:

  • EventSource: It works if browser supports EventSource from Server-Sent Events. If the browser is Safari 5 or 6, it works only when same origin connection is given. By reason of the spec's ambiguity, there is no way to determine whether a connection closed normally or not so that error event is not thrown even though the connection closed due to an error.
  • XMLHttpRequest: In case of same origin connection, it works without qualification. In case of cross origin, works if XMLHttpRequest supports CORS. However for both cases, if the browser is Internet Explorer, the version should be equal to or higher than 10 and if the browser is Opera, the version should be equal to or higher than 13.
  • XDomainRequest: It works if browser supports XDomainRequest, that is Internet Explorer 8-10, and xdrURL option is set.
  • iframe tag: It works if it's same origin connection and browser supports ActiveXObject, namely Internet Explorer 6-10. This transport differs from the traditional Hidden Iframe in terms of fetching a response text. The traditional transport expects script tags, whereas this transport periodically polls the response text.

To establish write-only channel through POST method, the following host object is used:

  • XMLHttpRequest: In case of same origin, it works without qualification. In case of cross origin, it works if browser supports CORS.
  • XDomainRequest: It works if browser supports XDomainRequest, that is Internet Explorer 8-10, and xdrURL option is set.
  • form tag: It works alwayas but makes a clicking sound.

HTTP Long Polling

The client performs a HTTP persistent connection and the server ends the connection with data. Then, the client receives it and performs a request again. A given URI should have http or https scheme and longpoll transport param. It is implemented by the following host object:

  • XMLHttpRequest: In case of same origin connection, it works without qualification. In case of cross origin, works if XMLHttpRequest supports CORS.
  • XDomainRequest: It works if browser supports XDomainRequest, that is Internet Explorer 8-10, and xdrURL option is set.
  • script tag: It works always.

To establish write-only channel through POST method, the following host object is used:

  • XMLHttpRequest: In case of same origin, it works without qualification. In case of cross origin, it works if browser supports CORS.
  • XDomainRequest: It works if browser supports XDomainRequest, that is Internet Explorer 8-10, and xdrURL option is set.
  • form tag: It works alwayas but makes a clicking sound.

Compatibility

The compatiblity of vibe.js depends on transport compatibility.

Browser

The browser support policy is the same with the one of jQuery 1.x.

Internet Explorer Chrome Firefox Safari Opera iOS Android
6+ (Current - 1) or Current (Current - 1) or Current 5.1+ 12.1x, (Current - 1) or Current 6.0+ 4.0+

Transport list in each cell is ordered by recommendation. As to ws, a word in cell means WebSocket protocol. So in order to use ws, the server has to be able to support that protocol. As to stream and longpoll, a word in cell means the host object used to establish a read-only channel.

Browser Version WebSocket HTTP Streaming HTTP Long Polling
Internet Explorer 11 rfc6455 XMLHttpRequest XMLHttpRequest
10 rfc6455 XMLHttpRequest XMLHttpRequest
8 XDomainRequest2, iframe1 XMLHttpRequest1, XDomainRequest2, script
6 iframe1 XMLHttpRequest1, script
Chrome 25 rfc6455 EventSource XMLHttpRequest
Firefox 11 rfc6455 EventSource XMLHttpRequest
Safari 7.0 rfc6455 EventSource XMLHttpRequest
6.0 rfc6455 EventSource1, XMLHttpRequest XMLHttpRequest
5.1 hixie-76 EventSource1, XMLHttpRequest XMLHttpRequest
Opera 15 rfc6455 EventSource XMLHttpRequest
12.10 rfc6455 EventSource XMLHttpRequest
iOS 7.0 rfc6455 EventSource XMLHttpRequest
6.0 EventSource1, XMLHttpRequest XMLHttpRequest
Android 4.4 rfc6455 EventSource XMLHttpRequest
4.0 XMLHttpRequest XMLHttpRequest

Note

  • 1: only availabe in same origin connection
  • 2: xdrURL option required.

Node.js

Node.js lower than 0.10 may work.

Version ws stream longpoll
0.10 rfc6455 EventSource XMLHttpRequest

Quirks

The vibe.js always has tried to deal with any quirks in non-invasive way. However, it is not possible sometimes.

The browser limits the number of simultaneous connections

Applies to: HTTP transport

According to the HTTP/1.1 spec, a single-user client should not maintain more than 2 connections. This restriction actually varies with the browser. If you consider multiple topics to subscribe and publish, utilize the custom event in a single connection.

Pressing ESC key aborts the connection

Applies to: Firefox less than 20

One of default behaviors of pressing ESC key in Firefox is to cancel all open networking requests fired by XMLHttpRequest, EventSource, WebSocket and so on. The workaround is to prevent that behavior, and its drawback is that the user can't expect the default behavior of pressing ESC key. Fixed in Firefox 20.

// With jQuery
$(window).keydown(function(event) {
    if (event.which === 27) {
        event.preventDefault();
    }
});

Sending an event emits a clicking sound

Applies to: cross-origin HTTP connection on browsers not supporting CORS

If a given url is cross-origin and the browser doesn't support CORS such as Internet Explorer 6, an invisible form tag is used to send data to the server. Here, a clicking sound occurs every time the form is submitted. There is no workaround.