PageRenderTime 64ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/chat/node_modules/socket.io/node_modules/engine.io/lib/socket.js

https://github.com/raghunat/suny-live
JavaScript | 372 lines | 211 code | 54 blank | 107 comment | 44 complexity | b2116921a16cf99f9b4e77469e467d74 MD5 | raw file
Possible License(s): MIT, 0BSD, Apache-2.0, GPL-2.0
  1. /**
  2. * Module dependencies.
  3. */
  4. var EventEmitter = require('events').EventEmitter;
  5. var debug = require('debug')('engine:socket');
  6. /**
  7. * Module exports.
  8. */
  9. module.exports = Socket;
  10. /**
  11. * Client class (abstract).
  12. *
  13. * @api private
  14. */
  15. function Socket (id, server, transport, req) {
  16. this.id = id;
  17. this.server = server;
  18. this.upgraded = false;
  19. this.readyState = 'opening';
  20. this.writeBuffer = [];
  21. this.packetsFn = [];
  22. this.sentCallbackFn = [];
  23. this.request = req;
  24. this.checkIntervalTimer = null;
  25. this.upgradeTimeoutTimer = null;
  26. this.pingTimeoutTimer = null;
  27. this.setTransport(transport);
  28. this.onOpen();
  29. }
  30. /**
  31. * Inherits from EventEmitter.
  32. */
  33. Socket.prototype.__proto__ = EventEmitter.prototype;
  34. /**
  35. * Called upon transport considered open.
  36. *
  37. * @api private
  38. */
  39. Socket.prototype.onOpen = function () {
  40. this.readyState = 'open';
  41. // sends an `open` packet
  42. this.transport.sid = this.id;
  43. this.sendPacket('open', JSON.stringify({
  44. sid: this.id
  45. , upgrades: this.getAvailableUpgrades()
  46. , pingInterval: this.server.pingInterval
  47. , pingTimeout: this.server.pingTimeout
  48. }));
  49. this.emit('open');
  50. this.setPingTimeout();
  51. };
  52. /**
  53. * Called upon transport packet.
  54. *
  55. * @param {Object} packet
  56. * @api private
  57. */
  58. Socket.prototype.onPacket = function (packet) {
  59. if ('open' == this.readyState) {
  60. // export packet event
  61. debug('packet');
  62. this.emit('packet', packet);
  63. // Reset ping timeout on any packet, incoming data is a good sign of
  64. // other side's liveness
  65. this.setPingTimeout();
  66. switch (packet.type) {
  67. case 'ping':
  68. debug('got ping');
  69. this.sendPacket('pong');
  70. this.emit('heartbeat');
  71. break;
  72. case 'error':
  73. this.onClose('parse error');
  74. break;
  75. case 'message':
  76. this.emit('data', packet.data);
  77. this.emit('message', packet.data);
  78. break;
  79. }
  80. } else {
  81. debug('packet received with closed socket');
  82. }
  83. };
  84. /**
  85. * Called upon transport error.
  86. *
  87. * @param {Error} error object
  88. * @api private
  89. */
  90. Socket.prototype.onError = function (err) {
  91. debug('transport error');
  92. this.onClose('transport error', err);
  93. };
  94. /**
  95. * Sets and resets ping timeout timer based on client pings.
  96. *
  97. * @api private
  98. */
  99. Socket.prototype.setPingTimeout = function () {
  100. var self = this;
  101. clearTimeout(self.pingTimeoutTimer);
  102. self.pingTimeoutTimer = setTimeout(function () {
  103. self.onClose('ping timeout');
  104. }, self.server.pingInterval + self.server.pingTimeout);
  105. };
  106. /**
  107. * Attaches handlers for the given transport.
  108. *
  109. * @param {Transport} transport
  110. * @api private
  111. */
  112. Socket.prototype.setTransport = function (transport) {
  113. this.transport = transport;
  114. this.transport.once('error', this.onError.bind(this));
  115. this.transport.on('packet', this.onPacket.bind(this));
  116. this.transport.on('drain', this.flush.bind(this));
  117. this.transport.once('close', this.onClose.bind(this, 'transport close'));
  118. //this function will manage packet events (also message callbacks)
  119. this.setupSendCallback();
  120. };
  121. /**
  122. * Upgrades socket to the given transport
  123. *
  124. * @param {Transport} transport
  125. * @api private
  126. */
  127. Socket.prototype.maybeUpgrade = function (transport) {
  128. debug('might upgrade socket transport from "%s" to "%s"'
  129. , this.transport.name, transport.name);
  130. var self = this;
  131. // set transport upgrade timer
  132. self.upgradeTimeoutTimer = setTimeout(function () {
  133. debug('client did not complete upgrade - closing transport');
  134. clearInterval(self.checkIntervalTimer);
  135. self.checkIntervalTimer = null;
  136. if ('open' == transport.readyState) {
  137. transport.close();
  138. }
  139. }, this.server.upgradeTimeout);
  140. function onPacket(packet){
  141. if ('ping' == packet.type && 'probe' == packet.data) {
  142. transport.send([{ type: 'pong', data: 'probe' }]);
  143. clearInterval(self.checkIntervalTimer);
  144. self.checkIntervalTimer = setInterval(check, 100);
  145. } else if ('upgrade' == packet.type && self.readyState == 'open') {
  146. debug('got upgrade packet - upgrading');
  147. self.upgraded = true;
  148. self.clearTransport();
  149. self.setTransport(transport);
  150. self.emit('upgrade', transport);
  151. self.setPingTimeout();
  152. self.flush();
  153. clearInterval(self.checkIntervalTimer);
  154. self.checkIntervalTimer = null;
  155. clearTimeout(self.upgradeTimeoutTimer);
  156. transport.removeListener('packet', onPacket);
  157. } else {
  158. transport.close();
  159. }
  160. }
  161. // we force a polling cycle to ensure a fast upgrade
  162. function check(){
  163. if ('polling' == self.transport.name && self.transport.writable) {
  164. debug('writing a noop packet to polling for fast upgrade');
  165. self.transport.send([{ type: 'noop' }]);
  166. }
  167. }
  168. transport.on('packet', onPacket);
  169. };
  170. /**
  171. * Clears listeners and timers associated with current transport.
  172. *
  173. * @api private
  174. */
  175. Socket.prototype.clearTransport = function () {
  176. // silence further transport errors and prevent uncaught exceptions
  177. this.transport.on('error', function(){
  178. debug('error triggered by discarded transport');
  179. });
  180. clearTimeout(this.pingTimeoutTimer);
  181. };
  182. /**
  183. * Called upon transport considered closed.
  184. * Possible reasons: `ping timeout`, `client error`, `parse error`,
  185. * `transport error`, `server close`, `transport close`
  186. */
  187. Socket.prototype.onClose = function (reason, description) {
  188. if ('closed' != this.readyState) {
  189. clearTimeout(this.pingTimeoutTimer);
  190. clearInterval(this.checkIntervalTimer);
  191. this.checkIntervalTimer = null;
  192. clearTimeout(this.upgradeTimeoutTimer);
  193. var self = this;
  194. // clean writeBuffer in next tick, so developers can still
  195. // grab the writeBuffer on 'close' event
  196. process.nextTick(function() {
  197. self.writeBuffer = [];
  198. });
  199. this.packetsFn = [];
  200. this.sentCallbackFn = [];
  201. this.clearTransport();
  202. this.readyState = 'closed';
  203. this.emit('close', reason, description);
  204. }
  205. };
  206. /**
  207. * Setup and manage send callback
  208. *
  209. * @api private
  210. */
  211. Socket.prototype.setupSendCallback = function () {
  212. var self = this;
  213. //the message was sent successfully, execute the callback
  214. this.transport.on('drain', function() {
  215. if (self.sentCallbackFn.length > 0) {
  216. var seqFn = self.sentCallbackFn.splice(0,1)[0];
  217. if ('function' == typeof seqFn) {
  218. debug('executing send callback');
  219. seqFn(self.transport);
  220. } else if (Array.isArray(seqFn)) {
  221. debug('executing batch send callback');
  222. for (var l = seqFn.length, i = 0; i < l; i++) {
  223. if ('function' == typeof seqFn[i]) {
  224. seqFn[i](self.transport);
  225. }
  226. }
  227. }
  228. }
  229. });
  230. };
  231. /**
  232. * Sends a message packet.
  233. *
  234. * @param {String} message
  235. * @param {Function} callback
  236. * @return {Socket} for chaining
  237. * @api public
  238. */
  239. Socket.prototype.send =
  240. Socket.prototype.write = function(data, callback){
  241. this.sendPacket('message', data, callback);
  242. return this;
  243. };
  244. /**
  245. * Sends a packet.
  246. *
  247. * @param {String} packet type
  248. * @param {String} optional, data
  249. * @api private
  250. */
  251. Socket.prototype.sendPacket = function (type, data, callback) {
  252. if ('closing' != this.readyState) {
  253. debug('sending packet "%s" (%s)', type, data);
  254. var packet = { type: type };
  255. if (data) packet.data = data;
  256. // exports packetCreate event
  257. this.emit('packetCreate', packet);
  258. this.writeBuffer.push(packet);
  259. //add send callback to object
  260. this.packetsFn.push(callback);
  261. this.flush();
  262. }
  263. };
  264. /**
  265. * Attempts to flush the packets buffer.
  266. *
  267. * @api private
  268. */
  269. Socket.prototype.flush = function () {
  270. if ('closed' != this.readyState && this.transport.writable
  271. && this.writeBuffer.length) {
  272. debug('flushing buffer to transport');
  273. this.emit('flush', this.writeBuffer);
  274. this.server.emit('flush', this, this.writeBuffer);
  275. var wbuf = this.writeBuffer;
  276. this.writeBuffer = [];
  277. if (!this.transport.supportsFraming) {
  278. this.sentCallbackFn.push(this.packetsFn);
  279. } else {
  280. this.sentCallbackFn.push.apply(this.sentCallbackFn, this.packetsFn);
  281. }
  282. this.packetsFn = [];
  283. this.transport.send(wbuf);
  284. this.emit('drain');
  285. this.server.emit('drain', this);
  286. }
  287. };
  288. /**
  289. * Get available upgrades for this socket.
  290. *
  291. * @api private
  292. */
  293. Socket.prototype.getAvailableUpgrades = function () {
  294. var availableUpgrades = [];
  295. var allUpgrades = this.server.upgrades(this.transport.name);
  296. for (var i = 0, l = allUpgrades.length; i < l; ++i) {
  297. var upg = allUpgrades[i];
  298. if (this.server.transports.indexOf(upg) != -1) {
  299. availableUpgrades.push(upg);
  300. }
  301. }
  302. return availableUpgrades;
  303. };
  304. /**
  305. * Closes the socket and underlying transport.
  306. *
  307. * @return {Socket} for chaining
  308. * @api public
  309. */
  310. Socket.prototype.close = function () {
  311. if ('open' == this.readyState) {
  312. this.readyState = 'closing';
  313. var self = this;
  314. this.transport.close(function () {
  315. self.onClose('forced close');
  316. });
  317. }
  318. };