PageRenderTime 58ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

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

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