PageRenderTime 124ms CodeModel.GetById 80ms app.highlight 17ms RepoModel.GetById 23ms app.codeStats 0ms

/js/lib/Socket.IO-node/lib/socket.io/client.js

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
JavaScript | 244 lines | 175 code | 35 blank | 34 comment | 38 complexity | 4775b440e0ddb0e3bf56f376ba8e825b MD5 | raw file
  1
  2/*!
  3 * Socket.IO - Client
  4 * Copyright (c) 2010-2011 Guillermo Rauch <guillermo@learnboost.com>
  5 * MIT Licensed
  6 */
  7
  8/**
  9 * Module dependencies.
 10 */
 11
 12var EventEmitter = require('events').EventEmitter
 13  , OutgoingMessage = require('http').OutgoingMessage
 14  , Stream = require('net').Stream
 15  , encode = require('./utils').encode
 16  , decode = require('./utils').decode
 17  , merge = require('./utils').merge
 18  , url = require('url');
 19
 20/**
 21 * Expose `Client`.
 22 */
 23
 24module.exports = Client;
 25
 26/**
 27 * Initialize `Client`.
 28 *
 29 * @api private
 30 */
 31
 32function Client(listener, req, res, options, head) {
 33  this.listener = listener;
 34
 35  var defaults = {
 36      timeout: 8000
 37    , heartbeatInterval: 10000
 38    , closeTimeout: 0
 39  };
 40
 41  options = merge(merge(defaults, this.options || {}), options);
 42  merge(this, options);
 43
 44  this.connections = 0;
 45  this._open = false;
 46  this._heartbeats = 0;
 47  this.connected = false;
 48  this.upgradeHead = head;
 49  this._onConnect(req, res);
 50};
 51
 52/**
 53 * Inherit from `EventEmitter.prototype`.
 54 */
 55
 56Client.prototype.__proto__ = EventEmitter.prototype;
 57
 58/**
 59 * Send the given `message` which is automatically
 60 * converted to JSON unless a string is given.
 61 *
 62 * @param {Object|String} message
 63 * @return {Client} for chaining
 64 * @api public
 65 */
 66
 67Client.prototype.send = function(message){
 68  var state = this.connection.readyState;
 69  if (this._open && ('open' == state || 'writeOnly' == state)) {
 70    this._write(encode(message));
 71  } else {
 72    this._queue(message);
 73  }
 74  return this;
 75};
 76
 77/**
 78 * Broadcast `message` to all the _other_ clients.
 79 *
 80 * @param {Object|String} message
 81 * @return {Client} for chaining
 82 * @api public
 83 */
 84
 85Client.prototype.broadcast = function(message){
 86  if (!('sessionId' in this)) return this;
 87  this.listener.broadcast(message, this.sessionId);
 88  return this;
 89};
 90
 91Client.prototype._onMessage = function(data){
 92  var messages = decode(data);
 93  if (messages === false) return this.listener.log('Bad message received from client ' + this.sessionId);
 94  for (var i = 0, l = messages.length, frame; i < l; i++){
 95    frame = messages[i].substr(0, 3);
 96    switch (frame){
 97      case '~h~':
 98        return this._onHeartbeat(messages[i].substr(3));
 99      case '~j~':
100        try {
101          messages[i] = JSON.parse(messages[i].substr(3));
102        } catch(e) {
103          messages[i] = {};
104        }
105        break;
106    }
107    this.emit('message', messages[i]);
108    this.listener._onClientMessage(messages[i], this);
109  }
110};
111
112Client.prototype._onConnect = function(req, res){
113  var self = this
114    , attachConnection = !this.connection;
115
116  this.request = req;
117  this.response = res;
118  this.connection = req.connection;
119
120  if(!attachConnection) attachConnection = !attachConnection && this.connection.eventsAttached === undefined;
121  this.connection.eventsAttached = true;
122  
123  if (attachConnection){
124    function destroyConnection(){
125      self._onClose();
126      self.connection && self.connection.destroy()
127    };
128    this.connection.addListener('end', destroyConnection);
129    this.connection.addListener('timeout', destroyConnection);
130    this.connection.addListener('error', destroyConnection);
131    }
132  
133  if (req){
134    function destroyRequest(){
135      req.destroy && req.destroy();
136    };
137    req.addListener('error', destroyRequest);
138    req.addListener('timeout', destroyRequest);
139    if (res){
140      function destroyResponse(){
141        res.destroy && res.destroy();
142      }
143      res.addListener('error', destroyResponse);
144      res.addListener('timeout', destroyResponse);
145    }
146    if (this._disconnectTimeout) clearTimeout(this._disconnectTimeout);
147  }
148};
149
150
151Client.prototype._payload = function(){
152  var payload = [];
153  
154  this.connections++;
155  this.connected = true;
156  this._open = true;
157  
158  if (!this.handshaked){
159    this._generateSessionId();
160    payload.push(this.sessionId);
161    this.handshaked = true;
162  }
163  
164  payload = payload.concat(this._writeQueue || []);
165  this._writeQueue = [];
166
167  if (payload.length) this._write(encode(payload));
168  if (this.connections === 1) this.listener._onClientConnect(this);
169  if (this.timeout) this._heartbeat();
170};
171  
172Client.prototype._heartbeat = function(){
173  var self = this;
174  this._heartbeatInterval = setTimeout(function(){
175    self.send('~h~' + ++self._heartbeats);
176    self._heartbeatTimeout = setTimeout(function(){
177      self._onClose();
178    }, self.timeout);
179  }, self.heartbeatInterval);
180};
181  
182Client.prototype._onHeartbeat = function(h){
183  if (h == this._heartbeats){
184    clearTimeout(this._heartbeatTimeout);
185    this._heartbeat();
186  }
187};
188
189Client.prototype._onClose = function(skipDisconnect){
190  if (!this._open) return this;
191  var self = this;
192  if (this._heartbeatInterval) clearTimeout(this._heartbeatInterval);
193  if (this._heartbeatTimeout) clearTimeout(this._heartbeatTimeout);
194  this._open = false;
195  this.request = null;
196  this.response = null;
197  if (skipDisconnect !== false){
198    if (this.handshaked){
199      this._disconnectTimeout = setTimeout(function(){
200        self._onDisconnect();
201      }, this.closeTimeout);
202    } else
203      this._onDisconnect();
204  }
205};
206
207Client.prototype._onDisconnect = function(){
208  if (this._open) this._onClose(true);
209  if (this._disconnectTimeout) clearTimeout(this._disconnectTimeout);
210  this._writeQueue = [];
211  this.connected = false;
212  if (this.handshaked){
213    this.emit('disconnect');
214    this.listener._onClientDisconnect(this);
215    this.handshaked = false;
216  }
217};
218
219Client.prototype._queue = function(message){
220  this._writeQueue = this._writeQueue || [];
221  this._writeQueue.push(message);
222  return this;
223};
224
225Client.prototype._generateSessionId = function(){
226  this.sessionId = Math.random().toString().substr(2);
227  return this;
228};
229
230Client.prototype._verifyOrigin = function(origin){
231  var origins = this.listener.origins;
232  if (origins.indexOf('*:*') !== -1) {
233    return true;
234  }
235  if (origin) {
236    try {
237      var parts = url.parse(origin);
238      return origins.indexOf(parts.host + ':' + parts.port) !== -1 ||
239          origins.indexOf(parts.host + ':*') !== -1 ||
240          origins.indexOf('*:' + parts.port) !== -1;  
241    } catch (ex) {}
242  }
243  return false;
244};