You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

reconnecting-eventsource.js 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // MIT License:
  2. //
  3. // Copyright (C) 2017 Fanout, Inc.
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to
  7. // deal in the Software without restriction, including without limitation the
  8. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. // sell copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. // IN THE SOFTWARE.
  22. var ReconnectingEventSource = function (url, configuration) {
  23. this._eventSource = null;
  24. this._lastEventId = null;
  25. this._timer = null;
  26. this._listeners = {};
  27. this.url = url;
  28. this.readyState = 0;
  29. this.retry_time = 3000;
  30. if (configuration != undefined && configuration.lastEventId) {
  31. this._lastEventId = configuration.lastEventId;
  32. delete configuration['lastEventId'];
  33. }
  34. this._configuration = configuration;
  35. var self = this;
  36. this._onevent_wrapped = function (event) { self._onevent(event); };
  37. this._start();
  38. };
  39. ReconnectingEventSource.prototype._start = function () {
  40. var url = this.url;
  41. if (this._lastEventId) {
  42. if (url.indexOf('?') === -1) {
  43. url += '?';
  44. } else {
  45. url += '&';
  46. }
  47. url += 'lastEventId=' + encodeURIComponent(this._lastEventId);
  48. }
  49. this._eventSource = new EventSource(url, this._configuration);
  50. var self = this;
  51. this._eventSource.onopen = function (event) { self._onopen(event); };
  52. this._eventSource.onerror = function (event) { self._onerror(event); };
  53. // apply listen types
  54. for (var type in this._listeners) {
  55. this._eventSource.addEventListener(type, this._onevent_wrapped);
  56. }
  57. };
  58. ReconnectingEventSource.prototype._onopen = function (event) {
  59. if (this.readyState == 0) {
  60. this.readyState = 1;
  61. this.onopen(event);
  62. }
  63. };
  64. ReconnectingEventSource.prototype._onerror = function (event) {
  65. if (this.readyState == 1) {
  66. this.readyState = 0;
  67. this.onerror(event);
  68. }
  69. if (this._eventSource) {
  70. if(this._eventSource.readyState == 2) {
  71. // reconnect with new object
  72. this._eventSource.close();
  73. this._eventSource = null;
  74. var self = this;
  75. this._timer = setTimeout(function () {
  76. self._start();
  77. }, this.retry_time);
  78. }
  79. }
  80. };
  81. ReconnectingEventSource.prototype._onevent = function (event) {
  82. if (event.lastEventId) {
  83. this._lastEventId = event.lastEventId;
  84. }
  85. var l = this._listeners[event.type];
  86. if (l != undefined) {
  87. // operate on a copy
  88. l = l.slice();
  89. for (var n = 0; n < l.length; n++) {
  90. l[n](event);
  91. }
  92. }
  93. if (event.type == 'message') {
  94. this.onmessage(event);
  95. }
  96. };
  97. ReconnectingEventSource.prototype.onopen = function (event) {
  98. // user may override
  99. };
  100. ReconnectingEventSource.prototype.onerror = function (event) {
  101. // user may override
  102. };
  103. ReconnectingEventSource.prototype.onmessage = function (event) {
  104. // user may override
  105. };
  106. ReconnectingEventSource.prototype.close = function () {
  107. if (this._timer) {
  108. clearTimeout(this._timer);
  109. this._timer = null;
  110. }
  111. if (this._eventSource) {
  112. this._eventSource.close();
  113. this._eventSource = null;
  114. }
  115. this.readyState = 2;
  116. };
  117. ReconnectingEventSource.prototype.addEventListener = function (type, callback) {
  118. var type = type.toString();
  119. var l = this._listeners[type];
  120. if (l == undefined) {
  121. l = [];
  122. this._listeners[type] = l;
  123. if (this._eventSource) {
  124. this._eventSource.addEventListener(type, this._onevent_wrapped);
  125. }
  126. }
  127. for (var n = 0; n < l.length; n++) {
  128. if (l[n] === callback) {
  129. return;
  130. }
  131. }
  132. l.push(callback);
  133. };
  134. ReconnectingEventSource.prototype.removeEventListener = function (type, callback) {
  135. var type = type.toString();
  136. var l = this._listeners[type];
  137. if (l == undefined) {
  138. return;
  139. }
  140. for (var n = 0; n < l.length; n++) {
  141. if (l[n] === callback) {
  142. l.splice(n, 1);
  143. break;
  144. }
  145. }
  146. if (l.length == 0) {
  147. delete this._listeners[type];
  148. if (this._eventSource) {
  149. this._eventSource.removeEventListener(type, this._onevent_wrapped);
  150. }
  151. }
  152. };