mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 02:55:58 +01:00 
			
		
		
		
	Merge pull request #4162 from akhoury/master
add continuation-local-storage (CLS) support
This commit is contained in:
		| @@ -26,6 +26,7 @@ | |||||||
|     "connect-mongo": "~1.1.0", |     "connect-mongo": "~1.1.0", | ||||||
|     "connect-multiparty": "^2.0.0", |     "connect-multiparty": "^2.0.0", | ||||||
|     "connect-redis": "~3.0.2", |     "connect-redis": "~3.0.2", | ||||||
|  |     "continuation-local-storage": "^3.1.6", | ||||||
|     "cookie-parser": "^1.3.3", |     "cookie-parser": "^1.3.3", | ||||||
|     "cron": "^1.0.5", |     "cron": "^1.0.5", | ||||||
|     "csurf": "^1.6.1", |     "csurf": "^1.6.1", | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								src/middleware/cls.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/middleware/cls.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | var path = require('path'); | ||||||
|  | var sockets = require('path'); | ||||||
|  | var websockets = require('../socket.io/'); | ||||||
|  | var continuationLocalStorage = require('continuation-local-storage'); | ||||||
|  | var APP_NAMESPACE = require(path.join(__dirname, '../../package.json')).name; | ||||||
|  | var namespace = continuationLocalStorage.createNamespace(APP_NAMESPACE); | ||||||
|  |  | ||||||
|  | (function(cls) { | ||||||
|  | 	cls.http = function (req, res, next) { | ||||||
|  | 		namespace.run(function() { | ||||||
|  | 			namespace.set('request', req); | ||||||
|  | 			next && next(); | ||||||
|  | 		}); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	cls.socket = function (socket, payload, event, next) { | ||||||
|  | 		namespace.run(function() { | ||||||
|  | 			namespace.set('request', websockets.reqFromSocket(socket, payload, event)); | ||||||
|  | 			next && next(); | ||||||
|  | 		}); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	cls.get = function (key) { | ||||||
|  | 		return namespace.get(key); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	cls.set = function (key, value) { | ||||||
|  | 		return namespace.set(key, value); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	cls.setItem = cls.set; | ||||||
|  | 	cls.getItem = cls.set; | ||||||
|  | 	cls.namespace = namespace; | ||||||
|  | 	cls.continuationLocalStorage = continuationLocalStorage; | ||||||
|  |  | ||||||
|  | })(exports); | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -14,6 +14,7 @@ var meta = require('../meta'), | |||||||
| 	compression = require('compression'), | 	compression = require('compression'), | ||||||
| 	favicon = require('serve-favicon'), | 	favicon = require('serve-favicon'), | ||||||
| 	session = require('express-session'), | 	session = require('express-session'), | ||||||
|  | 	cls = require('./cls'), | ||||||
| 	useragent = require('express-useragent'); | 	useragent = require('express-useragent'); | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -73,6 +74,7 @@ module.exports = function(app) { | |||||||
|  |  | ||||||
| 	app.use(middleware.addHeaders); | 	app.use(middleware.addHeaders); | ||||||
| 	app.use(middleware.processRender); | 	app.use(middleware.processRender); | ||||||
|  | 	app.use(cls.http); | ||||||
| 	auth.initialize(app, middleware); | 	auth.initialize(app, middleware); | ||||||
|  |  | ||||||
| 	return middleware; | 	return middleware; | ||||||
|   | |||||||
| @@ -8,6 +8,28 @@ module.exports = function(Plugins) { | |||||||
| 		'filter:user.custom_fields': null	// remove in v1.1.0 | 		'filter:user.custom_fields': null	// remove in v1.1.0 | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|  | 	Plugins.deprecatedHooksParams = { | ||||||
|  | 		'action:homepage.get': '{req, res}', | ||||||
|  | 		'filter:register.check': '{req, res}', | ||||||
|  | 		'action:user.loggedOut': '{req, res}', | ||||||
|  | 		'static:user.loggedOut': '{req, res}', | ||||||
|  | 		'filter:categories.build': '{req, res}', | ||||||
|  | 		'filter:category.build': '{req, res}', | ||||||
|  | 		'filter:group.build': '{req, res}', | ||||||
|  | 		'filter:register.build': '{req, res}', | ||||||
|  | 		'filter:composer.build': '{req, res}', | ||||||
|  | 		'filter:popular.build': '{req, res}', | ||||||
|  | 		'filter:recent.build': '{req, res}', | ||||||
|  | 		'filter:topic.build': '{req, res}', | ||||||
|  | 		'filter:users.build': '{req, res}', | ||||||
|  | 		'filter:admin.category.get': '{req, res}', | ||||||
|  | 		'filter:middleware.renderHeader': '{req, res}', | ||||||
|  | 		'filter:widget.render': '{req, res}', | ||||||
|  | 		'filter:middleware.buildHeader': '{req, locals}', | ||||||
|  | 		'action:middleware.pageView': '{req}', | ||||||
|  | 		'action:meta.override404': '{req}' | ||||||
|  | 	}; | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 		`data` is an object consisting of (* is required): | 		`data` is an object consisting of (* is required): | ||||||
| 			`data.hook`*, the name of the NodeBB hook | 			`data.hook`*, the name of the NodeBB hook | ||||||
| @@ -33,6 +55,17 @@ module.exports = function(Plugins) { | |||||||
| 					'there is no alternative.' | 					'there is no alternative.' | ||||||
| 				) | 				) | ||||||
| 			); | 			); | ||||||
|  | 		} else { | ||||||
|  | 			// handle hook's startsWith, i.e. action:homepage.get | ||||||
|  | 			var _parts = data.hook.split(':'); | ||||||
|  | 			_parts.pop(); | ||||||
|  | 			var _hook = _parts.join(':'); | ||||||
|  | 			if (Plugins.deprecatedHooksParams[_hook]) { | ||||||
|  | 				winston.warn('[plugins/' + id + '] Hook `' + _hook + '` parameters: `' + Plugins.deprecatedHooksParams[_hook] + '`, are being deprecated, ' | ||||||
|  | 				+ 'all plugins should now use the `middleware/cls` module instead of hook\'s arguments to get a reference to the `req`, `res` and/or `socket` object(s) (from which you can get the current `uid` if you need to.) ' | ||||||
|  | 				+ '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-each-request-from-within-any-plugin-hook'); | ||||||
|  | 				delete Plugins.deprecatedHooksParams[_hook]; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (data.hook && data.method) { | 		if (data.hook && data.method) { | ||||||
|   | |||||||
| @@ -10,209 +10,224 @@ var winston = require('winston'); | |||||||
| var db = require('../database'); | var db = require('../database'); | ||||||
| var logger = require('../logger'); | var logger = require('../logger'); | ||||||
| var ratelimit = require('../middleware/ratelimit'); | var ratelimit = require('../middleware/ratelimit'); | ||||||
|  | var cls = require('../middleware/cls'); | ||||||
|  |  | ||||||
| var Sockets = {}; | (function(Sockets) { | ||||||
| var Namespaces = {}; | 	var Namespaces = {}; | ||||||
|  | 	var io; | ||||||
|  |  | ||||||
| var io; | 	Sockets.init = function (server) { | ||||||
|  | 		requireModules(); | ||||||
|  |  | ||||||
| Sockets.init = function(server) { | 		io = new SocketIO({ | ||||||
| 	requireModules(); | 			path: nconf.get('relative_path') + '/socket.io' | ||||||
|  | 		}); | ||||||
|  |  | ||||||
| 	io = new SocketIO({ | 		addRedisAdapter(io); | ||||||
| 		path: nconf.get('relative_path') + '/socket.io' |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	addRedisAdapter(io); | 		io.use(socketioWildcard); | ||||||
|  | 		io.use(authorize); | ||||||
|  |  | ||||||
| 	io.use(socketioWildcard); | 		io.on('connection', onConnection); | ||||||
| 	io.use(authorize); | 		io.on('disconnect', onDisconnect); | ||||||
|  |  | ||||||
| 	io.on('connection', onConnection); | 		io.listen(server, { | ||||||
|  | 			transports: nconf.get('socket.io:transports') | ||||||
|  | 		}); | ||||||
|  |  | ||||||
| 	io.listen(server, { | 		Sockets.server = io; | ||||||
| 		transports: nconf.get('socket.io:transports') |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	Sockets.server = io; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| function onConnection(socket) { |  | ||||||
| 	socket.ip = socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress; |  | ||||||
|  |  | ||||||
| 	logger.io_one(socket, socket.uid); |  | ||||||
|  |  | ||||||
| 	onConnect(socket); |  | ||||||
|  |  | ||||||
| 	socket.on('*', function(payload) { |  | ||||||
| 		onMessage(socket, payload); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function onConnect(socket) { |  | ||||||
| 	if (socket.uid) { |  | ||||||
| 		socket.join('uid_' + socket.uid); |  | ||||||
| 		socket.join('online_users'); |  | ||||||
| 	} else { |  | ||||||
| 		socket.join('online_guests'); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function onMessage(socket, payload) { |  | ||||||
| 	if (!payload.data.length) { |  | ||||||
| 		return winston.warn('[socket.io] Empty payload'); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var eventName = payload.data[0]; |  | ||||||
| 	var params = payload.data[1]; |  | ||||||
| 	var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function() {}; |  | ||||||
|  |  | ||||||
| 	if (!eventName) { |  | ||||||
| 		return winston.warn('[socket.io] Empty method name'); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var parts = eventName.toString().split('.'); |  | ||||||
| 	var namespace = parts[0]; |  | ||||||
| 	var methodToCall = parts.reduce(function(prev, cur) { |  | ||||||
| 		if (prev !== null && prev[cur]) { |  | ||||||
| 			return prev[cur]; |  | ||||||
| 		} else { |  | ||||||
| 			return null; |  | ||||||
| 		} |  | ||||||
| 	}, Namespaces); |  | ||||||
|  |  | ||||||
| 	if(!methodToCall) { |  | ||||||
| 		if (process.env.NODE_ENV === 'development') { |  | ||||||
| 			winston.warn('[socket.io] Unrecognized message: ' + eventName); |  | ||||||
| 		} |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	socket.previousEvents = socket.previousEvents || []; |  | ||||||
| 	socket.previousEvents.push(eventName); |  | ||||||
| 	if (socket.previousEvents.length > 20) { |  | ||||||
| 		socket.previousEvents.shift(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) { |  | ||||||
| 		winston.warn('[socket.io] Too many emits! Disconnecting uid : ' + socket.uid + '. Events : ' + socket.previousEvents); |  | ||||||
| 		return socket.disconnect(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	async.waterfall([ |  | ||||||
| 		function (next) { |  | ||||||
| 			validateSession(socket, next); |  | ||||||
| 		}, |  | ||||||
| 		function (next) { |  | ||||||
| 			if (Namespaces[namespace].before) { |  | ||||||
| 				Namespaces[namespace].before(socket, eventName, params, next); |  | ||||||
| 			} else { |  | ||||||
| 				next(); |  | ||||||
| 			} |  | ||||||
| 		}, |  | ||||||
| 		function (next) { |  | ||||||
| 			methodToCall(socket, params, next); |  | ||||||
| 		} |  | ||||||
| 	], function(err, result) { |  | ||||||
| 		callback(err ? {message: err.message} : null, result); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function requireModules() { |  | ||||||
| 	var modules = ['admin', 'categories', 'groups', 'meta', 'modules', |  | ||||||
| 		'notifications', 'plugins', 'posts', 'topics', 'user', 'blacklist' |  | ||||||
| 	]; |  | ||||||
|  |  | ||||||
| 	modules.forEach(function(module) { |  | ||||||
| 		Namespaces[module] = require('./' + module); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function validateSession(socket, callback) { |  | ||||||
| 	var req = socket.request; |  | ||||||
| 	if (!req.signedCookies || !req.signedCookies['express.sid']) { |  | ||||||
| 		return callback(new Error('[[error:invalid-session]]')); |  | ||||||
| 	} |  | ||||||
| 	db.sessionStore.get(req.signedCookies['express.sid'], function(err, sessionData) { |  | ||||||
| 		if (err || !sessionData) { |  | ||||||
| 			return callback(err || new Error('[[error:invalid-session]]')); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		callback(); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function authorize(socket, callback) { |  | ||||||
| 	var request = socket.request; |  | ||||||
|  |  | ||||||
| 	if (!request) { |  | ||||||
| 		return callback(new Error('[[error:not-authorized]]')); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	async.waterfall([ |  | ||||||
| 		function(next) { |  | ||||||
| 			cookieParser(request, {}, next); |  | ||||||
| 		}, |  | ||||||
| 		function(next) { |  | ||||||
| 			db.sessionStore.get(request.signedCookies['express.sid'], function(err, sessionData) { |  | ||||||
| 				if (err) { |  | ||||||
| 					return next(err); |  | ||||||
| 				} |  | ||||||
| 				if (sessionData && sessionData.passport && sessionData.passport.user) { |  | ||||||
| 					request.session = sessionData; |  | ||||||
| 					socket.uid = parseInt(sessionData.passport.user, 10); |  | ||||||
| 				} else { |  | ||||||
| 					socket.uid = 0; |  | ||||||
| 				} |  | ||||||
| 				next(); |  | ||||||
| 			}); |  | ||||||
| 		} |  | ||||||
| 	], callback); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function addRedisAdapter(io) { |  | ||||||
| 	if (nconf.get('redis')) { |  | ||||||
| 		var redisAdapter = require('socket.io-redis'); |  | ||||||
| 		var redis = require('../database/redis'); |  | ||||||
| 		var pub = redis.connect({return_buffers: true}); |  | ||||||
| 		var sub = redis.connect({return_buffers: true}); |  | ||||||
|  |  | ||||||
| 		io.adapter(redisAdapter({pubClient: pub, subClient: sub})); |  | ||||||
| 	} else if (nconf.get('isCluster') === 'true') { |  | ||||||
| 		winston.warn('[socket.io] Clustering detected, you are advised to configure Redis as a websocket store.'); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Sockets.in = function(room) { |  | ||||||
| 	return io.in(room); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Sockets.getUserSocketCount = function(uid) { |  | ||||||
| 	if (!io) { |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var room = io.sockets.adapter.rooms['uid_' + uid]; |  | ||||||
| 	return room ? room.length : 0; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Sockets.reqFromSocket = function(socket) { |  | ||||||
| 	var headers = socket.request.headers; |  | ||||||
| 	var host = headers.host; |  | ||||||
| 	var referer = headers.referer || ''; |  | ||||||
|  |  | ||||||
| 	return { |  | ||||||
| 		ip: headers['x-forwarded-for'] || socket.ip, |  | ||||||
| 		host: host, |  | ||||||
| 		protocol: socket.request.connection.encrypted ? 'https' : 'http', |  | ||||||
| 		secure: !!socket.request.connection.encrypted, |  | ||||||
| 		url: referer, |  | ||||||
| 		path: referer.substr(referer.indexOf(host) + host.length), |  | ||||||
| 		headers: headers |  | ||||||
| 	}; | 	}; | ||||||
| }; |  | ||||||
|  | 	function onConnection(socket) { | ||||||
|  | 		socket.ip = socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress; | ||||||
|  |  | ||||||
|  | 		logger.io_one(socket, socket.uid); | ||||||
|  |  | ||||||
|  | 		cls.socket(socket, null, 'connection', function () { | ||||||
|  | 			onConnect(socket); | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		socket.on('*', function (payload) { | ||||||
|  | 			cls.socket(socket, payload, null, function () { | ||||||
|  | 				onMessage(socket, payload); | ||||||
|  | 			}); | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function onConnect(socket) { | ||||||
|  | 		if (socket.uid) { | ||||||
|  | 			socket.join('uid_' + socket.uid); | ||||||
|  | 			socket.join('online_users'); | ||||||
|  | 		} else { | ||||||
|  | 			socket.join('online_guests'); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function onDisconnect(socket) { | ||||||
|  | 		cls.socket(socket, null, 'disconnect', function () { | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
| module.exports = Sockets; | 	function onMessage(socket, payload) { | ||||||
|  | 		if (!payload.data.length) { | ||||||
|  | 			return winston.warn('[socket.io] Empty payload'); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		var eventName = payload.data[0]; | ||||||
|  | 		var params = payload.data[1]; | ||||||
|  | 		var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function () { | ||||||
|  | 		}; | ||||||
|  |  | ||||||
|  | 		if (!eventName) { | ||||||
|  | 			return winston.warn('[socket.io] Empty method name'); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		var parts = eventName.toString().split('.'); | ||||||
|  | 		var namespace = parts[0]; | ||||||
|  | 		var methodToCall = parts.reduce(function (prev, cur) { | ||||||
|  | 			if (prev !== null && prev[cur]) { | ||||||
|  | 				return prev[cur]; | ||||||
|  | 			} else { | ||||||
|  | 				return null; | ||||||
|  | 			} | ||||||
|  | 		}, Namespaces); | ||||||
|  |  | ||||||
|  | 		if (!methodToCall) { | ||||||
|  | 			if (process.env.NODE_ENV === 'development') { | ||||||
|  | 				winston.warn('[socket.io] Unrecognized message: ' + eventName); | ||||||
|  | 			} | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		socket.previousEvents = socket.previousEvents || []; | ||||||
|  | 		socket.previousEvents.push(eventName); | ||||||
|  | 		if (socket.previousEvents.length > 20) { | ||||||
|  | 			socket.previousEvents.shift(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) { | ||||||
|  | 			winston.warn('[socket.io] Too many emits! Disconnecting uid : ' + socket.uid + '. Events : ' + socket.previousEvents); | ||||||
|  | 			return socket.disconnect(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		async.waterfall([ | ||||||
|  | 			function (next) { | ||||||
|  | 				validateSession(socket, next); | ||||||
|  | 			}, | ||||||
|  | 			function (next) { | ||||||
|  | 				if (Namespaces[namespace].before) { | ||||||
|  | 					Namespaces[namespace].before(socket, eventName, params, next); | ||||||
|  | 				} else { | ||||||
|  | 					next(); | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 			function (next) { | ||||||
|  | 				methodToCall(socket, params, next); | ||||||
|  | 			} | ||||||
|  | 		], function (err, result) { | ||||||
|  | 			callback(err ? {message: err.message} : null, result); | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function requireModules() { | ||||||
|  | 		var modules = ['admin', 'categories', 'groups', 'meta', 'modules', | ||||||
|  | 			'notifications', 'plugins', 'posts', 'topics', 'user', 'blacklist' | ||||||
|  | 		]; | ||||||
|  |  | ||||||
|  | 		modules.forEach(function (module) { | ||||||
|  | 			Namespaces[module] = require('./' + module); | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function validateSession(socket, callback) { | ||||||
|  | 		var req = socket.request; | ||||||
|  | 		if (!req.signedCookies || !req.signedCookies['express.sid']) { | ||||||
|  | 			return callback(new Error('[[error:invalid-session]]')); | ||||||
|  | 		} | ||||||
|  | 		db.sessionStore.get(req.signedCookies['express.sid'], function (err, sessionData) { | ||||||
|  | 			if (err || !sessionData) { | ||||||
|  | 				return callback(err || new Error('[[error:invalid-session]]')); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			callback(); | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function authorize(socket, callback) { | ||||||
|  | 		var request = socket.request; | ||||||
|  |  | ||||||
|  | 		if (!request) { | ||||||
|  | 			return callback(new Error('[[error:not-authorized]]')); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		async.waterfall([ | ||||||
|  | 			function (next) { | ||||||
|  | 				cookieParser(request, {}, next); | ||||||
|  | 			}, | ||||||
|  | 			function (next) { | ||||||
|  | 				db.sessionStore.get(request.signedCookies['express.sid'], function (err, sessionData) { | ||||||
|  | 					if (err) { | ||||||
|  | 						return next(err); | ||||||
|  | 					} | ||||||
|  | 					if (sessionData && sessionData.passport && sessionData.passport.user) { | ||||||
|  | 						request.session = sessionData; | ||||||
|  | 						socket.uid = parseInt(sessionData.passport.user, 10); | ||||||
|  | 					} else { | ||||||
|  | 						socket.uid = 0; | ||||||
|  | 					} | ||||||
|  | 					next(); | ||||||
|  | 				}); | ||||||
|  | 			} | ||||||
|  | 		], callback); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function addRedisAdapter(io) { | ||||||
|  | 		if (nconf.get('redis')) { | ||||||
|  | 			var redisAdapter = require('socket.io-redis'); | ||||||
|  | 			var redis = require('../database/redis'); | ||||||
|  | 			var pub = redis.connect({return_buffers: true}); | ||||||
|  | 			var sub = redis.connect({return_buffers: true}); | ||||||
|  |  | ||||||
|  | 			io.adapter(redisAdapter({pubClient: pub, subClient: sub})); | ||||||
|  | 		} else if (nconf.get('isCluster') === 'true') { | ||||||
|  | 			winston.warn('[socket.io] Clustering detected, you are advised to configure Redis as a websocket store.'); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	Sockets.in = function (room) { | ||||||
|  | 		return io.in(room); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	Sockets.getUserSocketCount = function (uid) { | ||||||
|  | 		if (!io) { | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		var room = io.sockets.adapter.rooms['uid_' + uid]; | ||||||
|  | 		return room ? room.length : 0; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	Sockets.reqFromSocket = function (socket, payload, event) { | ||||||
|  | 		var headers = socket.request.headers; | ||||||
|  | 		var host = headers.host; | ||||||
|  | 		var referer = headers.referer || ''; | ||||||
|  | 		var data = ((payload || {}).data || []); | ||||||
|  |  | ||||||
|  | 		return { | ||||||
|  | 			uid: socket.uid, | ||||||
|  | 			params: data[1], | ||||||
|  | 			method: event || data[0], | ||||||
|  | 			body: payload, | ||||||
|  | 			ip: headers['x-forwarded-for'] || socket.ip, | ||||||
|  | 			host: host, | ||||||
|  | 			protocol: socket.request.connection.encrypted ? 'https' : 'http', | ||||||
|  | 			secure: !!socket.request.connection.encrypted, | ||||||
|  | 			url: referer, | ||||||
|  | 			path: referer.substr(referer.indexOf(host) + host.length), | ||||||
|  | 			headers: headers | ||||||
|  | 		}; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | })(exports); | ||||||
		Reference in New Issue
	
	Block a user