socket.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*!
  2. * socket.io-node
  3. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var parser = require('./parser')
  10. , util = require('./util')
  11. , EventEmitter = process.EventEmitter
  12. /**
  13. * Export the constructor.
  14. */
  15. exports = module.exports = Socket;
  16. /**
  17. * Default error event listener to prevent uncaught exceptions.
  18. */
  19. var defaultError = function () {};
  20. /**
  21. * Socket constructor.
  22. *
  23. * @param {Manager} manager instance
  24. * @param {String} session id
  25. * @param {Namespace} namespace the socket belongs to
  26. * @param {Boolean} whether the
  27. * @api public
  28. */
  29. function Socket (manager, id, nsp, readable) {
  30. this.id = id;
  31. this.namespace = nsp;
  32. this.manager = manager;
  33. this.disconnected = false;
  34. this.ackPackets = 0;
  35. this.acks = {};
  36. this.setFlags();
  37. this.readable = readable;
  38. this.store = this.manager.store.client(this.id);
  39. this.on('error', defaultError);
  40. };
  41. /**
  42. * Inherits from EventEmitter.
  43. */
  44. Socket.prototype.__proto__ = EventEmitter.prototype;
  45. /**
  46. * Accessor shortcut for the handshake data
  47. *
  48. * @api private
  49. */
  50. Socket.prototype.__defineGetter__('handshake', function () {
  51. return this.manager.handshaken[this.id];
  52. });
  53. /**
  54. * Accessor shortcut for the transport type
  55. *
  56. * @api private
  57. */
  58. Socket.prototype.__defineGetter__('transport', function () {
  59. return this.manager.transports[this.id].name;
  60. });
  61. /**
  62. * Accessor shortcut for the logger.
  63. *
  64. * @api private
  65. */
  66. Socket.prototype.__defineGetter__('log', function () {
  67. return this.manager.log;
  68. });
  69. /**
  70. * JSON message flag.
  71. *
  72. * @api public
  73. */
  74. Socket.prototype.__defineGetter__('json', function () {
  75. this.flags.json = true;
  76. return this;
  77. });
  78. /**
  79. * Volatile message flag.
  80. *
  81. * @api public
  82. */
  83. Socket.prototype.__defineGetter__('volatile', function () {
  84. this.flags.volatile = true;
  85. return this;
  86. });
  87. /**
  88. * Broadcast message flag.
  89. *
  90. * @api public
  91. */
  92. Socket.prototype.__defineGetter__('broadcast', function () {
  93. this.flags.broadcast = true;
  94. return this;
  95. });
  96. /**
  97. * Overrides the room to broadcast messages to (flag)
  98. *
  99. * @api public
  100. */
  101. Socket.prototype.to = Socket.prototype.in = function (room) {
  102. this.flags.room = room;
  103. return this;
  104. };
  105. /**
  106. * Resets flags
  107. *
  108. * @api private
  109. */
  110. Socket.prototype.setFlags = function () {
  111. this.flags = {
  112. endpoint: this.namespace.name
  113. , room: ''
  114. };
  115. return this;
  116. };
  117. /**
  118. * Triggered on disconnect
  119. *
  120. * @api private
  121. */
  122. Socket.prototype.onDisconnect = function (reason) {
  123. if (!this.disconnected) {
  124. this.$emit('disconnect', reason);
  125. this.disconnected = true;
  126. }
  127. };
  128. /**
  129. * Joins a user to a room.
  130. *
  131. * @api public
  132. */
  133. Socket.prototype.join = function (name, fn) {
  134. var nsp = this.namespace.name
  135. , name = (nsp + '/') + name;
  136. this.manager.onJoin(this.id, name);
  137. this.manager.store.publish('join', this.id, name);
  138. if (fn) {
  139. this.log.warn('Client#join callback is deprecated');
  140. fn();
  141. }
  142. return this;
  143. };
  144. /**
  145. * Un-joins a user from a room.
  146. *
  147. * @api public
  148. */
  149. Socket.prototype.leave = function (name, fn) {
  150. var nsp = this.namespace.name
  151. , name = (nsp + '/') + name;
  152. this.manager.onLeave(this.id, name);
  153. this.manager.store.publish('leave', this.id, name);
  154. if (fn) {
  155. this.log.warn('Client#leave callback is deprecated');
  156. fn();
  157. }
  158. return this;
  159. };
  160. /**
  161. * Transmits a packet.
  162. *
  163. * @api private
  164. */
  165. Socket.prototype.packet = function (packet) {
  166. if (this.flags.broadcast) {
  167. this.log.debug('broadcasting packet');
  168. this.namespace.in(this.flags.room).except(this.id).packet(packet);
  169. } else {
  170. packet.endpoint = this.flags.endpoint;
  171. packet = parser.encodePacket(packet);
  172. this.dispatch(packet, this.flags.volatile);
  173. }
  174. this.setFlags();
  175. return this;
  176. };
  177. /**
  178. * Dispatches a packet
  179. *
  180. * @api private
  181. */
  182. Socket.prototype.dispatch = function (packet, volatile) {
  183. if (this.manager.transports[this.id] && this.manager.transports[this.id].open) {
  184. this.manager.transports[this.id].onDispatch(packet, volatile);
  185. } else {
  186. if (!volatile) {
  187. this.manager.onClientDispatch(this.id, packet, volatile);
  188. }
  189. this.manager.store.publish('dispatch:' + this.id, packet, volatile);
  190. }
  191. };
  192. /**
  193. * Stores data for the client.
  194. *
  195. * @api public
  196. */
  197. Socket.prototype.set = function (key, value, fn) {
  198. this.store.set(key, value, fn);
  199. return this;
  200. };
  201. /**
  202. * Retrieves data for the client
  203. *
  204. * @api public
  205. */
  206. Socket.prototype.get = function (key, fn) {
  207. this.store.get(key, fn);
  208. return this;
  209. };
  210. /**
  211. * Checks data for the client
  212. *
  213. * @api public
  214. */
  215. Socket.prototype.has = function (key, fn) {
  216. this.store.has(key, fn);
  217. return this;
  218. };
  219. /**
  220. * Deletes data for the client
  221. *
  222. * @api public
  223. */
  224. Socket.prototype.del = function (key, fn) {
  225. this.store.del(key, fn);
  226. return this;
  227. };
  228. /**
  229. * Kicks client
  230. *
  231. * @api public
  232. */
  233. Socket.prototype.disconnect = function () {
  234. if (!this.disconnected) {
  235. this.log.info('booting client');
  236. if ('' === this.namespace.name) {
  237. if (this.manager.transports[this.id] && this.manager.transports[this.id].open) {
  238. this.manager.transports[this.id].onForcedDisconnect();
  239. } else {
  240. this.manager.onClientDisconnect(this.id);
  241. this.manager.store.publish('disconnect:' + this.id);
  242. }
  243. } else {
  244. this.packet({type: 'disconnect'});
  245. this.manager.onLeave(this.id, this.namespace.name);
  246. this.$emit('disconnect', 'booted');
  247. }
  248. }
  249. return this;
  250. };
  251. /**
  252. * Send a message.
  253. *
  254. * @api public
  255. */
  256. Socket.prototype.send = function (data, fn) {
  257. var packet = {
  258. type: this.flags.json ? 'json' : 'message'
  259. , data: data
  260. };
  261. if (fn) {
  262. packet.id = ++this.ackPackets;
  263. packet.ack = true;
  264. this.acks[packet.id] = fn;
  265. }
  266. return this.packet(packet);
  267. };
  268. /**
  269. * Original emit function.
  270. *
  271. * @api private
  272. */
  273. Socket.prototype.$emit = EventEmitter.prototype.emit;
  274. /**
  275. * Emit override for custom events.
  276. *
  277. * @api public
  278. */
  279. Socket.prototype.emit = function (ev) {
  280. if (ev == 'newListener') {
  281. return this.$emit.apply(this, arguments);
  282. }
  283. var args = util.toArray(arguments).slice(1)
  284. , lastArg = args[args.length - 1]
  285. , packet = {
  286. type: 'event'
  287. , name: ev
  288. };
  289. if ('function' == typeof lastArg) {
  290. packet.id = ++this.ackPackets;
  291. packet.ack = lastArg.length ? 'data' : true;
  292. this.acks[packet.id] = lastArg;
  293. args = args.slice(0, args.length - 1);
  294. }
  295. packet.args = args;
  296. return this.packet(packet);
  297. };