Skip to content

Instantly share code, notes, and snippets.

@shimondoodkin
Created November 14, 2012 13:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shimondoodkin/4072034 to your computer and use it in GitHub Desktop.
Save shimondoodkin/4072034 to your computer and use it in GitHub Desktop.
nodejs express 3 integration of - socket.io with sessions and sessionid handshake
/**
* Module dependencies.
*/
var express = require('express')
// , routes = require('./routes')
// , user = require('./routes/user')
, http = require('http')
, path = require('path');
var io = require('socket.io')
, sio, sio_client_on
, MemoryStore = express.session.MemoryStore
, sessionStore = new MemoryStore();//http://nodetoolbox.com/packages/mysql-session-store or redis or mongodb if you like i simply have mysql installed
// also possible to have a db store for the handshake data for socket io
// User validation
var auth = express.basicAuth(function(user, pass) { return (user=="mypass"&&pass=="mypass"); },'Secret Area');
//example: app.get('/', auth , function(req,res){})
var app = express();
app.configure(function(){
app.set('port', process.env.PORT || 5052);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
//app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.session({store: sessionStore , secret: 'secret' , key: 'express.sid',
//maxAge :24*3600000 //1 Hour * 24
}));
/*
app.all('/', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Origin, X-File-Name, Content-Type, Cache-Control");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
next();
});
*/
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
});
app.configure('development', function(){
app.use(express.errorHandler());
});
// Store imageData buffer 1x1 pixel transparent gif file
gif_1x1_buffer = new Buffer("R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==", encoding='base64');
app.get('/keepsession.gif', function(req,res){
res.writeHead(200,
{
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires':"Tue, 01 Jan 2000 12:12:12 GMT",
'Content-Type': 'image/gif'
});
res.write(gif_1x1_buffer.toString('binary'), 'binary');
res.end();
});
app.get('/', function(req,res){
res.writeHead(200,
{
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Content-Type': 'text/html',
'Expires':"Tue, 01 Jan 2000 12:12:12 GMT"
});
res.end("It Works! :-)<script>location.href='chat.html'</script>");
});
//app.get('/', routes.index);
//app.get('/users', user.list);
// excaption handling
process.on('uncaughtException', function (err) {
console.log('Caught exception: ' + err.stack);
});
// socket io
var server = http.createServer(app);
sio = io.listen(server);
sio.set('log level', 1);
sio.secret_keyword='server'
var utils = require('express/node_modules/connect/lib/utils')
var cookie = require('express/node_modules/cookie');
function parseCookie(cookies){
var secret='secret';
var reqcookies = {};
var reqsignedCookies = {};
if (cookies)
{
try
{
reqcookies = cookie.parse(cookies);
if (secret)
{
reqsignedCookies = utils.parseSignedCookies(reqcookies, secret);
var obj = utils.parseJSONCookies(reqsignedCookies);
reqsignedCookies = obj;
}
reqcookies = utils.parseJSONCookies(reqcookies);
}
catch (err)
{
errstatus = 400;
console.log("err",err.stack)
}
}
return {cookies:reqcookies,signedCookies:reqsignedCookies};
};
//var recreate_lost_sessions=true; // unsecure then it accepts the socket with a new empty session.
sio.set('authorization', function (handshake_data, accept) {
var data = handshake_data;
//console.log(data.headers);
if (data.headers.cookie) {
//console.log(data);
//console.log(data.headers.cookie);
data.cookie = parseCookie(data.headers.cookie,'secret');
data.sessionID = data.cookie.signedCookies['express.sid']||data.cookie.cookies['express.sid'];
//console.log(data.headers.cookie);
// (literally) get the session data from the session store
sessionStore.load(data.sessionID, function (err, session) {
if (err) {
// if we cannot grab a session, turn down the connection
accept(err.message, false);
} else if (session==undefined) {
// if we cannot grab a session, turn down the connection
accept('session not found', false);
} else {
data.session = session;
if(!data.session.userid)
{
data.session.userid=require('express/node_modules/connect/lib/utils').uid(24);// expose user's session it is unsecure lets have another id
data.session.save(function(){})
}
accept(null, true);
}
});
} else {
// Check to see if the conection is made from the server
// ~ auth with token
if (data.query.secret_keyword &&
(data.query.secret_keyword === sio.secret_keyword))
{
return accept(null, true);
}
return accept('No cookie transmitted.', false);
}
});
sio.sockets.on('connection', function (socket) {
var hs = socket.handshake;
//console.log('connection',socket.handshake);
console.log('A socket with sessionID ' + hs.sessionID + ' connected!');
// setup an inteval that will keep our session fresh
var intervalID = setInterval(function () {
// reload the session (just in case something changed,
// we don't want to override anything, but the age)
// reloading will also ensure we keep an up2date copy
// of the session with our connection.
if(hs&&hs.session)hs.session.reload( function () {
// "touch" it (resetting maxAge and lastAccess)
// and save it back again.
hs.session.touch().save();
});
}, 60 * 1000);
socket.on('disconnect', function () {
console.log('A socket with sessionID ' + hs.sessionID + ' disconnected!');
// clear the socket interval to stop refreshing the session
clearInterval(intervalID);
});
setup_socket_io(socket);
var client=socket;
sio_client_on.connection.apply(client,arguments);
});
function setup_socket_io(client)
{
Object.keys(sio_client_on)
.forEach(function(eventName){
var f=sio_client_on[eventName];
client.on(eventName,function(){f.apply(client,arguments)});});
}
//// end socket io
/*
// some service can http get a url to notify clients
app.get('/refresh', function (req, res) {
res.end('ok');
sio.sockets.emit('eval','if(refreshdata)refreshdata()');
});
*/
/******************/
var chatdata=[];
var xss = require('xss');
xss.whiteList.iframe=['style', 'class', 'height', 'width', 'border', 'src', 'allowfullscreen', 'frameborder']
xss.whiteList.object=['width', 'height']
xss.whiteList.param=['name', 'value']
xss.whiteList.embed=['src', 'type','width', 'height','allowscriptaccess','allowfullscreen']
xss.whiteList.font=['size','face']
//// setup socket io events
sio_client_on=
{
'connection': function () {
var client=this;
if(!client.handshake.session.nick)
{
client.handshake.session.nick="John"+Math.round(Math.random()*1000);
client.handshake.session.save(function(){})
}
client.emit('chat/full',chatdata);
client.join('chat/0');
//client.join('chat/1');
sio_client_on.users.call(this);
},
'disconnect': function () {
var client=this;
sio_client_on.users.call(this,[client.id]);
},
'users': function (skip) {
var client=this;
var myrooms=sio.sockets.manager.roomClients[client.id];
var rooms=[];
for(var aroom in myrooms)
{
if(aroom.indexOf('/chat/')==0)
{
var introom=parseInt(aroom.substr(6));
rooms[introom]={};
sio.sockets.clients(aroom.substr(1)).forEach(function(a){
var client_handshake=sio.sockets.manager.handshaken[a.id];
if(!rooms[introom][client_handshake.session.userid])
{
if(skip)
{
if(skip.indexOf(a.id)==-1)
rooms[introom][client_handshake.session.userid]=client_handshake.session.nick;
}
else
rooms[introom][client_handshake.session.userid]=client_handshake.session.nick;
}
});
}
sio.sockets.in(aroom.substr(1)).emit('chat/users',rooms);
}
},
'chat/message': function (message)
{
if(typeof message!='object') return;
if(typeof message.room!='number') return;
var client=this;
message.nick=client.handshake.session.nick||"nonick";
message.date=(new Date()).getTime();
message.id=message.room+'_'+message.date;
var xssoptions = {
//whiteList: {}, // if not specified, use the default configuration, refer xss.whiteList,
//onTagAttr: function () {}, //​if not specified, use the default configuration, you can reference xss.onTagAttr
//onIgnoreTag: function () {} // If you do not specify the default configuration, refer xss.onIgnoreTag,
};
message.text=xss(message.text,xssoptions);
if(!chatdata[message.room])chatdata[message.room]=[];
//do not insert multiple rows of same number
chatdata[message.room].push(message);
sio.sockets.in('chat/'+message.room).emit('chat/message',message);
//Broadcasting means sending a message to everyone else except for the socket that starts it.
//client.broadcast('event',data);
//sio.sockets.in(req.sessionID).broadcast('Man, good to see you back!');
}
}
server.listen(app.get('port')); console.log("Express server listening...");
//console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
var socket =io.connect("http://123.123.123.123:12345/",{ 'connect timeout': 1000 , 'try multiple transports': false});
socket.on('connect_failed', function (reason) {
if(reason.indexOf('handshake error')!=-1)
{
var o={}
o.img= new Image()
o.img.src="http://123.123.123.123:12345/keepsession.gif?"+((new Date).getTime())
o.img.onload=function(){setTimeout(function(){delete o.img;delete o;},0);}
}
console.log('unable to connect to namespace', reason);
})
.on('connect', function () {
console.log('sucessfully established a connection with the namespace');
})
.on('connect', function () {
console.log('connection lost');
});
// just one event on client:
socket.on('eval', function (d) {
eval(d);
});
socket.on('message', function (d) {
alert(d);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment