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.

299 lines
5.8 KiB

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