xhrnodereadablestream.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Copyright 2015 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview adaptor of XhrStreamReader to the NodeReadableStream interface.
  16. */
  17. goog.provide('goog.net.streams.XhrNodeReadableStream');
  18. goog.require('goog.array');
  19. goog.require('goog.log');
  20. goog.require('goog.net.streams.NodeReadableStream');
  21. goog.require('goog.net.streams.XhrStreamReader');
  22. /**
  23. * The XhrNodeReadableStream class.
  24. *
  25. * @param {!goog.net.streams.XhrStreamReader} xhrReader The XhrStreamReader
  26. * object that handles the events of the underlying Xhr.
  27. * @constructor
  28. * @implements {goog.net.streams.NodeReadableStream}
  29. * @struct
  30. * @final
  31. * @package
  32. */
  33. goog.net.streams.XhrNodeReadableStream = function(xhrReader) {
  34. /**
  35. * @const
  36. * @private {?goog.log.Logger} the logger.
  37. */
  38. this.logger_ = goog.log.getLogger('goog.net.streams.XhrNodeReadableStream');
  39. /**
  40. * The xhr reader.
  41. *
  42. * @private {!goog.net.streams.XhrStreamReader} the xhr reader.
  43. */
  44. this.xhrReader_ = xhrReader;
  45. this.xhrReader_.setDataHandler(goog.bind(this.onData_, this));
  46. this.xhrReader_.setStatusHandler(goog.bind(this.onStatusChange_, this));
  47. /**
  48. * The callback map, keyed by eventTypes.
  49. *
  50. * @private {!Object<Array<function(!Object=)>>}
  51. */
  52. this.callbackMap_ = {};
  53. /**
  54. * The callback-once map, keyed by eventTypes.
  55. *
  56. * @private {!Object<Array<function(!Object=)>>}
  57. */
  58. this.callbackOnceMap_ = {};
  59. };
  60. /**
  61. * @override
  62. */
  63. goog.net.streams.XhrNodeReadableStream.prototype.on = function(
  64. eventType, callback) {
  65. var callbacks = this.callbackMap_[eventType];
  66. if (!callbacks) {
  67. callbacks = [];
  68. this.callbackMap_[eventType] = callbacks;
  69. }
  70. callbacks.push(callback);
  71. return this;
  72. };
  73. /**
  74. * @override
  75. */
  76. goog.net.streams.XhrNodeReadableStream.prototype.addListener = function(
  77. eventType, callback) {
  78. this.on(eventType, callback);
  79. return this;
  80. };
  81. /**
  82. * @override
  83. */
  84. goog.net.streams.XhrNodeReadableStream.prototype.removeListener = function(
  85. eventType, callback) {
  86. var callbacks = this.callbackMap_[eventType];
  87. if (callbacks) {
  88. goog.array.remove(callbacks, callback); // keep the empty array
  89. }
  90. var onceCallbacks = this.callbackOnceMap_[eventType];
  91. if (onceCallbacks) {
  92. goog.array.remove(onceCallbacks, callback);
  93. }
  94. return this;
  95. };
  96. /**
  97. * @override
  98. */
  99. goog.net.streams.XhrNodeReadableStream.prototype.once = function(
  100. eventType, callback) {
  101. var callbacks = this.callbackOnceMap_[eventType];
  102. if (!callbacks) {
  103. callbacks = [];
  104. this.callbackOnceMap_[eventType] = callbacks;
  105. }
  106. callbacks.push(callback);
  107. return this;
  108. };
  109. /**
  110. * Handles any new data from XHR.
  111. *
  112. * @param {!Array<!Object>} messages New messages, to be delivered in order
  113. * and atomically.
  114. * @private
  115. */
  116. goog.net.streams.XhrNodeReadableStream.prototype.onData_ = function(messages) {
  117. var callbacks =
  118. this.callbackMap_[goog.net.streams.NodeReadableStream.EventType.DATA];
  119. if (callbacks) {
  120. this.doMessages_(messages, callbacks);
  121. }
  122. var onceCallbacks =
  123. this.callbackOnceMap_[goog.net.streams.NodeReadableStream.EventType.DATA];
  124. if (onceCallbacks) {
  125. this.doMessages_(messages, onceCallbacks);
  126. }
  127. this.callbackOnceMap_[goog.net.streams.NodeReadableStream.EventType.DATA] =
  128. [];
  129. };
  130. /**
  131. * Deliver messages to registered callbacks.
  132. *
  133. * Exceptions are caught and logged (debug), and ignored otherwise.
  134. *
  135. * @param {!Array<!Object>} messages The messages to be delivered
  136. * @param {!Array<function(!Object=)>} callbacks The callbacks.
  137. * @private
  138. */
  139. goog.net.streams.XhrNodeReadableStream.prototype.doMessages_ = function(
  140. messages, callbacks) {
  141. var self = this;
  142. for (var i = 0; i < messages.length; i++) {
  143. var message = messages[i];
  144. goog.array.forEach(callbacks, function(callback) {
  145. try {
  146. callback(message);
  147. } catch (ex) {
  148. self.handleError_('message-callback exception (ignored) ' + ex);
  149. }
  150. });
  151. }
  152. };
  153. /**
  154. * Handles any state changes from XHR.
  155. *
  156. * @private
  157. */
  158. goog.net.streams.XhrNodeReadableStream.prototype.onStatusChange_ = function() {
  159. var currentStatus = this.xhrReader_.getStatus();
  160. var Status = goog.net.streams.XhrStreamReader.Status;
  161. var EventType = goog.net.streams.NodeReadableStream.EventType;
  162. switch (currentStatus) {
  163. case Status.ACTIVE:
  164. this.doStatus_(EventType.READABLE);
  165. break;
  166. case Status.BAD_DATA:
  167. case Status.HANDLER_EXCEPTION:
  168. case Status.NO_DATA:
  169. case Status.TIMEOUT:
  170. case Status.XHR_ERROR:
  171. this.doStatus_(EventType.ERROR);
  172. break;
  173. case Status.CANCELLED:
  174. this.doStatus_(EventType.CLOSE);
  175. break;
  176. case Status.SUCCESS:
  177. this.doStatus_(EventType.END);
  178. break;
  179. }
  180. };
  181. /**
  182. * Run status change callbacks.
  183. *
  184. * @param {string} eventType The event type
  185. * @private
  186. */
  187. goog.net.streams.XhrNodeReadableStream.prototype.doStatus_ = function(
  188. eventType) {
  189. var callbacks = this.callbackMap_[eventType];
  190. var self = this;
  191. if (callbacks) {
  192. goog.array.forEach(callbacks, function(callback) {
  193. try {
  194. callback();
  195. } catch (ex) {
  196. self.handleError_('status-callback exception (ignored) ' + ex);
  197. }
  198. });
  199. }
  200. var onceCallbacks = this.callbackOnceMap_[eventType];
  201. if (onceCallbacks) {
  202. goog.array.forEach(onceCallbacks, function(callback) { callback(); });
  203. }
  204. this.callbackOnceMap_[eventType] = [];
  205. };
  206. /**
  207. * Log an error
  208. *
  209. * @param {string} message The error message
  210. * @private
  211. */
  212. goog.net.streams.XhrNodeReadableStream.prototype.handleError_ = function(
  213. message) {
  214. goog.log.error(this.logger_, message);
  215. };