Created
February 28, 2015 02:44
-
-
Save artgillespie/1990f5d2efda4c51947d to your computer and use it in GitHub Desktop.
Integrating socket.io with Express.js sessions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*jslint unparam:true, node:true*/ | |
'use strict'; | |
var express = require('express'), | |
session = require('express-session'), | |
cookie = require('cookie'), | |
cookieParser = require('cookie-parser'); | |
var app = express(); | |
/* | |
* This is the dance you need to serve the /socket.io endpoint | |
* with expressjs. Source: http://socket.io/docs/#using-with-express-3/4 | |
*/ | |
var http = require('http').Server(app); | |
var io = require('socket.io')(http); | |
/* | |
* We'll need the cookie name in our socket.io code. It's fine | |
* to use express-session default (`connect.sid`) but best | |
* to make it explicit. | |
*/ | |
var SESSION_COOKIE_NAME = 'session.id'; | |
var SESSION_COOKIE_SECRET = 's3kkr1t'; | |
/* | |
* This is what we'll use to integrate our session.io code with | |
* express-session. Since we only use `set` and `get` on the store, | |
* the same approach should work with any session store—`redis-connect`, | |
* your own custom session storage, etc. | |
*/ | |
var sessionStore = new session.MemoryStore(); | |
/* | |
* Set up `express-session` | |
*/ | |
app.use(session({ | |
store: sessionStore, | |
name: SESSION_COOKIE_NAME, | |
secret: SESSION_COOKIE_SECRET, | |
saveUninitialized: true, | |
resave: true | |
})); | |
/* | |
* Render index.jade. | |
*/ | |
app.get('/', function(req, res) { | |
if (req.session.count) { | |
req.session.count++; | |
} else { | |
req.session.count = 1; | |
} | |
res.render(app.path() + 'index.jade', {count: req.session.count}); | |
// notify any clients interested in the session's count | |
io.to(req.session.id).emit('count', req.session.count); | |
}); | |
/* | |
* In this example, we require an existing Express session and pass on errors | |
* if we don't find one. Obviously you can do anything you want here. | |
* The takeaway is: | |
* - Access the underlying HTTP request at `socket.request` to check for | |
* cookies, auth headers, whatever. | |
* - call `next(new Error('...'));` if you don't like what you see. The | |
* connection will never complete. (clients will receive an error) | |
* - call `next();` if everything's cool and you want the connection to proceed. | |
*/ | |
io.use(function(socket, next) { | |
// turn the raw cookie header into an object | |
var cookies = cookie.parse(socket.request.headers.cookie); | |
// make sure we have a session id | |
if (!cookies[SESSION_COOKIE_NAME]) { | |
return next(new Error('valid session required')); | |
} | |
// note this is the *signed* cookie | |
var ssid = cookies[SESSION_COOKIE_NAME]; | |
// check the signed cookie | |
var sid = cookieParser.signedCookie(ssid, SESSION_COOKIE_SECRET); | |
// `signedCookie` will return the same string it was passed if it fails | |
// so if our signed and 'un-signed' cookie are the same value, something's wrong | |
if (ssid === sid) { | |
// not a signed cookie | |
return next(new Error('valid session required')); | |
} | |
// now use the session store we set up earlier to find the session object | |
// associated with `sid` | |
sessionStore.get(sid, function(err, sess) { | |
if (err || !sess) { | |
return next(new Error('valid session required')); | |
} | |
// just store the session id on the socket, not the session object itself | |
// why? because the session would quickly become stale... you want to | |
// `sessionStore.get(...)` the session using the sessionId every time you | |
// use it. (and `sessionStore.set(...)` it every time you mutate it. | |
socket.sessionId = sid; | |
// ... and hand off | |
return next(); | |
}); | |
}); | |
io.on('connection', function(socket) { | |
// have the client join its session's 'room' | |
socket.join(socket.sessionId, function(err) { | |
// whenever we receive an `incr` message, fetch the socket's session, | |
// fetch the session, increment `count`, and save the session. | |
socket.on('incr', function(msg) { | |
// get the session | |
sessionStore.get(socket.sessionId, function(err, sess) { | |
if (err || !sess) { | |
// error-handling is for chumps! | |
return; | |
} | |
// increment the count | |
if (sess.count) { | |
sess.count++; | |
} else { | |
sess.count = 1; | |
} | |
// IMPORTANT! save the session. this happens automatically in express handlers, but | |
// not here | |
sessionStore.set(socket.sessionId, sess, function(){}); | |
// notify anyone that's interested in the session | |
io.to(socket.sessionId).emit('count', sess.count); | |
}); | |
}); | |
}); | |
}); | |
var server = http.listen(3000, function() { | |
var host = server.address().address; | |
var port = server.address().port; | |
console.log('Example app listening at http://%s:%s', host, port); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
doctype html | |
html(lang='en') | |
head | |
title= SocketSession | |
script(src='https://code.jquery.com/jquery-2.1.3.min.js') | |
script(src='/socket.io/socket.io.js') | |
script(type='text/javascript'). | |
$(function() { | |
var socket = io(); | |
socket.on('count', function(msg) { | |
$('#count').text(msg); | |
}); | |
$('#increment-btn').click(function(e) { | |
socket.emit('incr', {}); | |
}); | |
}); | |
body | |
h1 Hello, SocketSession | |
h4(id='count') #{count} | |
button(id='increment-btn') Increment | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Integrating Express.js Sessions with Socket.io
I had what I thought was a simple requirement: Integrate an express app's sessions with socket.io connections. I found very little current (socket.io 1.0 + express 4.x) information via google and stack overflow, so I thought I'd break out the results of several hours of trial and error and share them here. I'm relatively new to node.js, express, and socket.io—any feedback on violated idioms, bugs, etc. gratefully received.