Permalink
Browse files

Added a bunch of documentation

  • Loading branch information...
1 parent d70ce03 commit e8b1600a94eb638b17df5b7cb884da44d7a4b409 @jcwilk committed May 16, 2011
Showing with 48 additions and 18 deletions.
  1. +15 −0 README
  2. +33 −18 server.js
View
15 README
@@ -1 +1,16 @@
Push server in Node.js
+
+The purpose of this project is to make a very lightweight, flexible, easy to understand messaging server for getting data broadcasted
+to in-browser flash-free websockets-free javascript-only clients in real time using long-poll.
+
+The advantage of this approach rather than using flash or websockets is that it maximizes your compatibility, makes all of your clients
+behave the same way, and if your application can be optimized for the slightly jerky behavior of long poll it can be a very good option.
+
+The way it works is the push.js file which is used by the client page is passed via javascripts some channels to listen on.
+For each of these channels, a connection is made to /m/channel-name.json?s=50 with a GET.
+If there is new data on that channel since sequence 50, it is immediately returned.
+If sequence 50 indicates the client is up to date with the most current data, then the connection is added to the listener pool and the client waits for the request to return
+
+Whenever the server wants to broadcast messages...
+The application server posts data to /m/channel-name.json
+The push server appends the data to the channel and triggers any listener's callbacks which are waiting for data on that channel
View
@@ -8,44 +8,52 @@ var util = require('util'),
path = require('path'),
publicRoot = path.join(path.dirname(__filename), 'public');
+//Returns a new channel
+//A channel is basically just an indexed queue of json messages
+//External clients will typically be syncing their history of messages with these channel queues
+//Adding a message to a channel queue is synonymous with broadcasting that message to listeners of that channel
function channelFactory(){
var callbacks = [];
var messages = [];
+ //get array of all the messages since +start+
function messagesSince(start){
- var mes = messages.slice(start+1);
-// logInspect(messages);
-// logInspect('------------------------------');
-// logInspect(mes);
- return mes;
+ return messages.slice(start+1);
}
+ //get the current sequence number of the head of the channel
function currentSequence(){
return messages.length-1;
}
+ //depending on the sequence number, queue the +callback+ for later or process it now
function processCallback(callback){
- if(callback.sequence == currentSequence()){
+ if(callback.sequence == currentSequence()){ //they're up to date, add them to the listeners pool to wait for new data
callbacks.push(callback)
- } else {
+ } else { //they're out of date, run the callback right away and give it the data they're missing
callback({data: messagesSince(callback.sequence),
sequence: currentSequence()})
}
}
+ //handle a listener +callback+ synced to the channel up to +sequence+
function read(sequence, callback){
- if(sequence === undefined || sequence > currentSequence()){
+ if(sequence === undefined || sequence > currentSequence()){ //the sequence is missing or screwed up, mark them as missing all data
callback.sequence = -1
- } else {
+ } else { //assign the sequence to the callback
callback.sequence = sequence
}
- processCallback(callback);
+ processCallback(callback)
}
+ //add +data+ to the channel and trigger all the listeners to be processed
function send(data){
messages.push(data);
- var callbacksToProcess = callbacks;
- callbacks = [];
+ var callbacksToProcess = callbacks; //clone the listener queue
+ callbacks = []; //reset the listener queue
+
+ //go through each of the listeners and reprocess them,
+ //potentially adding them back into the reset queue if there's nothing new
while(callbacksToProcess.length > 0){
var callback = callbacksToProcess.shift();
processCallback(callback);
@@ -55,6 +63,9 @@ function channelFactory(){
return {read: read, send: send}
}
+//Returns a channel manager
+//A channel manager is a read-or-create associative array of channels
+//The server should use one of these to keep track of the active channels
function channelManagerFactory(){
var channels = {};
@@ -67,29 +78,33 @@ function channelManagerFactory(){
}
function main(){
- var channelMan = channelManagerFactory();
+ var channelMan = channelManagerFactory(); //set up a channel manager
- var server = require('./lib/node-router').getServer();
+ var server = require('./lib/node-router').getServer(); //set up the http server
+ //set up the long-poll channel serving GET action
server.get(new RegExp("^/m/([^?]*).json$"),function(req,res,match){
var query = url.parse(req.url,true).query;
- var lastMessage = query.s;
+ var lastMessage = query.s; //pull out the index that the listener is synced up to
if(lastMessage !== undefined) lastMessage = parseInt(lastMessage);
var callback = query.callback;
- channelMan(match).read(lastMessage, function(data){
+ channelMan(match).read(lastMessage, function(data){ //return the data when there's data since lastMessage index
res.simpleText(200,callback+'('+JSON.stringify(data)+')')
})
});
+ //post new data to a channel
server.post(new RegExp("^/m/([^?]*).json$"),function(req,res,match,data){
- channelMan(match).send(data);
- res.simpleJson(200,data);
+ channelMan(match).send(data); //add the posted data to the channel
+ res.simpleJson(200,data); //return the posted data as json for debugging reasons
},'json');
+ //get the client-side javascript file for abstracting the push server into a javascript object
server.get('/push.js',function(req,res){
paperboy.deliver(publicRoot,req,res)
});
+ //listen on 3000, this is ignored by the production servers
server.listen(3000);
}
main();

0 comments on commit e8b1600

Please sign in to comment.