PageRenderTime 43ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/socket.js

https://github.com/joker-2013/peerjs
JavaScript | 199 lines | 151 code | 27 blank | 21 comment | 29 complexity | c06accb3fbca2eec17e63fd5678c93a3 MD5 | raw file
  1. /**
  2. * An abstraction on top of WebSockets and XHR streaming to provide fastest
  3. * possible connection for peers.
  4. */
  5. function Socket(secure, host, port, path, key) {
  6. if (!(this instanceof Socket)) return new Socket(secure, host, port, path, key);
  7. EventEmitter.call(this);
  8. // Disconnected manually.
  9. this.disconnected = false;
  10. this._queue = [];
  11. var httpProtocol = secure ? 'https://' : 'http://';
  12. var wsProtocol = secure ? 'wss://' : 'ws://';
  13. this._httpUrl = httpProtocol + host + ':' + port + path + key;
  14. this._wsUrl = wsProtocol + host + ':' + port + path + 'peerjs?key=' + key;
  15. }
  16. util.inherits(Socket, EventEmitter);
  17. /** Check in with ID or get one from server. */
  18. Socket.prototype.start = function(id, token) {
  19. this.id = id;
  20. this._httpUrl += '/' + id + '/' + token;
  21. this._wsUrl += '&id='+id+'&token='+token;
  22. this._startXhrStream();
  23. this._startWebSocket();
  24. }
  25. /** Start up websocket communications. */
  26. Socket.prototype._startWebSocket = function(id) {
  27. var self = this;
  28. if (this._socket) {
  29. return;
  30. }
  31. this._socket = new WebSocket(this._wsUrl);
  32. this._socket.onmessage = function(event) {
  33. var data;
  34. try {
  35. data = JSON.parse(event.data);
  36. } catch(e) {
  37. util.log('Invalid server message', event.data);
  38. return;
  39. }
  40. self.emit('message', data);
  41. };
  42. // Take care of the queue of connections if necessary and make sure Peer knows
  43. // socket is open.
  44. this._socket.onopen = function() {
  45. if (self._timeout) {
  46. clearTimeout(self._timeout);
  47. setTimeout(function(){
  48. self._http.abort();
  49. self._http = null;
  50. }, 5000);
  51. }
  52. self._sendQueuedMessages();
  53. util.log('Socket open');
  54. };
  55. }
  56. /** Start XHR streaming. */
  57. Socket.prototype._startXhrStream = function(n) {
  58. try {
  59. var self = this;
  60. this._http = new XMLHttpRequest();
  61. this._http._index = 1;
  62. this._http._streamIndex = n || 0;
  63. this._http.open('post', this._httpUrl + '/id?i=' + this._http._streamIndex, true);
  64. this._http.onreadystatechange = function() {
  65. if (this.readyState == 2 && this.old) {
  66. this.old.abort();
  67. delete this.old;
  68. }
  69. if (this.readyState > 2 && this.status == 200 && this.responseText) {
  70. self._handleStream(this);
  71. }
  72. };
  73. this._http.send(null);
  74. this._setHTTPTimeout();
  75. } catch(e) {
  76. util.log('XMLHttpRequest not available; defaulting to WebSockets');
  77. }
  78. }
  79. /** Handles onreadystatechange response as a stream. */
  80. Socket.prototype._handleStream = function(http) {
  81. // 3 and 4 are loading/done state. All others are not relevant.
  82. var messages = http.responseText.split('\n');
  83. // Check to see if anything needs to be processed on buffer.
  84. if (http._buffer) {
  85. while (http._buffer.length > 0) {
  86. var index = http._buffer.shift();
  87. var bufferedMessage = messages[index];
  88. try {
  89. bufferedMessage = JSON.parse(bufferedMessage);
  90. } catch(e) {
  91. http._buffer.shift(index);
  92. break;
  93. }
  94. this.emit('message', bufferedMessage);
  95. }
  96. }
  97. var message = messages[http._index];
  98. if (message) {
  99. http._index += 1;
  100. // Buffering--this message is incomplete and we'll get to it next time.
  101. // This checks if the httpResponse ended in a `\n`, in which case the last
  102. // element of messages should be the empty string.
  103. if (http._index === messages.length) {
  104. if (!http._buffer) {
  105. http._buffer = [];
  106. }
  107. http._buffer.push(http._index - 1);
  108. } else {
  109. try {
  110. message = JSON.parse(message);
  111. } catch(e) {
  112. util.log('Invalid server message', message);
  113. return;
  114. }
  115. this.emit('message', message);
  116. }
  117. }
  118. }
  119. Socket.prototype._setHTTPTimeout = function() {
  120. var self = this;
  121. this._timeout = setTimeout(function() {
  122. var old = self._http;
  123. if (!self._wsOpen()) {
  124. self._startXhrStream(old._streamIndex + 1);
  125. self._http.old = old;
  126. } else {
  127. old.abort();
  128. }
  129. }, 25000);
  130. }
  131. /** Is the websocket currently open? */
  132. Socket.prototype._wsOpen = function() {
  133. return this._socket && this._socket.readyState == 1;
  134. }
  135. /** Send queued messages. */
  136. Socket.prototype._sendQueuedMessages = function() {
  137. for (var i = 0, ii = this._queue.length; i < ii; i += 1) {
  138. this.send(this._queue[i]);
  139. }
  140. }
  141. /** Exposed send for DC & Peer. */
  142. Socket.prototype.send = function(data) {
  143. if (this.disconnected) {
  144. return;
  145. }
  146. // If we didn't get an ID yet, we can't yet send anything so we should queue
  147. // up these messages.
  148. if (!this.id) {
  149. this._queue.push(data);
  150. return;
  151. }
  152. if (!data.type) {
  153. this.emit('error', 'Invalid message');
  154. return;
  155. }
  156. var message = JSON.stringify(data);
  157. if (this._wsOpen()) {
  158. this._socket.send(message);
  159. } else {
  160. var http = new XMLHttpRequest();
  161. var url = this._httpUrl + '/' + data.type.toLowerCase();
  162. http.open('post', url, true);
  163. http.setRequestHeader('Content-Type', 'application/json');
  164. http.send(message);
  165. }
  166. }
  167. Socket.prototype.close = function() {
  168. if (!this.disconnected && this._wsOpen()) {
  169. this._socket.close();
  170. this.disconnected = true;
  171. }
  172. }