• Stars
    star
    287
  • Rank 144,232 (Top 3 %)
  • Language
    CoffeeScript
  • Created about 13 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An implementation of a google browserchannel server in node.js

A BrowserChannel server.

Build Status

tldr; Its like socket.io, but it scales better and it has fewer bugs. It just does long polling. It also doesn't support websockets and doesn't support cross-domain requests out of the box.

Note: Websocket support is now reasonably universal. Strongly consider using raw websockets for new projects.

BrowserChannel is google's version of socket.io from when they first put chat in gmail. Unlike socket.io, browserchannel guarantees:

  • Messages will arrive in order
  • Messages will never arrive on the server after a connection has closed
  • The mail will always get through on any browser that google talk works on, which is all of them.

node-browserchannel:

  • Is compatible with the closure library's browserchannel implementation
  • Is super thoroughly tested
  • Works in IE5.5+, iOS, Safari, Chrome, Firefox, etc.
  • Works in any network environment (incl. behind buffering proxies)

Use it

# npm install browserchannel

Browserchannel is implemented as connect middleware. Here's an echo server:

var browserChannel = require('browserchannel').server;
var connect = require('connect');

var server = connect(
  connect.static("#{__dirname}/public"),
  browserChannel(function(session) {
    console.log('New session: ' + session.id +
      ' from ' + session.address +
      ' with cookies ' + session.headers.cookie);

    session.on('message', function(data) {
      console.log(session.id + ' sent ' + JSON.stringify(data));
      session.send(data);
    });

    session.on('close', function(reason) {
      console.log(session.id + ' disconnected (' + reason + ')');
    });
      
    // This tells the session to disconnect and don't reconnect
    //session.stop();
    
    // This kills the session.
    //session.close();
  })
);

server.listen(4444);

console.log('Echo server listening on localhost:4444');

The client emulates the websocket API. Here is a simple client:

var BCSocket = require('browserchannel').BCSocket;

var socket = new BCSocket('http://localhost:4321/channel');
socket.onopen = function() {
  socket.send({hi:'there'});
};
socket.onmessage = function(message) {
  console.log('got message', message);
};

// later...
socket.close()

... Or from a website:

<html><head>
<script src='/channel/bcsocket.js'></script>
<script>
socket = new BCSocket('/channel');
socket.onopen = function() {
  socket.send({hi:'there'});
  socket.close();
};
socket.onmessage = function(message) {
  // ...
};
</script>

You can also ask the client to automatically reconnect whenever its been disconnected. - Which is super useful.

var BCSocket = require('browserchannel').BCSocket;
socket = new BCSocket('http://localhost:4321/channel', reconnect:true);
socket.onopen = function() {
  socket.send("I just connected!");
};

Differences from Websocket

  • You can send messages before the client has connected. This is recommended, as any messages sent synchronously with the connection's creation will be sent during the initial request. This removes an extra round-trip.
  • The send method can pass a callback which will be called when the message has been received. NOTE: If the client closes, it is not guaranteed that this method will ever be called.
  • Send uses google's JSON encoder. Its almost the same as the browser one, but {x:undefined} turns in to {x:null} not {}.

API

Server API

The server is created as connect / express middleware. You create the middleware by calling

var browserChannel = require('browserchannel').server;

var middleware = browserChannel(options, function(session) {
  ...
});
 
// Express
app.use(middleware);

The options object is optional. The following server options are supported:

  • hostPrefixes: Array of extra subdomain prefixes on which clients can connect. Even modern browsers impose per-domain connection limits, which means that when you have a few tabs open with browserchannel requests your connections might stop woroking. Use subdomains to get around this limit. For example, if you're listening for connections on example.com, you can specify hostPrefixes: ['a', 'b', 'c'] to make clients send requests to a.example.com, b.example.com and c.example.com.
  • base: The base URL on which to listen for connections. (Defaults to "/channel"). Think of the base URL as a URL passed into app.use(url, middleware).
  • headers: Map of additional response headers to send with requests.
  • cors: Set Access-Control-Allow-Origin header. This allows you to specify a domain which is allowed to access the browserchannel server. See mozilla documentation for more information. You can set this to '*' to allow your server to be accessed from clients on any domain, but this may open up security vulnerabilities in your application as malicious sites could get users to connect to your server and send arbitrary messages.
  • corsAllowCredentials: (Default false) Sets the Access-Control-Allow-Credentials header in responses. This allows cross-domain requests to send their cookies. You cannot do this if you set cors:'*'. To make this work you must also add the {crossDomainXhr:true} option in the client. See mozilla documentation for more information. Setting this is equivalent to setting headers:{'Access-Control-Allow-Credentials':true}.
  • keepAliveInterval: (Default 20000 = 20 seconds). Keepalives are sent periodically to make sure http connections aren't closed by eager clients. The standard timeout is 30 seconds, so sending keepalives every 20 seconds keeps the connection healthy. Time is specified in milliseconds
  • sessionTimeoutInterval: (Default 30 seconds). Amount of time we wait before evicting a client connection. Setting this lower will make the server notice disconnected clients more quickly. Setting it higher will make connections more resiliant to temporary network disconnection. Time specified in milliseconds.

Note that by default, CORS support is disabled. This follows the lead of browsers. Be very careful when enabling CORS & CORS credentials. You should explicitly whitelist sites from which your users will connect.

Setting hostPrefixes in production is important - if you don't enable host prefixes, browserchannel will stop working for a user once they have more than a couple of tabs open. Set DNS rules to make the extra prefixes all point to the same server / cluster.

Client sessions

Whenever a client connects, your middleware's method is called with the new session. The session is a nodejs event emitter with the following properties:

  • id: An ID that is unique to the session.
  • address: A string containing the IP address of the connecting client.
  • query: An object containing the parsed HTTP query of the initial connection. Any custom query parameters will be exposed on this object.
  • state: The current state of the connection. One of 'init', 'ok' and 'closed'. When the state is changed, the client will emit a state changed event with the new state and old state as event parameters.
  • appVersion: The client's reported application version, or null. You can use this to reject clients which are connecting from old versions of your client.

Sending messages

You can send messages to the client using client.send(data, callback). The data parameter will be automatically JSON.stringified. If specified, the callback will be called once the message has been acknowledged by the client.

Note: If you wrap a browserchannel connection in a nodejs stream, don't use the callback. Node streams will only allow one message to be in flight at a time. As a result, you'll get much lower message throughput than you otherwise should.

Receiving messages

Receive messages through the message event.

session.on('message', function(data) {
  // ...
});

The message will be a javascript object if you sent a javascript object using the client API.

Stopping and closing connections

Browserchannel has two different methods for closing client connections, session.stop and session.close. Both methods disconnect the client. The difference is that stop also tells the client not to reconnect. You should use close when a recoverable server error occurs, and stop when the client is in an unrecoverable invalid state.

For example, if an exception occurs handling a message from a client, you may want to call close() to force the client to reconnect. On the other hand, if a browser is trying to connect using an old version of your app, you should call stop(). In the browser, you can handle the stop message with a notice to refresh the browser tab.

Events

The client is an event emitter. It fires the following events:

  • close (reason): The client connection was closed. This will happen for a variety of reasons (timeouts, explicit disconnection from the client, disconnection from the server, etc). Once a client has closed, it is gone forever. If the client reconnects, it will do so by establishing a new session.
  • message (data): The server received a message from the client. The data object will be a javascript object.
  • state changed (newstate, oldstate): The client's state changed. Clients start in the 'init' state. They move to the 'ok' state when the session is established then go to the 'closed' state. If a client reconnects, they will create an entirely new session. init -> ok -> closed are the only three valid state transitions.

Client API

For the most part, the client API is identical to websockets.

var socket = new BCSocket(hostname, opts);

opts is optional, and if it exists it should be an object which can contain the following properties:

  • appVersion: Your application's protocol version. This is passed to the server-side browserchannel code, in through your session handler as session.appVersion
  • prev: The previous BCSocket object, if one exists. When the socket is established, the previous bcsocket session will be disconnected as we reconnect.
  • reconnect: Tell the socket to automatically reconnect when its been disconnected.
  • failFast: Make the socket report errors immediately, rather than trying a few times first.
  • crossDomainXhr: Set to true to enable the cross-origin credential flags in XHR requests. The server must send the Access-Control-Allow-Credentials header and can't use wildcard access control hostnames. See: http://www.html5rocks.com/en/tutorials/cors/#toc-withcredentials
  • affinity: Set to null to disable session affinity token passing.
  • affinityParam: Session affinity tokens are sent in the query string as the GET parameter a by default. Your application may override the variable name if there is a query string conflict.

There are a couple of differences from the websocket API:

  • You can (and are encouraged to) call send() using JSON objects instead of mere strings. JSON serialization is handled by the library, and works in all browsers.
  • You can send messages immediately before the session is established. This removes a roundtrip before client messages can arrive on the server.
  • Browserchannel sessions have reconnect support. You should register socket.onconnecting = function() {...} to send any messages which need to be sent as the session is established. This will be called both when the socket is first established and when the session reconnects.

Caveats

  • It doesn't do RPC.
  • Currently there's no websocket support. So, its higher bandwidth on modern browsers. On iOS you'll sometimes see a perpetual loading spinner in the top black bar.

How to rebuild the client

The client uses google's closure library & compiler. There's a couple small bugs that google still hasn't fixed in their library (and probably never will), so I have a patch file kicking around.

Rebuilding the client library is annoying, so I keep an up to date compiled copy in dist/.

  1. Download the closure library as a sibling of this repository

    cd ..
    git clone https://code.google.com/p/closure-library/
    git checkout -q df47692b1bacd494548a3b00b150d9f6a428d58a
    cd closure-library
    
  2. Download the closure compiler

    curl http://dl.google.com/closure-compiler/compiler-latest.tar.gz > compiler-latest.tar.gz
    tar -xvf compiler-latest.tar.gz
    mv compiler-latest/compiler.jar .
    
  3. Patch the library

    cd closure/
    patch -p0 < ../../node-browserchannel/closure-*.patch
    
  4. Build

    cd ../../node-browserchannel
    make
    

Caveats

Java ~1.7 is a hard requirement.

Building this project with Java ~1.6 will fail, and may even fail silently.

Known issue with latest closure-library.

Until the bug introduced in closure-library#83c6a0b9 is resolved upstream, use closure-library#df47692


License

Licensed under the standard MIT license:

Copyright 2011 Joseph Gentle.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

More Repositories

1

ShareJS

Collaborative editing in any app
JavaScript
4,929
star
2

noisejs

Javascript 2D Perlin & Simplex noise functions
JavaScript
1,569
star
3

diamond-types

The world's fastest CRDT. WIP.
Rust
1,194
star
4

Chipmunk-js

Port of slembcke/Chipmunk-Physics to Javascript
JavaScript
537
star
5

librope

UTF-8 rope library for C
C
254
star
6

statecraft

Manage state with finesse
TypeScript
177
star
7

sephsplace

My own version of r/place, done in a weekend
JavaScript
130
star
8

jumprope-rs

Rust
104
star
9

reference-crdts

Simple, tiny spec-compliant reference implementations of Yjs and Automerge's list types.
TypeScript
98
star
10

jumprope

Fast string editing in Javascript using skip lists
JavaScript
86
star
11

steamdance

A multiplayer steam-based CPU simulator
JavaScript
45
star
12

editing-traces

Real world text editing traces for benchmarking CRDT and Rope data structures
JavaScript
39
star
13

schemaboi

Mergable, efficient data schema for intercompatible apps
TypeScript
33
star
14

braid-protocol

TypeScript
28
star
15

textot.rs

Text operational transform library, for rust. Compatible with libot, ottypes/text.
Rust
26
star
16

sharedb

ShareJS server, in C.
C
24
star
17

appstate

CoffeeScript
24
star
18

replica

Local first application platform built using CRDTs
TypeScript
18
star
19

wangjs

A javascript Wang tile implementation
CoffeeScript
17
star
20

eg-walker-reference

Simple reimplementation of the diamond types sequence CRDT in simple, pure, unoptimized typescript.
TypeScript
16
star
21

mail-viewer

Pure browser email viewer
JavaScript
15
star
22

diamond-js

Javascript wrapper bindings for diamond types
14
star
23

fdb-tuple

Pure javascript FoundationDB tuple encoder and decoder
TypeScript
12
star
24

node-timerstub

Stubbed out timer objects for fast unit testing in nodejs
CoffeeScript
11
star
25

gmail-jmap

Simple JMAP wrapper for gmail
TypeScript
10
star
26

braid-db

Simple database prototype
Rust
10
star
27

unicount

JavaScript
9
star
28

react-static-example

JavaScript
9
star
29

mime-to-jmap

Tools to convert RFC2822 email messages to JMAP
C
9
star
30

unsplash

JavaScript
8
star
31

egwalker-paper

Eg-walker paper, experiments and data.
Rust
8
star
32

tanksalot

CoffeeScript
7
star
33

lightwave

Fork of Torben Weis's lightwave experimental branch - http://code.google.com/p/lightwave/
Go
7
star
34

TP2

An implementation of OT for text which supports TP2 (for p2p editing)
CoffeeScript
6
star
35

text-crdt-rust2

Rust
6
star
36

miniohm

JavaScript
6
star
37

simple-crdt-text

Simple typescript reference implementation for a simple RGA based plain text CRDT
TypeScript
6
star
38

axidraw

Python
5
star
39

livedb-foundation

CoffeeScript
5
star
40

dt-simple-wiki

World's simplest wiki on top of diamond types
JavaScript
5
star
41

crdt-examples

CRDT examples from a DWEB talk
JavaScript
5
star
42

glassbeadtimer

Svelte
4
star
43

killdinohitler

Stealth jam game
CoffeeScript
4
star
44

lovehot

Lua
4
star
45

keykitten

Stores your public key.
4
star
46

ARTIST

Michael Brough and Andi McClure's 10 second artist game, allowing a starting image
Lua
4
star
47

skiplistrs

Rust
4
star
48

map2

Tuple map type for javascript (a,b) -> x
TypeScript
3
star
49

gossip

Simple proof-of-concept gossip protocol for CRDTs
TypeScript
3
star
50

tp2stuff

CoffeeScript
3
star
51

set2

Tuple set type for javascript
JavaScript
3
star
52

resolvable

TypeScript
2
star
53

libot-swift

Swift
2
star
54

roboname

A simple robot-themed name generator for nodejs
CoffeeScript
2
star
55

braid-explorer

UI experiment with braid for data first software
TypeScript
2
star
56

space

Spaaaaaaaace
JavaScript
2
star
57

robot-party

Robots run robots.
CoffeeScript
2
star
58

streamtoiter

TypeScript
2
star
59

Attila

a little game prototype
CoffeeScript
2
star
60

wave-prototype

Simple prototype of a wave-like email service
TypeScript
2
star
61

braid-kernel

TypeScript
2
star
62

canvasproxy

CoffeeScript
2
star
63

space-server

C
2
star
64

census-couch

A simple couchapp site for exploring census data. Way incomplete.
JavaScript
1
star
65

state-client

TypeScript
1
star
66

Clickboss

Click on the boss.
CoffeeScript
1
star
67

FactoryFactoryImpl

The code from my minecraft machine that makes anything
1
star
68

braid-cli

JavaScript
1
star
69

mars2

JavaScript
1
star
70

boilerplate-sim

Boilerplate simulator reference implementation
CoffeeScript
1
star
71

ietf-sync-prototype

JavaScript
1
star
72

boilerplate-gif

CoffeeScript
1
star
73

mtgox

CoffeeScript
1
star
74

boilerplate-jit

CoffeeScript
1
star
75

raider

Simple ECS-style game prototype for multiplayer dungeon raid type things. https://home.seph.codes/public/game/
Rust
1
star
76

state-server

TypeScript
1
star
77

Cocontroller

Userland USB controller for XBox360 controllers on a mac.
Objective-C
1
star
78

boilerplate-compiler

CoffeeScript
1
star
79

visbindiff

CoffeeScript
1
star
80

rle-utils

Javascript utilities for internal run-length encoding
TypeScript
1
star
81

mars

CSS
1
star
82

boilerbot

CoffeeScript
1
star