socket.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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 + '/'+key+'?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. self._http.abort();
  42. self._http = null;
  43. }
  44. util.log('Socket open');
  45. };
  46. };
  47. /** Start XHR streaming. */
  48. Socket.prototype._startXhrStream = function(n) {
  49. try {
  50. var self = this;
  51. this._http = new XMLHttpRequest();
  52. this._http._index = 1;
  53. this._http._streamIndex = n || 0;
  54. this._http.open('post', this._httpUrl + '/id?i=' + this._http._streamIndex, true);
  55. this._http.onreadystatechange = function() {
  56. if (this.readyState == 2 && !!this.old) {
  57. this.old.abort();
  58. delete this.old;
  59. }
  60. if (this.readyState > 2 && this.status == 200 && !!this.responseText) {
  61. self._handleStream(this);
  62. }
  63. };
  64. this._http.send(null);
  65. this._setHTTPTimeout();
  66. } catch(e) {
  67. util.log('XMLHttpRequest not available; defaulting to WebSockets');
  68. }
  69. };
  70. /** Handles onreadystatechange response as a stream. */
  71. Socket.prototype._handleStream = function(http) {
  72. var self = this;
  73. // 3 and 4 are loading/done state. All others are not relevant.
  74. var message = http.responseText.split('\n')[http._index];
  75. if (!!message) {
  76. http._index += 1;
  77. try {
  78. message = JSON.parse(message);
  79. } catch(e) {
  80. util.log('Invalid server message', message);
  81. return;
  82. }
  83. self.emit('message', message);
  84. }
  85. };
  86. Socket.prototype._setHTTPTimeout = function() {
  87. var self = this;
  88. this._timeout = setTimeout(function() {
  89. var old = self._http;
  90. if (!self._wsOpen()) {
  91. self._startXhrStream(old._streamIndex + 1);
  92. self._http.old = old;
  93. } else {
  94. old.abort();
  95. }
  96. }, 5000);
  97. };
  98. /** Exposed send for DC & Peer. */
  99. Socket.prototype.send = function(data) {
  100. if (!data.type) {
  101. this.emit('error', 'Invalid message');
  102. }
  103. message = JSON.stringify(data);
  104. if (this._wsOpen()) {
  105. this._socket.send(message);
  106. } else {
  107. var http = new XMLHttpRequest();
  108. var url = this._httpUrl + '/' + data.type.toLowerCase();
  109. http.open('post', url, true);
  110. http.setRequestHeader('Content-Type', 'application/json');
  111. http.send(message);
  112. }
  113. };
  114. Socket.prototype.close = function() {
  115. if (!!this._wsOpen()) {
  116. this._socket.close();
  117. }
  118. };
  119. Socket.prototype._wsOpen = function() {
  120. return !!this._socket && this._socket.readyState == 1;
  121. };