namespace.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /**
  2. * Module dependencies.
  3. */
  4. var Socket = require('./socket');
  5. var Emitter = require('events').EventEmitter;
  6. var parser = require('socket.io-parser');
  7. var debug = require('debug')('socket.io:namespace');
  8. var hasBin = require('has-binary');
  9. /**
  10. * Module exports.
  11. */
  12. module.exports = exports = Namespace;
  13. /**
  14. * Blacklisted events.
  15. */
  16. exports.events = [
  17. 'connect', // for symmetry with client
  18. 'connection',
  19. 'newListener'
  20. ];
  21. /**
  22. * Flags.
  23. */
  24. exports.flags = [
  25. 'json',
  26. 'volatile',
  27. 'local'
  28. ];
  29. /**
  30. * `EventEmitter#emit` reference.
  31. */
  32. var emit = Emitter.prototype.emit;
  33. /**
  34. * Namespace constructor.
  35. *
  36. * @param {Server} server instance
  37. * @param {Socket} name
  38. * @api private
  39. */
  40. function Namespace(server, name){
  41. this.name = name;
  42. this.server = server;
  43. this.sockets = {};
  44. this.connected = {};
  45. this.fns = [];
  46. this.ids = 0;
  47. this.initAdapter();
  48. }
  49. /**
  50. * Inherits from `EventEmitter`.
  51. */
  52. Namespace.prototype.__proto__ = Emitter.prototype;
  53. /**
  54. * Apply flags from `Socket`.
  55. */
  56. exports.flags.forEach(function(flag){
  57. Object.defineProperty(Namespace.prototype, flag, {
  58. get: function() {
  59. this.flags = this.flags || {};
  60. this.flags[flag] = true;
  61. return this;
  62. }
  63. });
  64. });
  65. /**
  66. * Initializes the `Adapter` for this nsp.
  67. * Run upon changing adapter by `Server#adapter`
  68. * in addition to the constructor.
  69. *
  70. * @api private
  71. */
  72. Namespace.prototype.initAdapter = function(){
  73. this.adapter = new (this.server.adapter())(this);
  74. };
  75. /**
  76. * Sets up namespace middleware.
  77. *
  78. * @return {Namespace} self
  79. * @api public
  80. */
  81. Namespace.prototype.use = function(fn){
  82. this.fns.push(fn);
  83. return this;
  84. };
  85. /**
  86. * Executes the middleware for an incoming client.
  87. *
  88. * @param {Socket} socket that will get added
  89. * @param {Function} fn last fn call in the middleware
  90. * @api private
  91. */
  92. Namespace.prototype.run = function(socket, fn){
  93. var fns = this.fns.slice(0);
  94. if (!fns.length) return fn(null);
  95. function run(i){
  96. fns[i](socket, function(err){
  97. // upon error, short-circuit
  98. if (err) return fn(err);
  99. // if no middleware left, summon callback
  100. if (!fns[i + 1]) return fn(null);
  101. // go on to next
  102. run(i + 1);
  103. });
  104. }
  105. run(0);
  106. };
  107. /**
  108. * Targets a room when emitting.
  109. *
  110. * @param {String} name
  111. * @return {Namespace} self
  112. * @api public
  113. */
  114. Namespace.prototype.to =
  115. Namespace.prototype.in = function(name){
  116. this.rooms = this.rooms || [];
  117. if (!~this.rooms.indexOf(name)) this.rooms.push(name);
  118. return this;
  119. };
  120. /**
  121. * Adds a new client.
  122. *
  123. * @return {Socket}
  124. * @api private
  125. */
  126. Namespace.prototype.add = function(client, query, fn){
  127. debug('adding socket to nsp %s', this.name);
  128. var socket = new Socket(this, client, query);
  129. var self = this;
  130. this.run(socket, function(err){
  131. process.nextTick(function(){
  132. if ('open' == client.conn.readyState) {
  133. if (err) return socket.error(err.data || err.message);
  134. // track socket
  135. self.sockets[socket.id] = socket;
  136. // it's paramount that the internal `onconnect` logic
  137. // fires before user-set events to prevent state order
  138. // violations (such as a disconnection before the connection
  139. // logic is complete)
  140. socket.onconnect();
  141. if (fn) fn();
  142. // fire user-set events
  143. self.emit('connect', socket);
  144. self.emit('connection', socket);
  145. } else {
  146. debug('next called after client was closed - ignoring socket');
  147. }
  148. });
  149. });
  150. return socket;
  151. };
  152. /**
  153. * Removes a client. Called by each `Socket`.
  154. *
  155. * @api private
  156. */
  157. Namespace.prototype.remove = function(socket){
  158. if (this.sockets.hasOwnProperty(socket.id)) {
  159. delete this.sockets[socket.id];
  160. } else {
  161. debug('ignoring remove for %s', socket.id);
  162. }
  163. };
  164. /**
  165. * Emits to all clients.
  166. *
  167. * @return {Namespace} self
  168. * @api public
  169. */
  170. Namespace.prototype.emit = function(ev){
  171. if (~exports.events.indexOf(ev)) {
  172. emit.apply(this, arguments);
  173. } else {
  174. // set up packet object
  175. var args = Array.prototype.slice.call(arguments);
  176. var parserType = parser.EVENT; // default
  177. if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary
  178. var packet = { type: parserType, data: args };
  179. if ('function' == typeof args[args.length - 1]) {
  180. throw new Error('Callbacks are not supported when broadcasting');
  181. }
  182. this.adapter.broadcast(packet, {
  183. rooms: this.rooms,
  184. flags: this.flags
  185. });
  186. delete this.rooms;
  187. delete this.flags;
  188. }
  189. return this;
  190. };
  191. /**
  192. * Sends a `message` event to all clients.
  193. *
  194. * @return {Namespace} self
  195. * @api public
  196. */
  197. Namespace.prototype.send =
  198. Namespace.prototype.write = function(){
  199. var args = Array.prototype.slice.call(arguments);
  200. args.unshift('message');
  201. this.emit.apply(this, args);
  202. return this;
  203. };
  204. /**
  205. * Gets a list of clients.
  206. *
  207. * @return {Namespace} self
  208. * @api public
  209. */
  210. Namespace.prototype.clients = function(fn){
  211. this.adapter.clients(this.rooms, fn);
  212. // delete rooms flag for scenario:
  213. // .in('room').clients() (GH-1978)
  214. delete this.rooms;
  215. return this;
  216. };
  217. /**
  218. * Sets the compress flag.
  219. *
  220. * @param {Boolean} compress if `true`, compresses the sending data
  221. * @return {Socket} self
  222. * @api public
  223. */
  224. Namespace.prototype.compress = function(compress){
  225. this.flags = this.flags || {};
  226. this.flags.compress = compress;
  227. return this;
  228. };