You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
4.7 KiB

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