summaryrefslogtreecommitdiffstats
path: root/README.md
blob: da5ddbdc9c39d67aef6174b4faf783e58036c6c1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
Bullet (discontinued)
=====================

This project is discontinued and has been archived. It has not been
updated in a few years and I am no longer using it nor do I have
the time to maintain it. If you are using it and would like me to
promote a fork or alternative based on Bullet, please send me an
email and I will oblige. Thanks for your interest!

The old README continues below.

Bullet is a Cowboy handler and associated Javascript library for
maintaining a persistent connection between a client and a server.

Bullet abstracts a general transport protocol similar to WebSockets.
Bullet will use a WebSocket if possible but will fallback to other
transports when necessary. If the client supports EventSource
connections then Bullet will use EventSource to send messages from the
server to the client and XHR for messages from the client to the
server. If EventSource is not available then Bullet will use XHR for
both directions, using long-polling for server-to-client messages.

A common interface is defined for both client and server to easily
facilitate the handling of such connections. Bullet additionally takes care
of reconnecting automatically whenever a connection is lost, and also
provides an optional heartbeat which is managed on the client side.

Dispatch options
----------------

Similar to any other handler, you need to setup the dispatch list before
you can access your Bullet handlers. Bullet itself is a Cowboy HTTP
handler that translates some of the lower-level functions into a
simplified higher-level interface.

The dispatch options for a Bullet handler looks as follow:

``` erlang
{[<<"path">>, <<"to">>, <<"bullet">>], bullet_handler,
	[{handler, my_stream}]}
```

Simply define this in your dispatch list and your handler will be
available and handled by Bullet properly.

The third element in the tuple ([{handler, my_stream}]) will be passed 
to init/4 as Opts, you can add your own options and get them using
lists:keyfind, for example if we define our handler as:

``` erlang
{[<<"path">>, <<"to">>, <<"bullet">>], bullet_handler,
	[{handler, my_stream}, {channel, "my channel"}]}
```

you can retrieve the channel value as follows:

``` erlang
init(_Transport, Req, Opts, _Active) ->
	{channel, Channel} = lists:keyfind(channel, 1, Opts),
	{ok, Req, #state{channel=Channel}}.
```

Cowboy handler
--------------

Similar to websocket handlers, you need to define 4 functions.
A very simple bullet handler would look like the following:

``` erlang
-module(stream_handler).
-export([init/4, stream/3, info/3, terminate/2]).

init(_Transport, Req, _Opts, _Active) ->
	{ok, Req, undefined_state}.

stream(Data, Req, State) ->
	{reply, Data, Req, State}.

info(_Info, Req, State) ->
	{ok, Req, State}.

terminate(_Req, _State) ->
	ok.
```

Of note is that the init/4 and terminate/2 functions are called
everytime a connection is made or closed, respectively, which can
happen many times over the course of a bullet connection's life,
as Bullet will reconnect everytime it detects a disconnection.

Note that you do not need to handle a heartbeat server-side, it
is automatically done when needed by the Bullet client as explained
later in this document.

You might have noticed the odd Active argument to init/4. It
indicates what type of connection we have. When Active == once,
we have a temporary connection that only allows one reply before
terminating. When Active == true, the connection allows any number
of replies. You can use this information to inform your session
process that it should send only 1 message, in the case of
Active == once, or that it can send messages whenever in the
case of Active == true.

You would typically use init/4 to inform your session process
that it can send you messages. In the same manner you can use
terminate/2 to inform it that the connection is going down.

Bullet handlers should only contain transport related code,
logic should be done in your session process if any, or other
parts of your application. Bullet processes should be considered
temporary as you never know when a connection is going to close
and therefore lose your State.

Client-side javascript
----------------------

Bullet requires the jQuery library to be used. Initializing a
bullet connection is quite simple and can be done directly from
a document.ready function like this:

``` js
$(document).ready(function(){
	var bullet = $.bullet('ws://localhost/path/to/bullet/handler');
	bullet.onopen = function(){
		console.log('bullet: opened');
	};
	bullet.ondisconnect = function(){
		console.log('bullet: disconnected');
	};
	bullet.onclose = function(){
		console.log('bullet: closed');
	};
	bullet.onmessage = function(e){
		alert(e.data);
	};
	bullet.onheartbeat = function(){
		bullet.send('ping');
	}
});
```

Use the WebSocket (ws:) form for your bullet URLs and Bullet
will change the URL as needed for non-WebSocket transports.

Use the standard (http:) form for your bullet URLs and Bullet
will only try non-WebSocket transports.

The `$.bullet` function takes an optional second 'options' object.
The following properties are supported:

| Name                   | Default | Description                         |
| ---------------------- | --------|------------------------------------ |
| disableWebSocket       | false   | Never make WebSocket connections.   |
| disableEventSource     | false   | Never make EventSource connections. |
| disableXHRPolling      | false   | Never fallback to XHR long polling. |

Note that if EventSource is enabled and chosen as the underlying
transport, XHR will be used for client-to-server messages.

Bullet works especially well when it is used to send JSON data
formatted with the jQuery JSON plugin.

``` js
bullet.send($.toJSON({type: 'event', data: 'hats!'}));
```

When receiving JSON you would typically receive a list of events,
in which case your onmessage handler can look like this, assuming
you previously defined a handlers function array for all your events:

``` js
	bullet.onmessage = function(e){
		var obj = $.parseJSON(e.data);
		for (i = 0; i < obj.length; i++){
			handlers[obj[i].type](obj[i]);
		}
	};
```