dataconnection.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /**
  2. * Wraps a DataChannel between two Peers.
  3. */
  4. function DataConnection(peer, options) {
  5. if (!(this instanceof DataConnection)) return new DataConnection(peer, options);
  6. EventEmitter.call(this);
  7. // TODO: perhaps default serialization should be binary-utf8?
  8. options = util.extend({
  9. serialization: 'binary'
  10. }, options);
  11. // Connection is not open yet.
  12. this.open = false;
  13. this.label = options.label;
  14. this.metadata = options.metadata;
  15. this.serialization = options.serialization;
  16. this.peer = peer;
  17. this.reliable = options.reliable;
  18. this.id = options.id; // TODO: or randomly generated ID.
  19. if (options.sdp) {
  20. // TODO: startConnection ideally calls handleSDP with the offer.
  21. // On this side though, do we also need to pass in this peer's ID? Probably.
  22. // Maybe instead of having a global negotiator, have a negotiator instance
  23. // per Peer?
  24. Negotiator.startConnection(this.peer, this.id, options.sdp, options.config);
  25. }
  26. /*this._dc = dc;
  27. if (this._dc) {
  28. this._configureDataChannel();
  29. }*/
  30. };
  31. util.inherits(DataConnection, EventEmitter);
  32. DataConnection.prototype._configureDataChannel = function() {
  33. var self = this;
  34. // TODO: util.supports.binary
  35. if (util.supports.binary) {
  36. // Webkit doesn't support binary yet
  37. this._dc.binaryType = 'arraybuffer';
  38. }
  39. this._dc.onopen = function() {
  40. util.log('Data channel connection success');
  41. self.open = true;
  42. self.emit('open');
  43. };
  44. // Use the Reliable shim for non Firefox browsers
  45. // TODO: util.supports.reliable
  46. if (!util.supports.reliable) {
  47. this._reliable = new Reliable(this._dc, util.debug);
  48. }
  49. if (this._reliable) {
  50. this._reliable.onmessage = function(msg) {
  51. self.emit('data', msg);
  52. };
  53. } else {
  54. this._dc.onmessage = function(e) {
  55. self._handleDataMessage(e);
  56. };
  57. }
  58. this._dc.onclose = function(e) {
  59. util.log('DataChannel closed.');
  60. self.close();
  61. };
  62. };
  63. DataConnection.prototype._cleanup = function() {
  64. if (this._dc && this._dc.readyState !== 'closed') {
  65. this._dc.close();
  66. this._dc = null;
  67. }
  68. this.open = false;
  69. this.emit('close');
  70. };
  71. // Handles a DataChannel message.
  72. DataConnection.prototype._handleDataMessage = function(e) {
  73. var self = this;
  74. var data = e.data;
  75. var datatype = data.constructor;
  76. if (this.serialization === 'binary' || this.serialization === 'binary-utf8') {
  77. if (datatype === Blob) {
  78. // Datatype should never be blob
  79. util.blobToArrayBuffer(data, function(ab) {
  80. data = util.unpack(ab);
  81. self.emit('data', data);
  82. });
  83. return;
  84. } else if (datatype === ArrayBuffer) {
  85. data = util.unpack(data);
  86. } else if (datatype === String) {
  87. // String fallback for binary data for browsers that don't support binary yet
  88. var ab = util.binaryStringToArrayBuffer(data);
  89. data = util.unpack(ab);
  90. }
  91. } else if (this.serialization === 'json') {
  92. data = JSON.parse(data);
  93. }
  94. this.emit('data', data);
  95. }
  96. DataConnection.prototype.addDC = function(dc) {
  97. this._dc = dc;
  98. this._configureDataChannel();
  99. }
  100. /**
  101. * Exposed functionality for users.
  102. */
  103. /** Allows user to close connection. */
  104. DataConnection.prototype.close = function() {
  105. if (!this.open) {
  106. return;
  107. }
  108. this._cleanup();
  109. };
  110. /** Allows user to send data. */
  111. DataConnection.prototype.send = function(data) {
  112. if (!this.open) {
  113. this.emit('error', new Error('Connection no longer open.'));
  114. }
  115. if (this._reliable) {
  116. // Note: reliable shim sending will make it so that you cannot customize
  117. // serialization.
  118. this._reliable.send(data);
  119. return;
  120. }
  121. var self = this;
  122. if (this.serialization === 'none') {
  123. this._dc.send(data);
  124. } else if (this.serialization === 'json') {
  125. this._dc.send(JSON.stringify(data));
  126. } else {
  127. var utf8 = (this.serialization === 'binary-utf8');
  128. var blob = util.pack(data, utf8);
  129. // DataChannel currently only supports strings.
  130. if (!util.supports.binary) {
  131. util.blobToBinaryString(blob, function(str){
  132. self._dc.send(str);
  133. });
  134. } else {
  135. this._dc.send(blob);
  136. }
  137. }
  138. };
  139. DataConnection.prototype.handleMessage = function(message) {
  140. var payload = message.payload;
  141. switch (message.type) {
  142. case 'ANSWER':
  143. // TODO: assert sdp exists.
  144. // Should we pass `this`?
  145. // Forward to negotiator
  146. Negotiator.handleSDP(this.peer, this.id, payload.sdp, message.type);
  147. break;
  148. case 'CANDIDATE':
  149. Negotiator.handleCandidate(this.peer, this.id, payload.candidate);
  150. break;
  151. default:
  152. util.warn('Unrecognized message type:', message.type, 'from peer:', this.peer);
  153. break;
  154. }
  155. }