浏览代码

progress on refactor

ericz 12 年之前
父节点
当前提交
d71d0b3975
共有 3 个文件被更改,包括 182 次插入115 次删除
  1. 109 101
      lib/peer.js
  2. 2 3
      lib/socket.js
  3. 71 11
      lib/util.js

+ 109 - 101
lib/peer.js

@@ -5,86 +5,84 @@ function Peer(id, options) {
   if (!(this instanceof Peer)) return new Peer(id, options);
   EventEmitter.call(this);
 
+  // Deal with overloading
   if (id && id.constructor == Object) {
     options = id;
     id = undefined;
+  } else {
+    // Ensure id is a string
+    id = id.toString();
   }
-
+  //
+  
+  // Configurize options
   options = util.extend({
-    debug: false,
+    debug: 0, // 1: Errors, 2: Warnings, 3: All logs
     host: '0.peerjs.com',
     port: 9000,
     key: 'peerjs',
     config: { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] }
   }, options);
-  this._options = options;
-  util.debug = options.debug;
-
-  // First check if browser can use PeerConnection/DataChannels.
-  // TODO: when media is supported, lower browser version limit and move DC
-  // check to where `connect` is called.
-  var self = this;
-  if (!util.isBrowserCompatible()) {
-    util.setZeroTimeout(function() {
-      self._abort('browser-incompatible', 'The current browser does not support WebRTC DataChannels');
-    });
-    return;
-  }
-
+  this.options = options;
   // Detect relative URL host.
   if (options.host === '/') {
     options.host = window.location.hostname;
   }
-
-  // Ensure alphanumeric_-
-  if (id && !/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(id)) {
-    util.setZeroTimeout(function() {
-      self._abort('invalid-id', 'ID "' + id + '" is invalid');
-    });
+  // Set whether we use SSL to same as current host
+  if (options.secure === undefined) {
+    options.secure = util.isSecure();  
+  }
+  // TODO: document this feature
+  // Set a custom log function if present
+  if (options.logFunction) {
+    util.setLogFunction(options.logFunction):
+  }
+  util.setLogLevel(options.debug);
+  //
+  
+  // Sanity checks
+  // Ensure WebRTC supported
+  if (!util.supports.audioVideo && !util.supports.data ) {
+    this._delayedAbort('browser-incompatible', 'The current browser does not support WebRTC');
     return;
   }
-  if (options.key && !/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(options.key)) {
-    util.setZeroTimeout(function() {
-      self._abort('invalid-key', 'API KEY "' + options.key + '" is invalid');
-    });
+  // Ensure alphanumeric id
+  if (!util.validateId(id)) {
+    this._delayedAbort('invalid-id', 'ID "' + id + '" is invalid');
     return;
   }
-
-  this._secure = util.isSecure();
-  // Errors for now because no support for SSL on cloud server.
-  if (this._secure && options.host === '0.peerjs.com') {
-    util.setZeroTimeout(function() {
-      self._abort('ssl-unavailable',
-        'The cloud server currently does not support HTTPS. Please run your own PeerServer to use HTTPS.');
-    });
+  // Ensure valid key
+  if (!util.validateKey(options.key)) {
+    this._delayedAbort('invalid-key', 'API KEY "' + options.key + '" is invalid');
     return;
   }
-
+  // Ensure not using unsecure cloud server on SSL page
+  if (options.secure && options.host === '0.peerjs.com') {
+    this._delayedAbort('ssl-unavailable',
+      'The cloud server currently does not support HTTPS. Please run your own PeerServer to use HTTPS.');
+    return;
+  }
+  //
+  
   // States.
   this.destroyed = false; // Connections have been killed
   this.disconnected = false; // Connection to PeerServer killed but P2P connections still active
-
-  // DataConnections for this peer.
-  this.connections = {};
+  //
   
-  // MediaConnections for this peer
-  this.calls = {}
+  // References
+  this.connections = {}; // DataConnections for this peer.
+  this.calls = {}; // MediaConnections for this peer
+  //
   
-  // Connection managers.
-  // peer => { 
-  //   nextId: unique number to use for next manager created
-  //   dataManager: the last created data manager, for multiplexing data connections
-  //   managers: { id: manager} }
-  // }
-  this._managers = {};
+  // Internal references
+  this._managers = {}; // Managers for peer connections
+  this._queued = []; // Queued connections to make.
+  //
 
-  // Queued connections to make.
-  this._queued = [];
 
-  // Init immediately if ID is given, otherwise ask server for ID
-  this.id = null;
+  // Start the connections
   if (id) {
-    this._initialize(id.toString());
+    this._initialize(id);
   } else {
     this._retrieveId();
   }
@@ -94,57 +92,53 @@ util.inherits(Peer, EventEmitter);
 
 Peer.prototype._retrieveId = function(cb) {
   var self = this;
-  try {
-    var http = new XMLHttpRequest();
-    var protocol = this._secure ? 'https://' : 'http://';
-    var url = protocol + this._options.host + ':' + this._options.port + '/' + this._options.key + '/id';
-    var queryString = '?ts=' + new Date().getTime() + '' + Math.random();
-    url += queryString;
-    // If there's no ID we need to wait for one before trying to init socket.
-    http.open('get', url, true);
-    http.onreadystatechange = function() {
-      if (http.readyState === 4) {
-        if (http.status !== 200) {
-          throw 'Retrieve ID response not 200';
-          return;
-        }
-        self.id = http.responseText;
-        self._initialize(self.id);
-      }
-    };
-    http.send(null);
-  } catch(e) {
-    this._abort('server-error', 'Could not get an ID from the server');
+  var http = new XMLHttpRequest();
+  var protocol = this.options.secure ? 'https://' : 'http://';
+  var url = protocol + this.options.host + ':' + this.options.port + '/' + this.options.key + '/id';
+  var queryString = '?ts=' + new Date().getTime() + '' + Math.random();
+  url += queryString;
+  // If there's no ID we need to wait for one before trying to init socket.
+  http.open('get', url, true);
+  http.onerror = function(e) { 
+    util.error('Error retrieving ID', e);
+    self._abort('server-error', 'Could not get an ID from the server');
   }
+  http.onreadystatechange = function() {
+    if (http.readyState !== 4) {
+      return;
+    }
+    if (http.status !== 200) {
+      http.onerror();
+      return;
+    }
+    self._initialize(http.responseText);
+  };
+  http.send(null);
 };
 
 
 Peer.prototype._initialize = function(id) {
   var self = this;
   this.id = id;
-  this._socket = new Socket(this._options.host, this._options.port, this._options.key, this.id);
-  this._socket.on('message', function(data) {
-    self._handleServerJSONMessage(data);
+  this.socket = new Socket(this.secure, this.options.host, this.options.port, this.options.key, this.id);
+  this.socket.on('message', function(data) {
+    self._dispatchMessage(data);
   });
-  this._socket.on('error', function(error) {
-    util.log(error);
+  this.socket.on('error', function(error) {
     self._abort('socket-error', error);
   });
-  this._socket.on('close', function() {
-    var msg = 'Underlying socket has closed';
-    util.log('error', msg);
-    self._abort('socket-closed', msg);
+  this.socket.on('close', function() {
+    // TODO: What if we disconnected on purpose?
+    self._abort('socket-closed', 'Underlying socket has closed');
   });
-  this._socket.start();
+  this.socket.start();
 }
 
 
-Peer.prototype._handleServerJSONMessage = function(message) {
-  var peer = message.src;
-  var managerId = message.manager;
-  var manager = this._getManager(peer, managerId);
-  var payload = message.payload;
-  switch (message.type) {
+Peer.prototype._dispatchMessage = function(message) {
+  var type = message.type;
+  // Message types that don't involve a peer
+  switch (type) {
     case 'OPEN':
       this._processQueue();
       this.emit('open', this.id);
@@ -155,11 +149,22 @@ Peer.prototype._handleServerJSONMessage = function(message) {
     case 'ID-TAKEN':
       this._abort('unavailable-id', 'ID `'+this.id+'` is taken');
       break;
+    case 'INVALID-KEY':
+      this._abort('invalid-key', 'API KEY "' + this._key + '" is invalid');
+      break;
+  }
+  
+  var peer = message.src;
+  var managerId = message.manager;
+  var manager = this._getManager(peer, managerId);
+  var payload = message.payload;
+  switch (message.type) {
+   
     case 'OFFER':
       var options = {
         sdp: payload.sdp,
         labels: payload.labels,
-        config: this._options.config
+        config: this.options.config
       };
       // Either forward to or create new manager
       
@@ -193,11 +198,8 @@ Peer.prototype._handleServerJSONMessage = function(message) {
         }
       }
       break;
-    case 'INVALID-KEY':
-      this._abort('invalid-key', 'API KEY "' + this._key + '" is invalid');
-      break;
     default:
-      util.log('Unrecognized message type:', message.type);
+      util.warn('Unrecognized message type:', message.type);
       break;
   }
 };
@@ -206,7 +208,7 @@ Peer.prototype._handleServerJSONMessage = function(message) {
 Peer.prototype._processQueue = function() {
   while (this._queued.length > 0) {
     var manager = this._queued.pop();
-    manager.initialize(this.id, this._socket);
+    manager.initialize(this.id, this.socket);
   }
 };
 
@@ -260,7 +262,7 @@ Peer.prototype._createManager = function(managerId, peer, options) {
   }
 
   options = util.extend({
-    config: this._options.config
+    config: this.options.config
   }, options);
 
   if (!this._managers[peer]) {
@@ -270,8 +272,8 @@ Peer.prototype._createManager = function(managerId, peer, options) {
   managerId = managerId || peer + this._managers[peer].nextId++;
   
   var manager = new ConnectionManager(managerId, peer, options);
-  if (!!this.id && !!this._socket) {
-    manager.initialize(this.id, this._socket);
+  if (!!this.id && !!this.socket) {
+    manager.initialize(this.id, this.socket);
   } else {
     this._queued.push(manager);
   }
@@ -298,10 +300,16 @@ Peer.prototype.call = function(peer, stream, options) {
 
 
 
+Peer.prototype._delayedAbort = function(type, message) {
+  var self = this;
+  util.setZeroTimeout(function(){
+    self._abort(type, message);
+  });
+}
 
 /** Destroys the Peer and emits an error message. */
 Peer.prototype._abort = function(type, message) {
-  util.log('Aborting. Error:', message);
+  util.error('Aborting. Error:', message);
   var err = new Error(message);
   err.type = type;
   this.destroy();
@@ -348,8 +356,8 @@ Peer.prototype._cleanup = function() {
  */
 Peer.prototype.disconnect = function() {
   if (!this.disconnected) {
-    if (!!this._socket) {
-      this._socket.close();
+    if (!!this.socket) {
+      this.socket.close();
     }
     this.id = null;
     this.disconnected = true;

+ 2 - 3
lib/socket.js

@@ -2,8 +2,8 @@
  * An abstraction on top of WebSockets and XHR streaming to provide fastest
  * possible connection for peers.
  */
-function Socket(host, port, key, id) {
-  if (!(this instanceof Socket)) return new Socket(host, port, key, id);
+function Socket(secure, host, port, key, id) {
+  if (!(this instanceof Socket)) return new Socket(secure, host, port, key, id);
   EventEmitter.call(this);
 
   this._id = id;
@@ -11,7 +11,6 @@ function Socket(host, port, key, id) {
 
   this.disconnected = false;
 
-  var secure = util.isSecure();
   var protocol = secure ? 'https://' : 'http://';
   var wsProtocol = secure ? 'wss://' : 'ws://';
   this._httpUrl = protocol + host + ':' + port + '/' + key + '/' + id + '/' + token;

+ 71 - 11
lib/util.js

@@ -1,5 +1,76 @@
 var util = {
 
+  noop: function() {},
+  
+  // Logging logic
+  logLevel: 0,
+  setLogLevel: function(level) {
+    if (level = parseInt(level, 10)) {
+      util.logLevel = level;
+    } else {
+      // If they are using truthy/falsy values for debug
+      util.logLevel = (!!level) ? 3 : 0;
+    }
+    util.log = util.warn = util.error = util.noop;
+    if (util.logLevel > 0) {
+      util.error = util._printWith('ERROR');
+    }
+    if (util.logLevel > 1) {
+      util.warn = util._printWith('WARNING');
+    }
+    if (util.logLevel > 2) {
+      util.log = util._print;
+    }
+  },
+  setLogFunction: function(fn) {
+    util._print = fn;
+  },
+  _printWith: function(prefix) {
+    return function() {}
+      var copy = Array.prototype.slice.call(arguments);
+      copy.unshift(prefix);
+      util._print.apply(util, copy);
+    };
+  },
+  _print: function () {
+    var err = false;
+    var copy = Array.prototype.slice.call(arguments);
+    copy.unshift('PeerJS: ');
+    for (var i = 0, l = copy.length; i < l; i++){
+      if (copy[i] instanceof Error) {
+        copy[i] = '(' + copy[i].name + ') ' + copy[i].message;
+        err = true;
+      }
+    }
+    err ? console.error.apply(console, copy) : console.log.apply(console, copy);  
+  },
+  //
+  
+  // Lists which features are supported
+  // Temporarily set everything to true
+  supports: (function(){
+    return {
+      audioVideo: true,
+      data: true,
+      binaryData: true,
+      reliableData: true
+    };
+  }()),
+  //
+  
+  // Ensure alphanumeric ids
+  validateId: function(id) {
+    // Allow empty ids
+    return !id || /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(id);
+  },
+  
+  validateKey: function(key) {
+    // Allow empty keys
+    return !key || /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(key);
+  },
+  
+  
+  // OLD
   chromeCompatible: true,
   firefoxCompatible: true,
   chromeVersion: 26,
@@ -120,17 +191,6 @@ var util = {
     return false;
   },
   
-  // Lists which features are supported
-  // Temporarily set everything to true
-  supports: (function(){
-    return {
-      audioVideo: true,
-      data: true,
-      binaryData: true,
-      reliableData: true
-    }
-  }()),
-  
   
   isSecure: function() {
     return location.protocol === 'https:';