Socket.io: How to uniquely identify a connection on client and server? - javascript

My app supports multiple socket.io clients from the same host (IP address). For diagnostics, I need to be able correlate client and server logs to identify which client the server is talking to. Does socket.io provide a way to uniquely identify a connection?

What I do is that within /routes/socket.js, I have these added to my requires:
var thisApp = require('../app');
var cookieSig = require('express/node_modules/cookie-signature');
var cookie = require('cookie');
var connect = require('express/node_modules/connect')
, parseSignedCookie = connect.utils.parseSignedCookie;
This answer assumes you have a session store of some kind that you can access via thisApp.thisStore. In my case, in the main app.js, I set up a session store using kcbanner's connect-mongo (available via npm and github.com) using a MongoDB back-end hosted on MongoLab for mine. In the session store, for each session, you can have a unique username, or some other identifier that goes along with that session. Really, you can tack any data you want to that session. That's how you'd tell them apart.
The code I use looks like this:
module.exports = function (socket) {
socket.on('taste:cookie', function (data, callback) {
console.log("taste:cookie function running");
//get the session ID
var sid = data.sid;
sid = parseSignedCookie(sid['connect.sid'], "mySecret");
console.log("sid: ",sid);
//get the handshake cookie
var hssid = cookie.parse(socket.handshake.headers.cookie);
hssid = parseSignedCookie(hssid['connect.sid'], "mySecret");
console.log("hssid: %s",hssid);
if(sid) {
if(sid['connect.sid']) {
sid = sid['connect.sid'].slice(2);
console.log("sliced the sid: %s",sid);
sid = cookieSig.unsign(sid, "mySecret");
hssid = sid;
}
if(hssid != sid) {
console.log("browser cookie not set right; rectifying...");
data.sid = hssid;
sid = hssid;
}
else console.log("browser cookie was set right");
}
thisApp.thisStore.get(sid, function(err, gotsession) {
if(err || !gotsession) {
//handle error
return;
} else {
if(gotsession.username) {
callback(0, {username:gotsession.username});
}
else callback(1, {username:""});
}
});
});
Maybe there's a more elegant way to do this, but this does work.

You can use session+cookies: Here's a library that you can use or learn from: session-socket.io.
You'll find plenty of examples on their README page.

Related

NodeJS Websocket use variable in HTML (with express?)

I have been searching for two days now looking for a solution that might work for me. Sadly I have only seen examples and guides on how to setup a websocket server (that sends messages back and forth to clients) and a websocket client (that resides in browser). None of these really work for me, and I am not sure how to achieve what I want here.
Basically I have the following websocket:
require('dotenv').config()
const WebSocket = require('ws');
var connection = new WebSocket('ws://XXX');
connection.onopen = function () {
connection.send(JSON.stringify({"authenticate":process.env.API}));
connection.send(JSON.stringify({"XXX":"YYY"}));
connection.send(JSON.stringify({
"db" : "unique_id",
"query" : {
"table" : "users"
}
}));
};
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
var myResponse = JSON.parse(e.data);
var qList = myResponse.results;
};
What I want to do is have my nodeJS-script running, for example an express script with a html page, that also includes the response from onmessage. Why I am complicating this instead of just using the websocket client-side is that I cannot send my auth-code publicly.
Hope I have been clear enough, let me know if you are unsure of my question!
PS. If you think I would be better off using another websocket-script such as Socket.io - I have been looking at them and have not gotten much wiser sadly.
You have a lot of options. Probably the easiest is to export the connection. At the bottom of the file, e.g. module.exports = connection
Then in the express application, import the connection, e.g. const connection = require('./path/connection');
Then make a function that calls itself at a given interval and sends the appropriate message.
Then within the Express app you can use something like connection.on('message', (data, flags) => // do stuff);
Your other option is to create a store object. E.g.
// ./store.js
class store {
constructor() {
this.wsMaterial = {};
}
add(wsInfo) {
this.wsMaterial[key] = wsInfo
}
get store() {
return this.wsMaterial
}
}
module.exports = new store()
Then import the store and updated it, e.g.
// ./websocket file
const store = require('./store');
...
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
var myResponse = JSON.parse(e.data);
var qList = myResponse.results;
store.add(qList)
};
Then from Express...
// ./express.js
const store = require('./store');
store.get // all of your stuff;

Express - socket.io - session. Refer to user id as socket id

I am using express-socket.io-session. I think I was able to setup the basic config by seeing the tutorials:
//BASIC CONFIG?
var clients = [];
var session = require("express-session")({
secret: 'some key',
resave: true,
saveUninitialized: true
});
var sharedsession = require("express-socket.io-session");
app.use(session);
io.use(function(socket, next){
next();
});
io.use(sharedsession(session, {
autoSave:true
}));
io.on('connection', function(socket) {
console.log("CLIENT CONNECTED");
var session = socket.handshake.session;
clients.push(socket);
socket.on('disconnect', function() {
console.log("CLIENT DISCONNECTED");
});
});
What I want to be able to do now is to refer to a specific client socket not by the socket but by the session id that should be assigned to that socket. When a user logins this happens:
req.session.user_id = user_id;
//(user_id is equal to DB {0,1,2,3...} ids
I was able to send sockets to specific clients when I did this:
clients[0].emit("to_do",info); // I don't know who is client index 0 compared to the login reference...
I would like to be able to do this or similar:
user_id = 3; // which would have a socket assigned
clients(user_id).emit("to_do",info);
That would mean each client would have a socket assigned to its previously assigned id. How could I do this so I could specify the socket by that id? I am not experienced at all with all of this so sorry for any big mistakes. Thanks
Your problem can be solved by each socket joining a group named after it's id:
socket.join(socket.id);
io.sockets.in(socket.id).emit('to_do', info);
//or
io.sockets.in(clients[0].id).emit('to_do', info);
Well I solved this out by iterating through the clients list and seeing which one had the socket I wanted
I ran into a similar issue, when using express-socket.io-session the user ID in socket.handshake.session.passport changes when a new user login, I used the below to solve it.
var userID;
if (!userID){
userID = socket.handshake.session.userID = socket.handshake.session.passport['user'];
}

Get subdomain and query database for results - Meteor

I am pretty new to Meteor now.
I want to:
get the sub-domain of the url
check if a client exists matching the sub-domain
if client exists query the database and get some results (say client settings) from the database.
I am sure that this would be a piece of cake if we use MongoDB, however, we have to move an existing application (built on PHP) that has MySQL backend.
I have found a package numtel:mysql for meteor and I have added it to the project.
Here is the source code written so far:
if(!Session.get('client_details')) {
var hostnameArray = document.location.hostname.split('.');
if(hostnameArray[1] === "localhost" && hostnameArray[2] === "local") {
var subdomain = hostnameArray[0];
}
if(subdomain) {
currentClientDetails = new MysqlSubscription('getClientDetailsFromUrl', subdomain).reactive();
Tracker.autorun(function() {
if(currentClientDetails.ready()) {
if(currentClientDetails.length > 0) {
var clientDetails = currentClientDetails[0];
Session.setPersistent('client_details', clientDetails);
var clientId = clientDetails.id;
if(!Session.get('client_settings')) {
clientSettings = new MysqlSubscription('clientSettings', clientId).reactive();
Tracker.autorun(function() {
if(clientSettings.ready()) {
if(clientSettings.length > 0)
Session.setPersistent('client_settings', clientSettings[0]);
else
Session.setPersistent('client_settings', {});
}
});
}
}
}
});
}
}
the session.setPersistent comes from u2622:persistent-session to store Sessions on client side
and here is the publish statement:
Meteor.publish("getClientDetailsFromUrl", function(url) {
if(typeof url === undefined) {
return;
}
var clientDetails = Meteor.readLiveDb.select(
'select * from clients where client_url = "'+ url +'"',
[{table: 'clients'}]
);
return clientDetails;
});
Meteor.publish("clientSettings", function(clientId) {
if(typeof clientId === undefined) {
throw new error('ClientId cannot be null');
return;
}
var clientSettings = Meteor.readLiveDb.select(
'select * from client_settings where client_id = ' + clientId, [{
table: 'client_settings'
}]);
return clientSettings;
});
and the database is initiated as
Meteor.readLiveDb = new LiveMysql(<MySQL Settings like host, user, passwords, etc>);
Problem
I get client_details into the session successfully, however, cannot get client_settings into the session. End up with an error:
Exception from Tracker recompute function:
Error: Subscription failed!
at Array.MysqlSubscription (MysqlSubscription.js:40)
at app.js?6d4a99f53112f9f7d8eb52934c5886a2b7693aae:28
at Tracker.Computation._compute (tracker.js:294)
at Tracker.Computation._recompute (tracker.js:313)
at Object.Tracker._runFlush (tracker.js:452)
at onGlobalMessage (setimmediate.js:102)
I know the code is messy and could get a lot better, please suggestions welcome

express.js parsing cookies?

I am trying to use an old library balloons.io as a base for a chat app, but it's quite out dated, in this particular code I am trying to figure out how to use express 4x to parse the cookie to get an sid without getting it from the req.session
Since express 4x is not using connect anymore how can I do something similar to the below but in the new express version?
/*
* Module dependencies
*/
var sio = require('socket.io')
, parseCookies = require('connect').utils.parseSignedCookies
, cookie = require('cookie')
, fs = require('fs');
/**
* Expose Sockets initialization
*/
module.exports = Sockets;
/**
* Socket.io
*
* #param {Express} app `Express` instance.
* #param {HTTPServer} server `http` server instance.
* #api public
*/
function Sockets (app, server) {
var config = app.get('config');
var client = app.get('redisClient');
var sessionStore = app.get('sessionStore');
var io = sio.listen(server);
io.set('authorization', function (hsData, accept) {
if(hsData.headers.cookie) {
var cookies = parseCookies(cookie.parse(hsData.headers.cookie), config.session.secret)
, sid = cookies['balloons'];
sessionStore.load(sid, function(err, session) {
if(err || !session) {
return accept('Error retrieving session!', false);
}
hsData.balloons = {
user: session.passport.user,
room: /\/(?:([^\/]+?))\/?$/g.exec(hsData.headers.referer)[1]
};
return accept(null, true);
});
} else {
return accept('No cookie transmitted.', false);
}
});
});
};
Not sure if this helps, but Cookie parsing in express 4.x has been extracted to the cookie-parser package. I'm not sure, but you may be able to swap out connect.util.parseSignedCookies with cookieParser.parseSignedCookies`.
That's about all I can help you with, as I haven't used socket.io much yet.
function Sockets (app, server, pub, sub, sessionStore) {
var config = app.get('config');
var secrets = require('./config/secrets');
var client = pub;
var io = sio.listen(server);
io.set('authorization', function (handshake, callback) {
if(handshake.headers.cookie) {
// pay attention here, this is how you parse, make sure you use
// cookie-parser and cookie
var cookies = cookie.parse(handshake.headers.cookie);
var sid = cookieParser.signedCookie(cookies['balloons'], secrets.sessionSecret);
// get the session data from the session store
sessionStore.load(sid, function(err, session) {
if(err || !session) {
return callback('Error retrieving session!', false);
}
// this is not storing the data into the handshake object
handshake.headers.xygaming = {
user: session.passport.user,
room: /\/(?:([^\/]+?))\/?$/g.exec(handshake.headers.referer)[1]
};
return callback(null, true);
});
} else {
return callback('No cookie transmitted.', false);
}
});
}

socket.io parse connect (>= 2.4.1) signed session cookie

With the latest version of connect (as of 2012-07-26), I've found the following way to get a session ID from socket.io that will work with a connect-redis store.
var express = require('express')
, routes = require('./routes')
, fs = require('fs')
, http = require('http')
, io = require('socket.io')
, redis = require('connect-redis')
, connect = require('express/node_modules/connect')
, parseSignedCookie = connect.utils.parseSignedCookie
, cookie = require('express/node_modules/cookie');
var secret = '...';
var rStore = new(require('connect-redis')(express));
//...
var server = http.createServer(app);
var sio = io.listen(server);
sio.set('authorization', function(data, accept) {
if(data.headers.cookie) {
data.cookie = cookie.parse(data.headers.cookie);
data.sessionID = parseSignedCookie(data.cookie['connect.sid'], secret);
} else {
return accept('No cookie transmitted', false);
}
accept(null, true);
});
data.sessionID can then be used later such as
sio.sockets.on('connection', function(socket) {
console.log('New socket connection with ID: ' + socket.handshake.sessionID);
rStore.get(socket.handshake.sessionID, function(err, session) {
//...
});
});
Having to import so many from express (connect, a utility of connect, and the cookie module) seems like an overly roundabout way of getting the functions needed to parse connect's signed cookies. Has anyone found another way?
I was running into the same and just wrote a tiny module to abstract it. Here's how its usage looks like. It was written and tested using express 3 so should work fine with connect 2.4.x. Please let me know otherwise.
var SessionSockets = require('session.socket.io')
, sessionSockets = new SessionSockets(io, sessionStore, cookieParser);
sessionSockets.on('connection', function (err, socket, session) {
//your regular socket.io code goes here
});
For more details on how it works see https://github.com/wcamarao/session.socket.io

Categories

Resources