annotate node_modules/socket.io/lib/transport.js @ 70:753414e075a0

danger stash pop
author tzara <rc-web@kiben.net>
date Sat, 26 Oct 2013 13:43:08 +0000
parents 333afcfd3f3a
children
rev   line source
rc-web@69 1
rc-web@69 2 /*!
rc-web@69 3 * socket.io-node
rc-web@69 4 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
rc-web@69 5 * MIT Licensed
rc-web@69 6 */
rc-web@69 7
rc-web@69 8 /**
rc-web@69 9 * Module dependencies.
rc-web@69 10 */
rc-web@69 11
rc-web@69 12 var parser = require('./parser');
rc-web@69 13
rc-web@69 14 /**
rc-web@69 15 * Expose the constructor.
rc-web@69 16 */
rc-web@69 17
rc-web@69 18 exports = module.exports = Transport;
rc-web@69 19
rc-web@69 20 /**
rc-web@69 21 * Transport constructor.
rc-web@69 22 *
rc-web@69 23 * @api public
rc-web@69 24 */
rc-web@69 25
rc-web@69 26 function Transport (mng, data, req) {
rc-web@69 27 this.manager = mng;
rc-web@69 28 this.id = data.id;
rc-web@69 29 this.disconnected = false;
rc-web@69 30 this.drained = true;
rc-web@69 31 this.handleRequest(req);
rc-web@69 32 };
rc-web@69 33
rc-web@69 34 /**
rc-web@69 35 * Access the logger.
rc-web@69 36 *
rc-web@69 37 * @api public
rc-web@69 38 */
rc-web@69 39
rc-web@69 40 Transport.prototype.__defineGetter__('log', function () {
rc-web@69 41 return this.manager.log;
rc-web@69 42 });
rc-web@69 43
rc-web@69 44 /**
rc-web@69 45 * Access the store.
rc-web@69 46 *
rc-web@69 47 * @api public
rc-web@69 48 */
rc-web@69 49
rc-web@69 50 Transport.prototype.__defineGetter__('store', function () {
rc-web@69 51 return this.manager.store;
rc-web@69 52 });
rc-web@69 53
rc-web@69 54 /**
rc-web@69 55 * Handles a request when it's set.
rc-web@69 56 *
rc-web@69 57 * @api private
rc-web@69 58 */
rc-web@69 59
rc-web@69 60 Transport.prototype.handleRequest = function (req) {
rc-web@69 61 this.log.debug('setting request', req.method, req.url);
rc-web@69 62 this.req = req;
rc-web@69 63
rc-web@69 64 if (req.method == 'GET') {
rc-web@69 65 this.socket = req.socket;
rc-web@69 66 this.open = true;
rc-web@69 67 this.drained = true;
rc-web@69 68 this.setHeartbeatInterval();
rc-web@69 69
rc-web@69 70 this.setHandlers();
rc-web@69 71 this.onSocketConnect();
rc-web@69 72 }
rc-web@69 73 };
rc-web@69 74
rc-web@69 75 /**
rc-web@69 76 * Called when a connection is first set.
rc-web@69 77 *
rc-web@69 78 * @api private
rc-web@69 79 */
rc-web@69 80
rc-web@69 81 Transport.prototype.onSocketConnect = function () { };
rc-web@69 82
rc-web@69 83 /**
rc-web@69 84 * Sets transport handlers
rc-web@69 85 *
rc-web@69 86 * @api private
rc-web@69 87 */
rc-web@69 88
rc-web@69 89 Transport.prototype.setHandlers = function () {
rc-web@69 90 var self = this;
rc-web@69 91
rc-web@69 92 // we need to do this in a pub/sub way since the client can POST the message
rc-web@69 93 // over a different socket (ie: different Transport instance)
rc-web@69 94 this.store.subscribe('heartbeat-clear:' + this.id, function () {
rc-web@69 95 self.onHeartbeatClear();
rc-web@69 96 });
rc-web@69 97
rc-web@69 98 this.store.subscribe('disconnect-force:' + this.id, function () {
rc-web@69 99 self.onForcedDisconnect();
rc-web@69 100 });
rc-web@69 101
rc-web@69 102 this.store.subscribe('dispatch:' + this.id, function (packet, volatile) {
rc-web@69 103 self.onDispatch(packet, volatile);
rc-web@69 104 });
rc-web@69 105
rc-web@69 106 this.bound = {
rc-web@69 107 end: this.onSocketEnd.bind(this)
rc-web@69 108 , close: this.onSocketClose.bind(this)
rc-web@69 109 , error: this.onSocketError.bind(this)
rc-web@69 110 , drain: this.onSocketDrain.bind(this)
rc-web@69 111 };
rc-web@69 112
rc-web@69 113 this.socket.on('end', this.bound.end);
rc-web@69 114 this.socket.on('close', this.bound.close);
rc-web@69 115 this.socket.on('error', this.bound.error);
rc-web@69 116 this.socket.on('drain', this.bound.drain);
rc-web@69 117
rc-web@69 118 this.handlersSet = true;
rc-web@69 119 };
rc-web@69 120
rc-web@69 121 /**
rc-web@69 122 * Removes transport handlers
rc-web@69 123 *
rc-web@69 124 * @api private
rc-web@69 125 */
rc-web@69 126
rc-web@69 127 Transport.prototype.clearHandlers = function () {
rc-web@69 128 if (this.handlersSet) {
rc-web@69 129 this.store.unsubscribe('disconnect-force:' + this.id);
rc-web@69 130 this.store.unsubscribe('heartbeat-clear:' + this.id);
rc-web@69 131 this.store.unsubscribe('dispatch:' + this.id);
rc-web@69 132
rc-web@69 133 this.socket.removeListener('end', this.bound.end);
rc-web@69 134 this.socket.removeListener('close', this.bound.close);
rc-web@69 135 this.socket.removeListener('error', this.bound.error);
rc-web@69 136 this.socket.removeListener('drain', this.bound.drain);
rc-web@69 137 }
rc-web@69 138 };
rc-web@69 139
rc-web@69 140 /**
rc-web@69 141 * Called when the connection dies
rc-web@69 142 *
rc-web@69 143 * @api private
rc-web@69 144 */
rc-web@69 145
rc-web@69 146 Transport.prototype.onSocketEnd = function () {
rc-web@69 147 this.end('socket end');
rc-web@69 148 };
rc-web@69 149
rc-web@69 150 /**
rc-web@69 151 * Called when the connection dies
rc-web@69 152 *
rc-web@69 153 * @api private
rc-web@69 154 */
rc-web@69 155
rc-web@69 156 Transport.prototype.onSocketClose = function (error) {
rc-web@69 157 this.end(error ? 'socket error' : 'socket close');
rc-web@69 158 };
rc-web@69 159
rc-web@69 160 /**
rc-web@69 161 * Called when the connection has an error.
rc-web@69 162 *
rc-web@69 163 * @api private
rc-web@69 164 */
rc-web@69 165
rc-web@69 166 Transport.prototype.onSocketError = function (err) {
rc-web@69 167 if (this.open) {
rc-web@69 168 this.socket.destroy();
rc-web@69 169 this.onClose();
rc-web@69 170 }
rc-web@69 171
rc-web@69 172 this.log.info('socket error ' + err.stack);
rc-web@69 173 };
rc-web@69 174
rc-web@69 175 /**
rc-web@69 176 * Called when the connection is drained.
rc-web@69 177 *
rc-web@69 178 * @api private
rc-web@69 179 */
rc-web@69 180
rc-web@69 181 Transport.prototype.onSocketDrain = function () {
rc-web@69 182 this.drained = true;
rc-web@69 183 };
rc-web@69 184
rc-web@69 185 /**
rc-web@69 186 * Called upon receiving a heartbeat packet.
rc-web@69 187 *
rc-web@69 188 * @api private
rc-web@69 189 */
rc-web@69 190
rc-web@69 191 Transport.prototype.onHeartbeatClear = function () {
rc-web@69 192 this.clearHeartbeatTimeout();
rc-web@69 193 this.setHeartbeatInterval();
rc-web@69 194 };
rc-web@69 195
rc-web@69 196 /**
rc-web@69 197 * Called upon a forced disconnection.
rc-web@69 198 *
rc-web@69 199 * @api private
rc-web@69 200 */
rc-web@69 201
rc-web@69 202 Transport.prototype.onForcedDisconnect = function () {
rc-web@69 203 if (!this.disconnected) {
rc-web@69 204 this.log.info('transport end by forced client disconnection');
rc-web@69 205 if (this.open) {
rc-web@69 206 this.packet({ type: 'disconnect' });
rc-web@69 207 }
rc-web@69 208 this.end('booted');
rc-web@69 209 }
rc-web@69 210 };
rc-web@69 211
rc-web@69 212 /**
rc-web@69 213 * Dispatches a packet.
rc-web@69 214 *
rc-web@69 215 * @api private
rc-web@69 216 */
rc-web@69 217
rc-web@69 218 Transport.prototype.onDispatch = function (packet, volatile) {
rc-web@69 219 if (volatile) {
rc-web@69 220 this.writeVolatile(packet);
rc-web@69 221 } else {
rc-web@69 222 this.write(packet);
rc-web@69 223 }
rc-web@69 224 };
rc-web@69 225
rc-web@69 226 /**
rc-web@69 227 * Sets the close timeout.
rc-web@69 228 */
rc-web@69 229
rc-web@69 230 Transport.prototype.setCloseTimeout = function () {
rc-web@69 231 if (!this.closeTimeout) {
rc-web@69 232 var self = this;
rc-web@69 233
rc-web@69 234 this.closeTimeout = setTimeout(function () {
rc-web@69 235 self.log.debug('fired close timeout for client', self.id);
rc-web@69 236 self.closeTimeout = null;
rc-web@69 237 self.end('close timeout');
rc-web@69 238 }, this.manager.get('close timeout') * 1000);
rc-web@69 239
rc-web@69 240 this.log.debug('set close timeout for client', this.id);
rc-web@69 241 }
rc-web@69 242 };
rc-web@69 243
rc-web@69 244 /**
rc-web@69 245 * Clears the close timeout.
rc-web@69 246 */
rc-web@69 247
rc-web@69 248 Transport.prototype.clearCloseTimeout = function () {
rc-web@69 249 if (this.closeTimeout) {
rc-web@69 250 clearTimeout(this.closeTimeout);
rc-web@69 251 this.closeTimeout = null;
rc-web@69 252
rc-web@69 253 this.log.debug('cleared close timeout for client', this.id);
rc-web@69 254 }
rc-web@69 255 };
rc-web@69 256
rc-web@69 257 /**
rc-web@69 258 * Sets the heartbeat timeout
rc-web@69 259 */
rc-web@69 260
rc-web@69 261 Transport.prototype.setHeartbeatTimeout = function () {
rc-web@69 262 if (!this.heartbeatTimeout && this.manager.enabled('heartbeats')) {
rc-web@69 263 var self = this;
rc-web@69 264
rc-web@69 265 this.heartbeatTimeout = setTimeout(function () {
rc-web@69 266 self.log.debug('fired heartbeat timeout for client', self.id);
rc-web@69 267 self.heartbeatTimeout = null;
rc-web@69 268 self.end('heartbeat timeout');
rc-web@69 269 }, this.manager.get('heartbeat timeout') * 1000);
rc-web@69 270
rc-web@69 271 this.log.debug('set heartbeat timeout for client', this.id);
rc-web@69 272 }
rc-web@69 273 };
rc-web@69 274
rc-web@69 275 /**
rc-web@69 276 * Clears the heartbeat timeout
rc-web@69 277 *
rc-web@69 278 * @param text
rc-web@69 279 */
rc-web@69 280
rc-web@69 281 Transport.prototype.clearHeartbeatTimeout = function () {
rc-web@69 282 if (this.heartbeatTimeout && this.manager.enabled('heartbeats')) {
rc-web@69 283 clearTimeout(this.heartbeatTimeout);
rc-web@69 284 this.heartbeatTimeout = null;
rc-web@69 285 this.log.debug('cleared heartbeat timeout for client', this.id);
rc-web@69 286 }
rc-web@69 287 };
rc-web@69 288
rc-web@69 289 /**
rc-web@69 290 * Sets the heartbeat interval. To be called when a connection opens and when
rc-web@69 291 * a heartbeat is received.
rc-web@69 292 *
rc-web@69 293 * @api private
rc-web@69 294 */
rc-web@69 295
rc-web@69 296 Transport.prototype.setHeartbeatInterval = function () {
rc-web@69 297 if (!this.heartbeatInterval && this.manager.enabled('heartbeats')) {
rc-web@69 298 var self = this;
rc-web@69 299
rc-web@69 300 this.heartbeatInterval = setTimeout(function () {
rc-web@69 301 self.heartbeat();
rc-web@69 302 self.heartbeatInterval = null;
rc-web@69 303 }, this.manager.get('heartbeat interval') * 1000);
rc-web@69 304
rc-web@69 305 this.log.debug('set heartbeat interval for client', this.id);
rc-web@69 306 }
rc-web@69 307 };
rc-web@69 308
rc-web@69 309 /**
rc-web@69 310 * Clears all timeouts.
rc-web@69 311 *
rc-web@69 312 * @api private
rc-web@69 313 */
rc-web@69 314
rc-web@69 315 Transport.prototype.clearTimeouts = function () {
rc-web@69 316 this.clearCloseTimeout();
rc-web@69 317 this.clearHeartbeatTimeout();
rc-web@69 318 this.clearHeartbeatInterval();
rc-web@69 319 };
rc-web@69 320
rc-web@69 321 /**
rc-web@69 322 * Sends a heartbeat
rc-web@69 323 *
rc-web@69 324 * @api private
rc-web@69 325 */
rc-web@69 326
rc-web@69 327 Transport.prototype.heartbeat = function () {
rc-web@69 328 if (this.open) {
rc-web@69 329 this.log.debug('emitting heartbeat for client', this.id);
rc-web@69 330 this.packet({ type: 'heartbeat' });
rc-web@69 331 this.setHeartbeatTimeout();
rc-web@69 332 }
rc-web@69 333
rc-web@69 334 return this;
rc-web@69 335 };
rc-web@69 336
rc-web@69 337 /**
rc-web@69 338 * Handles a message.
rc-web@69 339 *
rc-web@69 340 * @param {Object} packet object
rc-web@69 341 * @api private
rc-web@69 342 */
rc-web@69 343
rc-web@69 344 Transport.prototype.onMessage = function (packet) {
rc-web@69 345 var current = this.manager.transports[this.id];
rc-web@69 346
rc-web@69 347 if ('heartbeat' == packet.type) {
rc-web@69 348 this.log.debug('got heartbeat packet');
rc-web@69 349
rc-web@69 350 if (current && current.open) {
rc-web@69 351 current.onHeartbeatClear();
rc-web@69 352 } else {
rc-web@69 353 this.store.publish('heartbeat-clear:' + this.id);
rc-web@69 354 }
rc-web@69 355 } else {
rc-web@69 356 if ('disconnect' == packet.type && packet.endpoint == '') {
rc-web@69 357 this.log.debug('got disconnection packet');
rc-web@69 358
rc-web@69 359 if (current) {
rc-web@69 360 current.onForcedDisconnect();
rc-web@69 361 } else {
rc-web@69 362 this.store.publish('disconnect-force:' + this.id);
rc-web@69 363 }
rc-web@69 364
rc-web@69 365 return;
rc-web@69 366 }
rc-web@69 367
rc-web@69 368 if (packet.id && packet.ack != 'data') {
rc-web@69 369 this.log.debug('acknowledging packet automatically');
rc-web@69 370
rc-web@69 371 var ack = parser.encodePacket({
rc-web@69 372 type: 'ack'
rc-web@69 373 , ackId: packet.id
rc-web@69 374 , endpoint: packet.endpoint || ''
rc-web@69 375 });
rc-web@69 376
rc-web@69 377 if (current && current.open) {
rc-web@69 378 current.onDispatch(ack);
rc-web@69 379 } else {
rc-web@69 380 this.manager.onClientDispatch(this.id, ack);
rc-web@69 381 this.store.publish('dispatch:' + this.id, ack);
rc-web@69 382 }
rc-web@69 383 }
rc-web@69 384
rc-web@69 385 // handle packet locally or publish it
rc-web@69 386 if (current) {
rc-web@69 387 this.manager.onClientMessage(this.id, packet);
rc-web@69 388 } else {
rc-web@69 389 this.store.publish('message:' + this.id, packet);
rc-web@69 390 }
rc-web@69 391 }
rc-web@69 392 };
rc-web@69 393
rc-web@69 394 /**
rc-web@69 395 * Clears the heartbeat interval
rc-web@69 396 *
rc-web@69 397 * @api private
rc-web@69 398 */
rc-web@69 399
rc-web@69 400 Transport.prototype.clearHeartbeatInterval = function () {
rc-web@69 401 if (this.heartbeatInterval && this.manager.enabled('heartbeats')) {
rc-web@69 402 clearTimeout(this.heartbeatInterval);
rc-web@69 403 this.heartbeatInterval = null;
rc-web@69 404 this.log.debug('cleared heartbeat interval for client', this.id);
rc-web@69 405 }
rc-web@69 406 };
rc-web@69 407
rc-web@69 408 /**
rc-web@69 409 * Finishes the connection and makes sure client doesn't reopen
rc-web@69 410 *
rc-web@69 411 * @api private
rc-web@69 412 */
rc-web@69 413
rc-web@69 414 Transport.prototype.disconnect = function (reason) {
rc-web@69 415 this.packet({ type: 'disconnect' });
rc-web@69 416 this.end(reason);
rc-web@69 417
rc-web@69 418 return this;
rc-web@69 419 };
rc-web@69 420
rc-web@69 421 /**
rc-web@69 422 * Closes the connection.
rc-web@69 423 *
rc-web@69 424 * @api private
rc-web@69 425 */
rc-web@69 426
rc-web@69 427 Transport.prototype.close = function () {
rc-web@69 428 if (this.open) {
rc-web@69 429 this.doClose();
rc-web@69 430 this.onClose();
rc-web@69 431 }
rc-web@69 432 };
rc-web@69 433
rc-web@69 434 /**
rc-web@69 435 * Called upon a connection close.
rc-web@69 436 *
rc-web@69 437 * @api private
rc-web@69 438 */
rc-web@69 439
rc-web@69 440 Transport.prototype.onClose = function () {
rc-web@69 441 if (this.open) {
rc-web@69 442 this.setCloseTimeout();
rc-web@69 443 this.clearHandlers();
rc-web@69 444 this.open = false;
rc-web@69 445 this.manager.onClose(this.id);
rc-web@69 446 this.store.publish('close', this.id);
rc-web@69 447 }
rc-web@69 448 };
rc-web@69 449
rc-web@69 450 /**
rc-web@69 451 * Cleans up the connection, considers the client disconnected.
rc-web@69 452 *
rc-web@69 453 * @api private
rc-web@69 454 */
rc-web@69 455
rc-web@69 456 Transport.prototype.end = function (reason) {
rc-web@69 457 if (!this.disconnected) {
rc-web@69 458 this.log.info('transport end (' + reason + ')');
rc-web@69 459
rc-web@69 460 var local = this.manager.transports[this.id];
rc-web@69 461
rc-web@69 462 this.close();
rc-web@69 463 this.clearTimeouts();
rc-web@69 464 this.disconnected = true;
rc-web@69 465
rc-web@69 466 if (local) {
rc-web@69 467 this.manager.onClientDisconnect(this.id, reason, true);
rc-web@69 468 } else {
rc-web@69 469 this.store.publish('disconnect:' + this.id, reason);
rc-web@69 470 }
rc-web@69 471 }
rc-web@69 472 };
rc-web@69 473
rc-web@69 474 /**
rc-web@69 475 * Signals that the transport should pause and buffer data.
rc-web@69 476 *
rc-web@69 477 * @api public
rc-web@69 478 */
rc-web@69 479
rc-web@69 480 Transport.prototype.discard = function () {
rc-web@69 481 this.log.debug('discarding transport');
rc-web@69 482 this.discarded = true;
rc-web@69 483 this.clearTimeouts();
rc-web@69 484 this.clearHandlers();
rc-web@69 485
rc-web@69 486 return this;
rc-web@69 487 };
rc-web@69 488
rc-web@69 489 /**
rc-web@69 490 * Writes an error packet with the specified reason and advice.
rc-web@69 491 *
rc-web@69 492 * @param {Number} advice
rc-web@69 493 * @param {Number} reason
rc-web@69 494 * @api public
rc-web@69 495 */
rc-web@69 496
rc-web@69 497 Transport.prototype.error = function (reason, advice) {
rc-web@69 498 this.packet({
rc-web@69 499 type: 'error'
rc-web@69 500 , reason: reason
rc-web@69 501 , advice: advice
rc-web@69 502 });
rc-web@69 503
rc-web@69 504 this.log.warn(reason, advice ? ('client should ' + advice) : '');
rc-web@69 505 this.end('error');
rc-web@69 506 };
rc-web@69 507
rc-web@69 508 /**
rc-web@69 509 * Write a packet.
rc-web@69 510 *
rc-web@69 511 * @api public
rc-web@69 512 */
rc-web@69 513
rc-web@69 514 Transport.prototype.packet = function (obj) {
rc-web@69 515 return this.write(parser.encodePacket(obj));
rc-web@69 516 };
rc-web@69 517
rc-web@69 518 /**
rc-web@69 519 * Writes a volatile message.
rc-web@69 520 *
rc-web@69 521 * @api private
rc-web@69 522 */
rc-web@69 523
rc-web@69 524 Transport.prototype.writeVolatile = function (msg) {
rc-web@69 525 if (this.open) {
rc-web@69 526 if (this.drained) {
rc-web@69 527 this.write(msg);
rc-web@69 528 } else {
rc-web@69 529 this.log.debug('ignoring volatile packet, buffer not drained');
rc-web@69 530 }
rc-web@69 531 } else {
rc-web@69 532 this.log.debug('ignoring volatile packet, transport not open');
rc-web@69 533 }
rc-web@69 534 };