dataconnection.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /**
  2. * Wraps a DataChannel between two Peers.
  3. */
  4. function DataConnection(peer, provider, options) {
  5. if (!(this instanceof DataConnection)) return new DataConnection(peer, provider, options);
  6. EventEmitter.call(this);
  7. // TODO: perhaps default serialization should be binary-utf8?
  8. this.options = util.extend({
  9. serialization: 'binary'
  10. }, options);
  11. // Connection is not open yet.
  12. this.open = false;
  13. this.type = 'data';
  14. this.peer = peer;
  15. this.provider = provider;
  16. this.label = this.options.label;
  17. this.metadata = this.options.metadata; // TODO: metadata could also be a part of the paylod.
  18. this.serialization = this.options.serialization;
  19. this.reliable = this.options.reliable;
  20. this.id = this.options._id || DataConnection._idPrefix + util.randomToken();
  21. this._pc = Negotiator.startConnection(
  22. this.peer,
  23. this.id,
  24. this.provider,
  25. this.options._payload
  26. );
  27. }
  28. util.inherits(DataConnection, EventEmitter);
  29. DataConnection._idPrefix = 'dc_';
  30. /** Called by the Negotiator when the DataChannel is ready. */
  31. DataConnection.prototype.initialize = function(dc) {
  32. this._dc = dc;
  33. this._configureDataChannel();
  34. }
  35. DataConnection.prototype._configureDataChannel = function() {
  36. var self = this;
  37. // TODO: util.supports.binary
  38. if (util.supports.binary) {
  39. // Webkit doesn't support binary yet
  40. this._dc.binaryType = 'arraybuffer';
  41. }
  42. this._dc.onopen = function() {
  43. util.log('Data channel connection success');
  44. self.open = true;
  45. self.emit('open');
  46. };
  47. // Use the Reliable shim for non Firefox browsers
  48. // TODO: util.supports.reliable
  49. if (!util.supports.reliable) {
  50. this._reliable = new Reliable(this._dc, util.debug);
  51. }
  52. if (this._reliable) {
  53. this._reliable.onmessage = function(msg) {
  54. self.emit('data', msg);
  55. };
  56. } else {
  57. this._dc.onmessage = function(e) {
  58. self._handleDataMessage(e);
  59. };
  60. }
  61. this._dc.onclose = function(e) {
  62. util.log('DataChannel closed.');
  63. self.close();
  64. };
  65. };
  66. DataConnection.prototype._cleanup = function() {
  67. if (this._dc && this._dc.readyState !== 'closed') {
  68. this._dc.close();
  69. this._dc = null;
  70. }
  71. this.open = false;
  72. // Negotiator will listen for this and take care of the PC if appropriate.
  73. this.emit('close');
  74. };
  75. // Handles a DataChannel message.
  76. DataConnection.prototype._handleDataMessage = function(e) {
  77. var self = this;
  78. var data = e.data;
  79. var datatype = data.constructor;
  80. if (this.serialization === 'binary' || this.serialization === 'binary-utf8') {
  81. if (datatype === Blob) {
  82. // Datatype should never be blob
  83. util.blobToArrayBuffer(data, function(ab) {
  84. data = util.unpack(ab);
  85. self.emit('data', data);
  86. });
  87. return;
  88. } else if (datatype === ArrayBuffer) {
  89. data = util.unpack(data);
  90. } else if (datatype === String) {
  91. // String fallback for binary data for browsers that don't support binary yet
  92. var ab = util.binaryStringToArrayBuffer(data);
  93. data = util.unpack(ab);
  94. }
  95. } else if (this.serialization === 'json') {
  96. data = JSON.parse(data);
  97. }
  98. this.emit('data', data);
  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 is not open. You should listen for the `open` event before sending messages.'));
  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. }