namespace.js 5.2 KB

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