socket.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /**
  2. * An abstraction on top of WebSockets and XHR streaming to provide fastest
  3. * possible connection for peers.
  4. */
  5. function Socket(host, port, key, id) {
  6. if (!(this instanceof Socket)) return new Socket(server, id, key);
  7. EventEmitter.call(this);
  8. this._id = id;
  9. var token = util.randomToken();
  10. this._httpUrl = 'http://' + host + ':' + port + '/' + key + '/' + id + '/' + token;
  11. this._wsUrl = 'ws://' + host + ':' + port + '/peerjs?key='+key+'&id='+id+'&token='+token;
  12. };
  13. util.inherits(Socket, EventEmitter);
  14. /** Check in with ID or get one from server. */
  15. Socket.prototype.start = function() {
  16. this._startXhrStream();
  17. this._startWebSocket();
  18. };
  19. /** Start up websocket communications. */
  20. Socket.prototype._startWebSocket = function() {
  21. var self = this;
  22. if (!!this._socket) {
  23. return;
  24. }
  25. this._socket = new WebSocket(this._wsUrl);
  26. this._socket.onmessage = function(event) {
  27. var data;
  28. try {
  29. data = JSON.parse(event.data);
  30. } catch(e) {
  31. util.log('Invalid server message', event.data);
  32. return;
  33. }
  34. self.emit('message', data);
  35. };
  36. // Take care of the queue of connections if necessary and make sure Peer knows
  37. // socket is open.
  38. this._socket.onopen = function() {
  39. if (!!self._timeout) {
  40. clearTimeout(self._timeout);
  41. setTimeout(function(){
  42. self._http.abort();
  43. self._http = null;
  44. }, 5000);
  45. }
  46. util.log('Socket open');
  47. };
  48. };
  49. /** Start XHR streaming. */
  50. Socket.prototype._startXhrStream = function(n) {
  51. try {
  52. var self = this;
  53. this._http = new XMLHttpRequest();
  54. this._http._index = 1;
  55. this._http._streamIndex = n || 0;
  56. this._http.open('post', this._httpUrl + '/id?i=' + this._http._streamIndex, true);
  57. this._http.onreadystatechange = function() {
  58. if (this.readyState == 2 && !!this.old) {
  59. this.old.abort();
  60. delete this.old;
  61. }
  62. if (this.readyState > 2 && this.status == 200 && !!this.responseText) {
  63. self._handleStream(this);
  64. }
  65. };
  66. this._http.send(null);
  67. this._setHTTPTimeout();
  68. } catch(e) {
  69. util.log('XMLHttpRequest not available; defaulting to WebSockets');
  70. }
  71. };
  72. /** Handles onreadystatechange response as a stream. */
  73. Socket.prototype._handleStream = function(http) {
  74. var self = this;
  75. // 3 and 4 are loading/done state. All others are not relevant.
  76. var message = http.responseText.split('\n')[http._index];
  77. if (!!message) {
  78. http._index += 1;
  79. try {
  80. message = JSON.parse(message);
  81. } catch(e) {
  82. util.log('Invalid server message', message);
  83. return;
  84. }
  85. self.emit('message', message);
  86. }
  87. };
  88. Socket.prototype._setHTTPTimeout = function() {
  89. var self = this;
  90. this._timeout = setTimeout(function() {
  91. var old = self._http;
  92. if (!self._wsOpen()) {
  93. self._startXhrStream(old._streamIndex + 1);
  94. self._http.old = old;
  95. } else {
  96. old.abort();
  97. }
  98. }, 5000);
  99. };
  100. /** Exposed send for DC & Peer. */
  101. Socket.prototype.send = function(data) {
  102. if (!data.type) {
  103. this.emit('error', 'Invalid message');
  104. }
  105. message = JSON.stringify(data);
  106. if (this._wsOpen()) {
  107. this._socket.send(message);
  108. } else {
  109. var http = new XMLHttpRequest();
  110. var url = this._httpUrl + '/' + data.type.toLowerCase();
  111. http.open('post', url, true);
  112. http.setRequestHeader('Content-Type', 'application/json');
  113. http.send(message);
  114. }
  115. };
  116. Socket.prototype.close = function() {
  117. if (!!this._wsOpen()) {
  118. this._socket.close();
  119. }
  120. };
  121. Socket.prototype._wsOpen = function() {
  122. return !!this._socket && this._socket.readyState == 1;
  123. };