PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/socket.js

https://github.com/deepinit-arek/socket.io-client
JavaScript | 357 lines | 171 code | 63 blank | 123 comment | 19 complexity | 9eb689e55b3b2d06658aba4060b9eb87 MD5 | raw file
  1. /**
  2. * Module dependencies.
  3. */
  4. var parser = require('socket.io-parser')
  5. , Emitter = require('./emitter')
  6. , toArray = require('to-array')
  7. , debug = require('debug')('socket.io-client:socket')
  8. , on = require('./on')
  9. , bind;
  10. /**
  11. * Module exports.
  12. */
  13. module.exports = exports = Socket;
  14. /**
  15. * Internal events (blacklisted).
  16. * These events can't be emitted by the user.
  17. */
  18. var events = exports.events = [
  19. 'connect',
  20. 'disconnect',
  21. 'error'
  22. ];
  23. /**
  24. * Shortcut to `Emitter#emit`.
  25. */
  26. var emit = Emitter.prototype.emit;
  27. /**
  28. * `Socket` constructor.
  29. *
  30. * @api public
  31. */
  32. function Socket(io, nsp){
  33. this.io = io;
  34. this.nsp = nsp;
  35. this.json = this; // compat
  36. this.ids = 0;
  37. this.acks = {};
  38. this.open();
  39. this.buffer = [];
  40. this.connected = false;
  41. this.disconnected = true;
  42. }
  43. /**
  44. * Mix in `Emitter`.
  45. */
  46. Emitter(Socket.prototype);
  47. /**
  48. * Called upon engine `open`.
  49. *
  50. * @api private
  51. */
  52. Socket.prototype.open =
  53. Socket.prototype.connect = function(){
  54. var io = this.io;
  55. io.open(); // ensure open
  56. if ('open' == this.io.readyState) this.onopen();
  57. this.subs = [
  58. on(io, 'open', bind(this, 'onopen')),
  59. on(io, 'error', bind(this, 'onerror'))
  60. ];
  61. };
  62. /**
  63. * Sends a `message` event.
  64. *
  65. * @return {Socket} self
  66. * @api public
  67. */
  68. Socket.prototype.send = function(){
  69. var args = toArray(arguments);
  70. args.unshift('message');
  71. this.emit.apply(this, args);
  72. return this;
  73. };
  74. /**
  75. * Override `emit`.
  76. * If the event is in `events`, it's emitted normally.
  77. *
  78. * @param {String} event name
  79. * @return {Socket} self
  80. * @api public
  81. */
  82. Socket.prototype.emit = function(ev){
  83. if (~events.indexOf(ev)) {
  84. emit.apply(this, arguments);
  85. } else {
  86. var args = toArray(arguments);
  87. var packet = { type: parser.EVENT, data: args };
  88. // event ack callback
  89. if ('function' == typeof args[args.length - 1]) {
  90. debug('emitting packet with ack id %d', this.ids);
  91. this.acks[this.ids] = args.pop();
  92. packet.id = this.ids++;
  93. }
  94. this.packet(packet);
  95. }
  96. return this;
  97. };
  98. /**
  99. * Sends a packet.
  100. *
  101. * @param {Object} packet
  102. * @api private
  103. */
  104. Socket.prototype.packet = function(packet){
  105. packet.nsp = this.nsp;
  106. this.io.packet(packet);
  107. };
  108. /**
  109. * Called upon `error`.
  110. *
  111. * @param {Object} data
  112. * @api private
  113. */
  114. Socket.prototype.onerror = function(data){
  115. this.emit('error', data);
  116. };
  117. /**
  118. * "Opens" the socket.
  119. *
  120. * @api private
  121. */
  122. Socket.prototype.onopen = function(){
  123. debug('transport is open - connecting');
  124. // write connect packet if necessary
  125. if ('/' != this.nsp) {
  126. this.packet({ type: parser.CONNECT });
  127. }
  128. // subscribe
  129. var io = this.io;
  130. this.subs.push(
  131. on(io, 'packet', bind(this, 'onpacket')),
  132. on(io, 'close', bind(this, 'onclose'))
  133. );
  134. };
  135. /**
  136. * Called upon engine `close`.
  137. *
  138. * @param {String} reason
  139. * @api private
  140. */
  141. Socket.prototype.onclose = function(reason){
  142. debug('close (%s)', reason);
  143. this.connected = false;
  144. this.disconnected = true;
  145. this.emit('disconnect', reason);
  146. };
  147. /**
  148. * Called with socket packet.
  149. *
  150. * @param {Object} packet
  151. * @api private
  152. */
  153. Socket.prototype.onpacket = function(packet){
  154. if (packet.nsp != this.nsp) return;
  155. switch (packet.type) {
  156. case parser.CONNECT:
  157. this.onconnect();
  158. break;
  159. case parser.EVENT:
  160. this.onevent(packet);
  161. break;
  162. case parser.ACK:
  163. this.onack(packet);
  164. break;
  165. case parser.DISCONNECT:
  166. this.ondisconnect();
  167. break;
  168. case parser.ERROR:
  169. this.emit('error', packet.data);
  170. break;
  171. }
  172. };
  173. /**
  174. * Called upon a server event.
  175. *
  176. * @param {Object} packet
  177. * @api private
  178. */
  179. Socket.prototype.onevent = function(packet){
  180. var args = packet.data || [];
  181. debug('emitting event %j', args);
  182. if (packet.id) {
  183. debug('attaching ack callback to event');
  184. args.push(this.ack(packet.id));
  185. }
  186. if (this.connected) {
  187. emit.apply(this, args);
  188. } else {
  189. this.buffer.push(args);
  190. }
  191. };
  192. /**
  193. * Produces an ack callback to emit with an event.
  194. *
  195. * @api private
  196. */
  197. Socket.prototype.ack = function(id){
  198. var self = this;
  199. var sent = false;
  200. return function(){
  201. // prevent double callbacks
  202. if (sent) return;
  203. var args = toArray(arguments);
  204. debug('sending ack %j', args);
  205. self.packet({
  206. type: parser.ACK,
  207. id: id,
  208. data: args
  209. });
  210. };
  211. };
  212. /**
  213. * Called upon a server acknowlegement.
  214. *
  215. * @param {Object} packet
  216. * @api private
  217. */
  218. Socket.prototype.onack = function(packet){
  219. debug('calling ack %s with %j', packet.id, packet.data);
  220. var fn = this.acks[packet.id];
  221. fn.apply(this, packet.data);
  222. delete this.acks[packet.id];
  223. };
  224. /**
  225. * Called upon server connect.
  226. *
  227. * @api private
  228. */
  229. Socket.prototype.onconnect = function(){
  230. this.connected = true;
  231. this.disconnected = false;
  232. this.emit('connect');
  233. this.emitBuffered();
  234. };
  235. /**
  236. * Emit buffered events.
  237. *
  238. * @api private
  239. */
  240. Socket.prototype.emitBuffered = function(){
  241. for (var i = 0; i < this.buffer.length; i++) {
  242. emit.apply(this, this.buffer[i]);
  243. }
  244. this.buffer = [];
  245. };
  246. /**
  247. * Called upon server disconnect.
  248. *
  249. * @api private
  250. */
  251. Socket.prototype.ondisconnect = function(){
  252. debug('server disconnect (%s)', this.nsp);
  253. this.destroy();
  254. this.onclose('io server disconnect');
  255. };
  256. /**
  257. * Cleans up.
  258. *
  259. * @api private
  260. */
  261. Socket.prototype.destroy = function(){
  262. debug('destroying socket (%s)', this.nsp);
  263. // manual close means no reconnect
  264. for (var i = 0; i < this.subs.length; i++) {
  265. this.subs[i].destroy();
  266. }
  267. // notify manager
  268. this.io.destroy(this);
  269. };
  270. /**
  271. * Disconnects the socket manually.
  272. *
  273. * @return {Socket} self
  274. * @api public
  275. */
  276. Socket.prototype.close =
  277. Socket.prototype.disconnect = function(){
  278. if (!this.connected) return this;
  279. debug('performing disconnect (%s)', this.nsp);
  280. this.packet({ type: parser.PACKET_DISCONNECT });
  281. // destroy subscriptions
  282. this.destroy();
  283. // fire events
  284. this.onclose('io client disconnect');
  285. return this;
  286. };
  287. /**
  288. * Load `bind`.
  289. */
  290. try {
  291. bind = require('bind');
  292. } catch(e){
  293. bind = require('bind-component');
  294. }