PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/socket.js

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