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