fetchxmlhttpfactory.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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. goog.provide('goog.net.FetchXmlHttp');
  15. goog.provide('goog.net.FetchXmlHttpFactory');
  16. goog.require('goog.asserts');
  17. goog.require('goog.events.EventTarget');
  18. goog.require('goog.functions');
  19. goog.require('goog.log');
  20. goog.require('goog.net.XhrLike');
  21. goog.require('goog.net.XmlHttpFactory');
  22. /**
  23. * Factory for creating Xhr objects that uses the native fetch() method.
  24. * https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
  25. * Note that this factory is intended for use in Service Worker only.
  26. * @param {!WorkerGlobalScope} worker The Service Worker global scope.
  27. * @extends {goog.net.XmlHttpFactory}
  28. * @struct
  29. * @constructor
  30. */
  31. goog.net.FetchXmlHttpFactory = function(worker) {
  32. goog.net.FetchXmlHttpFactory.base(this, 'constructor');
  33. /** @private @final {!WorkerGlobalScope} */
  34. this.worker_ = worker;
  35. /** @private {!RequestCredentials|undefined} */
  36. this.credentialsMode_ = undefined;
  37. /** @private {!RequestCache|undefined} */
  38. this.cacheMode_ = undefined;
  39. };
  40. goog.inherits(goog.net.FetchXmlHttpFactory, goog.net.XmlHttpFactory);
  41. /** @override */
  42. goog.net.FetchXmlHttpFactory.prototype.createInstance = function() {
  43. var instance = new goog.net.FetchXmlHttp(this.worker_);
  44. if (this.credentialsMode_) {
  45. instance.setCredentialsMode(this.credentialsMode_);
  46. }
  47. if (this.cacheMode_) {
  48. instance.setCacheMode(this.cacheMode_);
  49. }
  50. return instance;
  51. };
  52. /** @override */
  53. goog.net.FetchXmlHttpFactory.prototype.internalGetOptions =
  54. goog.functions.constant({});
  55. /**
  56. * @param {!RequestCredentials} credentialsMode The credentials mode of the
  57. * Service Worker fetch.
  58. */
  59. goog.net.FetchXmlHttpFactory.prototype.setCredentialsMode = function(
  60. credentialsMode) {
  61. this.credentialsMode_ = credentialsMode;
  62. };
  63. /**
  64. * @param {!RequestCache} cacheMode The cache mode of the Service Worker fetch.
  65. */
  66. goog.net.FetchXmlHttpFactory.prototype.setCacheMode = function(cacheMode) {
  67. this.cacheMode_ = cacheMode;
  68. };
  69. /**
  70. * FetchXmlHttp object constructor.
  71. * @param {!WorkerGlobalScope} worker
  72. * @extends {goog.events.EventTarget}
  73. * @implements {goog.net.XhrLike}
  74. * @constructor
  75. * @struct
  76. */
  77. goog.net.FetchXmlHttp = function(worker) {
  78. goog.net.FetchXmlHttp.base(this, 'constructor');
  79. /** @private @final {!WorkerGlobalScope} */
  80. this.worker_ = worker;
  81. /** @private {RequestCredentials|undefined} */
  82. this.credentialsMode_ = undefined;
  83. /** @private {RequestCache|undefined} */
  84. this.cacheMode_ = undefined;
  85. /**
  86. * Request state.
  87. * @type {goog.net.FetchXmlHttp.RequestState}
  88. */
  89. this.readyState = goog.net.FetchXmlHttp.RequestState.UNSENT;
  90. /**
  91. * HTTP status.
  92. * @type {number}
  93. */
  94. this.status = 0;
  95. /**
  96. * HTTP status string.
  97. * @type {string}
  98. */
  99. this.statusText = '';
  100. /**
  101. * Content of the response.
  102. * @type {string}
  103. */
  104. this.responseText = '';
  105. /**
  106. * Document response entity body.
  107. * NOTE: This is always null and not supported by this class.
  108. * @final {null}
  109. */
  110. this.responseXML = null;
  111. /**
  112. * Method to call when the state changes.
  113. * @type {?function()}
  114. */
  115. this.onreadystatechange = null;
  116. /** @private {!Headers} */
  117. this.requestHeaders_ = new Headers();
  118. /** @private {?Headers} */
  119. this.responseHeaders_ = null;
  120. /**
  121. * Request method (GET or POST).
  122. * @private {string}
  123. */
  124. this.method_ = 'GET';
  125. /**
  126. * Request URL.
  127. * @private {string}
  128. */
  129. this.url_ = '';
  130. /**
  131. * Whether the request is in progress.
  132. * @private {boolean}
  133. */
  134. this.inProgress_ = false;
  135. /** @private @final {?goog.log.Logger} */
  136. this.logger_ = goog.log.getLogger('goog.net.FetchXmlHttp');
  137. };
  138. goog.inherits(goog.net.FetchXmlHttp, goog.events.EventTarget);
  139. /**
  140. * State of the requests.
  141. * @enum {number}
  142. */
  143. goog.net.FetchXmlHttp.RequestState = {
  144. UNSENT: 0,
  145. OPENED: 1,
  146. HEADER_RECEIVED: 2,
  147. LOADING: 3,
  148. DONE: 4
  149. };
  150. /** @override */
  151. goog.net.FetchXmlHttp.prototype.open = function(method, url, opt_async) {
  152. goog.asserts.assert(!!opt_async, 'Only async requests are supported.');
  153. if (this.readyState != goog.net.FetchXmlHttp.RequestState.UNSENT) {
  154. this.abort();
  155. throw Error('Error reopening a connection');
  156. }
  157. this.method_ = method;
  158. this.url_ = url;
  159. this.readyState = goog.net.FetchXmlHttp.RequestState.OPENED;
  160. this.dispatchCallback_();
  161. };
  162. /** @override */
  163. goog.net.FetchXmlHttp.prototype.send = function(opt_data) {
  164. if (this.readyState != goog.net.FetchXmlHttp.RequestState.OPENED) {
  165. this.abort();
  166. throw Error('need to call open() first. ');
  167. }
  168. this.inProgress_ = true;
  169. var requestInit = {
  170. headers: this.requestHeaders_,
  171. method: this.method_,
  172. credentials: this.credentialsMode_,
  173. cache: this.cacheMode_
  174. };
  175. if (opt_data) {
  176. requestInit['body'] = opt_data;
  177. }
  178. this.worker_
  179. .fetch(new Request(this.url_, /** @type {!RequestInit} */ (requestInit)))
  180. .then(
  181. this.handleResponse_.bind(this), this.handleSendFailure_.bind(this));
  182. };
  183. /** @override */
  184. goog.net.FetchXmlHttp.prototype.abort = function() {
  185. this.responseText = '';
  186. this.requestHeaders_ = new Headers();
  187. this.status = 0;
  188. if (((this.readyState >= goog.net.FetchXmlHttp.RequestState.OPENED) &&
  189. this.inProgress_) &&
  190. (this.readyState != goog.net.FetchXmlHttp.RequestState.DONE)) {
  191. this.readyState = goog.net.FetchXmlHttp.RequestState.DONE;
  192. this.inProgress_ = false;
  193. this.dispatchCallback_();
  194. }
  195. this.readyState = goog.net.FetchXmlHttp.RequestState.UNSENT;
  196. };
  197. /**
  198. * Handles the fetch response.
  199. * @param {!Response} response
  200. * @private
  201. */
  202. goog.net.FetchXmlHttp.prototype.handleResponse_ = function(response) {
  203. if (!this.inProgress_) {
  204. // The request was aborted, ignore.
  205. return;
  206. }
  207. if (!this.responseHeaders_) {
  208. this.responseHeaders_ = response.headers;
  209. this.readyState = goog.net.FetchXmlHttp.RequestState.HEADER_RECEIVED;
  210. this.dispatchCallback_();
  211. }
  212. // A callback may abort the request.
  213. if (!this.inProgress_) {
  214. // The request was aborted, ignore.
  215. return;
  216. }
  217. this.readyState = goog.net.FetchXmlHttp.RequestState.LOADING;
  218. this.dispatchCallback_();
  219. // A callback may abort the request.
  220. if (!this.inProgress_) {
  221. // The request was aborted, ignore.
  222. return;
  223. }
  224. response.text().then(
  225. this.handleResponseText_.bind(this, response),
  226. this.handleSendFailure_.bind(this));
  227. };
  228. /**
  229. * Handles the response text.
  230. * @param {!Response} response
  231. * @param {string} responseText
  232. * @private
  233. */
  234. goog.net.FetchXmlHttp.prototype.handleResponseText_ = function(
  235. response, responseText) {
  236. if (!this.inProgress_) {
  237. // The request was aborted, ignore.
  238. return;
  239. }
  240. this.status = response.status;
  241. this.statusText = response.statusText;
  242. this.responseText = responseText;
  243. this.readyState = goog.net.FetchXmlHttp.RequestState.DONE;
  244. this.dispatchCallback_();
  245. };
  246. /**
  247. * Handles the send failure.
  248. * @param {*} error
  249. * @private
  250. */
  251. goog.net.FetchXmlHttp.prototype.handleSendFailure_ = function(error) {
  252. var e = error instanceof Error ? error : Error(error);
  253. goog.log.warning(this.logger_, 'Failed to fetch url ' + this.url_, e);
  254. if (!this.inProgress_) {
  255. // The request was aborted, ignore.
  256. return;
  257. }
  258. this.readyState = goog.net.FetchXmlHttp.RequestState.DONE;
  259. this.dispatchCallback_();
  260. };
  261. /** @override */
  262. goog.net.FetchXmlHttp.prototype.setRequestHeader = function(header, value) {
  263. this.requestHeaders_.append(header, value);
  264. };
  265. /** @override */
  266. goog.net.FetchXmlHttp.prototype.getResponseHeader = function(header) {
  267. return this.responseHeaders_.get(header.toLowerCase()) || '';
  268. };
  269. /** @override */
  270. goog.net.FetchXmlHttp.prototype.getAllResponseHeaders = function() {
  271. // TODO(user): Implement once the Headers extern support entries().
  272. return '';
  273. };
  274. /**
  275. * @param {!RequestCredentials} credentialsMode The credentials mode of the
  276. * Service Worker fetch.
  277. */
  278. goog.net.FetchXmlHttp.prototype.setCredentialsMode = function(credentialsMode) {
  279. this.credentialsMode_ = credentialsMode;
  280. };
  281. /**
  282. * @param {!RequestCache} cacheMode The cache mode of the Service Worker fetch.
  283. */
  284. goog.net.FetchXmlHttp.prototype.setCacheMode = function(cacheMode) {
  285. this.cacheMode_ = cacheMode;
  286. };
  287. /**
  288. * Dispatches the callback, if the callback attribute is defined.
  289. * @private
  290. */
  291. goog.net.FetchXmlHttp.prototype.dispatchCallback_ = function() {
  292. if (this.onreadystatechange) {
  293. this.onreadystatechange.call(this);
  294. }
  295. };