xhrio.js 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362
  1. // Copyright 2006 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 Wrapper class for handling XmlHttpRequests.
  16. *
  17. * One off requests can be sent through goog.net.XhrIo.send() or an
  18. * instance can be created to send multiple requests. Each request uses its
  19. * own XmlHttpRequest object and handles clearing of the event callback to
  20. * ensure no leaks.
  21. *
  22. * XhrIo is event based, it dispatches events on success, failure, finishing,
  23. * ready-state change, or progress (download and upload).
  24. *
  25. * The ready-state or timeout event fires first, followed by
  26. * a generic completed event. Then the abort, error, or success event
  27. * is fired as appropriate. Progress events are fired as they are
  28. * received. Lastly, the ready event will fire to indicate that the
  29. * object may be used to make another request.
  30. *
  31. * The error event may also be called before completed and
  32. * ready-state-change if the XmlHttpRequest.open() or .send() methods throw.
  33. *
  34. * This class does not support multiple requests, queuing, or prioritization.
  35. *
  36. * When progress events are supported by the browser, and progress is
  37. * enabled via .setProgressEventsEnabled(true), the
  38. * goog.net.EventType.PROGRESS event will be the re-dispatched browser
  39. * progress event. Additionally, a DOWNLOAD_PROGRESS or UPLOAD_PROGRESS event
  40. * will be fired for download and upload progress respectively.
  41. *
  42. */
  43. goog.provide('goog.net.XhrIo');
  44. goog.provide('goog.net.XhrIo.ResponseType');
  45. goog.require('goog.Timer');
  46. goog.require('goog.array');
  47. goog.require('goog.asserts');
  48. goog.require('goog.debug.entryPointRegistry');
  49. goog.require('goog.events.EventTarget');
  50. goog.require('goog.json');
  51. goog.require('goog.log');
  52. goog.require('goog.net.ErrorCode');
  53. goog.require('goog.net.EventType');
  54. goog.require('goog.net.HttpStatus');
  55. goog.require('goog.net.XmlHttp');
  56. goog.require('goog.string');
  57. goog.require('goog.structs');
  58. goog.require('goog.structs.Map');
  59. goog.require('goog.uri.utils');
  60. goog.require('goog.userAgent');
  61. goog.forwardDeclare('goog.Uri');
  62. /**
  63. * Basic class for handling XMLHttpRequests.
  64. * @param {goog.net.XmlHttpFactory=} opt_xmlHttpFactory Factory to use when
  65. * creating XMLHttpRequest objects.
  66. * @constructor
  67. * @extends {goog.events.EventTarget}
  68. */
  69. goog.net.XhrIo = function(opt_xmlHttpFactory) {
  70. goog.net.XhrIo.base(this, 'constructor');
  71. /**
  72. * Map of default headers to add to every request, use:
  73. * XhrIo.headers.set(name, value)
  74. * @type {!goog.structs.Map}
  75. */
  76. this.headers = new goog.structs.Map();
  77. /**
  78. * Optional XmlHttpFactory
  79. * @private {goog.net.XmlHttpFactory}
  80. */
  81. this.xmlHttpFactory_ = opt_xmlHttpFactory || null;
  82. /**
  83. * Whether XMLHttpRequest is active. A request is active from the time send()
  84. * is called until onReadyStateChange() is complete, or error() or abort()
  85. * is called.
  86. * @private {boolean}
  87. */
  88. this.active_ = false;
  89. /**
  90. * The XMLHttpRequest object that is being used for the transfer.
  91. * @private {?goog.net.XhrLike.OrNative}
  92. */
  93. this.xhr_ = null;
  94. /**
  95. * The options to use with the current XMLHttpRequest object.
  96. * @private {Object}
  97. */
  98. this.xhrOptions_ = null;
  99. /**
  100. * Last URL that was requested.
  101. * @private {string|goog.Uri}
  102. */
  103. this.lastUri_ = '';
  104. /**
  105. * Method for the last request.
  106. * @private {string}
  107. */
  108. this.lastMethod_ = '';
  109. /**
  110. * Last error code.
  111. * @private {!goog.net.ErrorCode}
  112. */
  113. this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
  114. /**
  115. * Last error message.
  116. * @private {Error|string}
  117. */
  118. this.lastError_ = '';
  119. /**
  120. * Used to ensure that we don't dispatch an multiple ERROR events. This can
  121. * happen in IE when it does a synchronous load and one error is handled in
  122. * the ready statte change and one is handled due to send() throwing an
  123. * exception.
  124. * @private {boolean}
  125. */
  126. this.errorDispatched_ = false;
  127. /**
  128. * Used to make sure we don't fire the complete event from inside a send call.
  129. * @private {boolean}
  130. */
  131. this.inSend_ = false;
  132. /**
  133. * Used in determining if a call to {@link #onReadyStateChange_} is from
  134. * within a call to this.xhr_.open.
  135. * @private {boolean}
  136. */
  137. this.inOpen_ = false;
  138. /**
  139. * Used in determining if a call to {@link #onReadyStateChange_} is from
  140. * within a call to this.xhr_.abort.
  141. * @private {boolean}
  142. */
  143. this.inAbort_ = false;
  144. /**
  145. * Number of milliseconds after which an incomplete request will be aborted
  146. * and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout
  147. * is set.
  148. * @private {number}
  149. */
  150. this.timeoutInterval_ = 0;
  151. /**
  152. * Timer to track request timeout.
  153. * @private {?number}
  154. */
  155. this.timeoutId_ = null;
  156. /**
  157. * The requested type for the response. The empty string means use the default
  158. * XHR behavior.
  159. * @private {goog.net.XhrIo.ResponseType}
  160. */
  161. this.responseType_ = goog.net.XhrIo.ResponseType.DEFAULT;
  162. /**
  163. * Whether a "credentialed" request is to be sent (one that is aware of
  164. * cookies and authentication). This is applicable only for cross-domain
  165. * requests and more recent browsers that support this part of the HTTP Access
  166. * Control standard.
  167. *
  168. * @see http://www.w3.org/TR/XMLHttpRequest/#the-withcredentials-attribute
  169. *
  170. * @private {boolean}
  171. */
  172. this.withCredentials_ = false;
  173. /**
  174. * Whether progress events are enabled for this request. This is
  175. * disabled by default because setting a progress event handler
  176. * causes pre-flight OPTIONS requests to be sent for CORS requests,
  177. * even in cases where a pre-flight request would not otherwise be
  178. * sent.
  179. *
  180. * @see http://xhr.spec.whatwg.org/#security-considerations
  181. *
  182. * Note that this can cause problems for Firefox 22 and below, as an
  183. * older "LSProgressEvent" will be dispatched by the browser. That
  184. * progress event is no longer supported, and can lead to failures,
  185. * including throwing exceptions.
  186. *
  187. * @see http://bugzilla.mozilla.org/show_bug.cgi?id=845631
  188. * @see b/23469793
  189. *
  190. * @private {boolean}
  191. */
  192. this.progressEventsEnabled_ = false;
  193. /**
  194. * True if we can use XMLHttpRequest's timeout directly.
  195. * @private {boolean}
  196. */
  197. this.useXhr2Timeout_ = false;
  198. };
  199. goog.inherits(goog.net.XhrIo, goog.events.EventTarget);
  200. /**
  201. * Response types that may be requested for XMLHttpRequests.
  202. * @enum {string}
  203. * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetype-attribute
  204. */
  205. goog.net.XhrIo.ResponseType = {
  206. DEFAULT: '',
  207. TEXT: 'text',
  208. DOCUMENT: 'document',
  209. // Not supported as of Chrome 10.0.612.1 dev
  210. BLOB: 'blob',
  211. ARRAY_BUFFER: 'arraybuffer'
  212. };
  213. /**
  214. * A reference to the XhrIo logger
  215. * @private {?goog.log.Logger}
  216. * @const
  217. */
  218. goog.net.XhrIo.prototype.logger_ = goog.log.getLogger('goog.net.XhrIo');
  219. /**
  220. * The Content-Type HTTP header name
  221. * @type {string}
  222. */
  223. goog.net.XhrIo.CONTENT_TYPE_HEADER = 'Content-Type';
  224. /**
  225. * The Content-Transfer-Encoding HTTP header name
  226. * @type {string}
  227. */
  228. goog.net.XhrIo.CONTENT_TRANSFER_ENCODING = 'Content-Transfer-Encoding';
  229. /**
  230. * The pattern matching the 'http' and 'https' URI schemes
  231. * @type {!RegExp}
  232. */
  233. goog.net.XhrIo.HTTP_SCHEME_PATTERN = /^https?$/i;
  234. /**
  235. * The methods that typically come along with form data. We set different
  236. * headers depending on whether the HTTP action is one of these.
  237. */
  238. goog.net.XhrIo.METHODS_WITH_FORM_DATA = ['POST', 'PUT'];
  239. /**
  240. * The Content-Type HTTP header value for a url-encoded form
  241. * @type {string}
  242. */
  243. goog.net.XhrIo.FORM_CONTENT_TYPE =
  244. 'application/x-www-form-urlencoded;charset=utf-8';
  245. /**
  246. * The XMLHttpRequest Level two timeout delay ms property name.
  247. *
  248. * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
  249. *
  250. * @private {string}
  251. * @const
  252. */
  253. goog.net.XhrIo.XHR2_TIMEOUT_ = 'timeout';
  254. /**
  255. * The XMLHttpRequest Level two ontimeout handler property name.
  256. *
  257. * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
  258. *
  259. * @private {string}
  260. * @const
  261. */
  262. goog.net.XhrIo.XHR2_ON_TIMEOUT_ = 'ontimeout';
  263. /**
  264. * All non-disposed instances of goog.net.XhrIo created
  265. * by {@link goog.net.XhrIo.send} are in this Array.
  266. * @see goog.net.XhrIo.cleanup
  267. * @private {!Array<!goog.net.XhrIo>}
  268. */
  269. goog.net.XhrIo.sendInstances_ = [];
  270. /**
  271. * Static send that creates a short lived instance of XhrIo to send the
  272. * request.
  273. * @see goog.net.XhrIo.cleanup
  274. * @param {string|goog.Uri} url Uri to make request to.
  275. * @param {?function(this:goog.net.XhrIo, ?)=} opt_callback Callback function
  276. * for when request is complete.
  277. * @param {string=} opt_method Send method, default: GET.
  278. * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}
  279. * opt_content Body data.
  280. * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
  281. * request.
  282. * @param {number=} opt_timeoutInterval Number of milliseconds after which an
  283. * incomplete request will be aborted; 0 means no timeout is set.
  284. * @param {boolean=} opt_withCredentials Whether to send credentials with the
  285. * request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}.
  286. * @return {!goog.net.XhrIo} The sent XhrIo.
  287. */
  288. goog.net.XhrIo.send = function(
  289. url, opt_callback, opt_method, opt_content, opt_headers,
  290. opt_timeoutInterval, opt_withCredentials) {
  291. var x = new goog.net.XhrIo();
  292. goog.net.XhrIo.sendInstances_.push(x);
  293. if (opt_callback) {
  294. x.listen(goog.net.EventType.COMPLETE, opt_callback);
  295. }
  296. x.listenOnce(goog.net.EventType.READY, x.cleanupSend_);
  297. if (opt_timeoutInterval) {
  298. x.setTimeoutInterval(opt_timeoutInterval);
  299. }
  300. if (opt_withCredentials) {
  301. x.setWithCredentials(opt_withCredentials);
  302. }
  303. x.send(url, opt_method, opt_content, opt_headers);
  304. return x;
  305. };
  306. /**
  307. * Disposes all non-disposed instances of goog.net.XhrIo created by
  308. * {@link goog.net.XhrIo.send}.
  309. * {@link goog.net.XhrIo.send} cleans up the goog.net.XhrIo instance
  310. * it creates when the request completes or fails. However, if
  311. * the request never completes, then the goog.net.XhrIo is not disposed.
  312. * This can occur if the window is unloaded before the request completes.
  313. * We could have {@link goog.net.XhrIo.send} return the goog.net.XhrIo
  314. * it creates and make the client of {@link goog.net.XhrIo.send} be
  315. * responsible for disposing it in this case. However, this makes things
  316. * significantly more complicated for the client, and the whole point
  317. * of {@link goog.net.XhrIo.send} is that it's simple and easy to use.
  318. * Clients of {@link goog.net.XhrIo.send} should call
  319. * {@link goog.net.XhrIo.cleanup} when doing final
  320. * cleanup on window unload.
  321. */
  322. goog.net.XhrIo.cleanup = function() {
  323. var instances = goog.net.XhrIo.sendInstances_;
  324. while (instances.length) {
  325. instances.pop().dispose();
  326. }
  327. };
  328. /**
  329. * Installs exception protection for all entry point introduced by
  330. * goog.net.XhrIo instances which are not protected by
  331. * {@link goog.debug.ErrorHandler#protectWindowSetTimeout},
  332. * {@link goog.debug.ErrorHandler#protectWindowSetInterval}, or
  333. * {@link goog.events.protectBrowserEventEntryPoint}.
  334. *
  335. * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
  336. * protect the entry point(s).
  337. */
  338. goog.net.XhrIo.protectEntryPoints = function(errorHandler) {
  339. goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =
  340. errorHandler.protectEntryPoint(
  341. goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);
  342. };
  343. /**
  344. * Disposes of the specified goog.net.XhrIo created by
  345. * {@link goog.net.XhrIo.send} and removes it from
  346. * {@link goog.net.XhrIo.pendingStaticSendInstances_}.
  347. * @private
  348. */
  349. goog.net.XhrIo.prototype.cleanupSend_ = function() {
  350. this.dispose();
  351. goog.array.remove(goog.net.XhrIo.sendInstances_, this);
  352. };
  353. /**
  354. * Returns the number of milliseconds after which an incomplete request will be
  355. * aborted, or 0 if no timeout is set.
  356. * @return {number} Timeout interval in milliseconds.
  357. */
  358. goog.net.XhrIo.prototype.getTimeoutInterval = function() {
  359. return this.timeoutInterval_;
  360. };
  361. /**
  362. * Sets the number of milliseconds after which an incomplete request will be
  363. * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no
  364. * timeout is set.
  365. * @param {number} ms Timeout interval in milliseconds; 0 means none.
  366. */
  367. goog.net.XhrIo.prototype.setTimeoutInterval = function(ms) {
  368. this.timeoutInterval_ = Math.max(0, ms);
  369. };
  370. /**
  371. * Sets the desired type for the response. At time of writing, this is only
  372. * supported in very recent versions of WebKit (10.0.612.1 dev and later).
  373. *
  374. * If this is used, the response may only be accessed via {@link #getResponse}.
  375. *
  376. * @param {goog.net.XhrIo.ResponseType} type The desired type for the response.
  377. */
  378. goog.net.XhrIo.prototype.setResponseType = function(type) {
  379. this.responseType_ = type;
  380. };
  381. /**
  382. * Gets the desired type for the response.
  383. * @return {goog.net.XhrIo.ResponseType} The desired type for the response.
  384. */
  385. goog.net.XhrIo.prototype.getResponseType = function() {
  386. return this.responseType_;
  387. };
  388. /**
  389. * Sets whether a "credentialed" request that is aware of cookie and
  390. * authentication information should be made. This option is only supported by
  391. * browsers that support HTTP Access Control. As of this writing, this option
  392. * is not supported in IE.
  393. *
  394. * @param {boolean} withCredentials Whether this should be a "credentialed"
  395. * request.
  396. */
  397. goog.net.XhrIo.prototype.setWithCredentials = function(withCredentials) {
  398. this.withCredentials_ = withCredentials;
  399. };
  400. /**
  401. * Gets whether a "credentialed" request is to be sent.
  402. * @return {boolean} The desired type for the response.
  403. */
  404. goog.net.XhrIo.prototype.getWithCredentials = function() {
  405. return this.withCredentials_;
  406. };
  407. /**
  408. * Sets whether progress events are enabled for this request. Note
  409. * that progress events require pre-flight OPTIONS request handling
  410. * for CORS requests, and may cause trouble with older browsers. See
  411. * progressEventsEnabled_ for details.
  412. * @param {boolean} enabled Whether progress events should be enabled.
  413. */
  414. goog.net.XhrIo.prototype.setProgressEventsEnabled = function(enabled) {
  415. this.progressEventsEnabled_ = enabled;
  416. };
  417. /**
  418. * Gets whether progress events are enabled.
  419. * @return {boolean} Whether progress events are enabled for this request.
  420. */
  421. goog.net.XhrIo.prototype.getProgressEventsEnabled = function() {
  422. return this.progressEventsEnabled_;
  423. };
  424. /**
  425. * Instance send that actually uses XMLHttpRequest to make a server call.
  426. * @param {string|goog.Uri} url Uri to make request to.
  427. * @param {string=} opt_method Send method, default: GET.
  428. * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}
  429. * opt_content Body data.
  430. * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
  431. * request.
  432. * @suppress {deprecated} Use deprecated goog.structs.forEach to allow different
  433. * types of parameters for opt_headers.
  434. */
  435. goog.net.XhrIo.prototype.send = function(
  436. url, opt_method, opt_content, opt_headers) {
  437. if (this.xhr_) {
  438. throw Error(
  439. '[goog.net.XhrIo] Object is active with another request=' +
  440. this.lastUri_ + '; newUri=' + url);
  441. }
  442. var method = opt_method ? opt_method.toUpperCase() : 'GET';
  443. this.lastUri_ = url;
  444. this.lastError_ = '';
  445. this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
  446. this.lastMethod_ = method;
  447. this.errorDispatched_ = false;
  448. this.active_ = true;
  449. // Use the factory to create the XHR object and options
  450. this.xhr_ = this.createXhr();
  451. this.xhrOptions_ = this.xmlHttpFactory_ ? this.xmlHttpFactory_.getOptions() :
  452. goog.net.XmlHttp.getOptions();
  453. // Set up the onreadystatechange callback
  454. this.xhr_.onreadystatechange = goog.bind(this.onReadyStateChange_, this);
  455. // Set up upload/download progress events, if progress events are supported.
  456. if (this.getProgressEventsEnabled() && 'onprogress' in this.xhr_) {
  457. this.xhr_.onprogress =
  458. goog.bind(function(e) { this.onProgressHandler_(e, true); }, this);
  459. if (this.xhr_.upload) {
  460. this.xhr_.upload.onprogress = goog.bind(this.onProgressHandler_, this);
  461. }
  462. }
  463. /**
  464. * Try to open the XMLHttpRequest (always async), if an error occurs here it
  465. * is generally permission denied
  466. */
  467. try {
  468. goog.log.fine(this.logger_, this.formatMsg_('Opening Xhr'));
  469. this.inOpen_ = true;
  470. this.xhr_.open(method, String(url), true); // Always async!
  471. this.inOpen_ = false;
  472. } catch (err) {
  473. goog.log.fine(
  474. this.logger_, this.formatMsg_('Error opening Xhr: ' + err.message));
  475. this.error_(goog.net.ErrorCode.EXCEPTION, err);
  476. return;
  477. }
  478. // We can't use null since this won't allow requests with form data to have a
  479. // content length specified which will cause some proxies to return a 411
  480. // error.
  481. var content = opt_content || '';
  482. var headers = this.headers.clone();
  483. // Add headers specific to this request
  484. if (opt_headers) {
  485. goog.structs.forEach(
  486. opt_headers, function(value, key) { headers.set(key, value); });
  487. }
  488. // Find whether a content type header is set, ignoring case.
  489. // HTTP header names are case-insensitive. See:
  490. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
  491. var contentTypeKey =
  492. goog.array.find(headers.getKeys(), goog.net.XhrIo.isContentTypeHeader_);
  493. var contentIsFormData =
  494. (goog.global['FormData'] && (content instanceof goog.global['FormData']));
  495. if (goog.array.contains(goog.net.XhrIo.METHODS_WITH_FORM_DATA, method) &&
  496. !contentTypeKey && !contentIsFormData) {
  497. // For requests typically with form data, default to the url-encoded form
  498. // content type unless this is a FormData request. For FormData,
  499. // the browser will automatically add a multipart/form-data content type
  500. // with an appropriate multipart boundary.
  501. headers.set(
  502. goog.net.XhrIo.CONTENT_TYPE_HEADER, goog.net.XhrIo.FORM_CONTENT_TYPE);
  503. }
  504. // Add the headers to the Xhr object
  505. headers.forEach(function(value, key) {
  506. this.xhr_.setRequestHeader(key, value);
  507. }, this);
  508. if (this.responseType_) {
  509. this.xhr_.responseType = this.responseType_;
  510. }
  511. // Set xhr_.withCredentials only when the value is different, or else in
  512. // synchronous XMLHtppRequest.open Firefox will throw an exception.
  513. // https://bugzilla.mozilla.org/show_bug.cgi?id=736340
  514. if ('withCredentials' in this.xhr_ &&
  515. this.xhr_.withCredentials !== this.withCredentials_) {
  516. this.xhr_.withCredentials = this.withCredentials_;
  517. }
  518. /**
  519. * Try to send the request, or other wise report an error (404 not found).
  520. */
  521. try {
  522. this.cleanUpTimeoutTimer_(); // Paranoid, should never be running.
  523. if (this.timeoutInterval_ > 0) {
  524. this.useXhr2Timeout_ = goog.net.XhrIo.shouldUseXhr2Timeout_(this.xhr_);
  525. goog.log.fine(
  526. this.logger_, this.formatMsg_(
  527. 'Will abort after ' + this.timeoutInterval_ +
  528. 'ms if incomplete, xhr2 ' + this.useXhr2Timeout_));
  529. if (this.useXhr2Timeout_) {
  530. this.xhr_[goog.net.XhrIo.XHR2_TIMEOUT_] = this.timeoutInterval_;
  531. this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] =
  532. goog.bind(this.timeout_, this);
  533. } else {
  534. this.timeoutId_ =
  535. goog.Timer.callOnce(this.timeout_, this.timeoutInterval_, this);
  536. }
  537. }
  538. goog.log.fine(this.logger_, this.formatMsg_('Sending request'));
  539. this.inSend_ = true;
  540. this.xhr_.send(content);
  541. this.inSend_ = false;
  542. } catch (err) {
  543. goog.log.fine(this.logger_, this.formatMsg_('Send error: ' + err.message));
  544. this.error_(goog.net.ErrorCode.EXCEPTION, err);
  545. }
  546. };
  547. /**
  548. * Determines if the argument is an XMLHttpRequest that supports the level 2
  549. * timeout value and event.
  550. *
  551. * Currently, FF 21.0 OS X has the fields but won't actually call the timeout
  552. * handler. Perhaps the confusion in the bug referenced below hasn't
  553. * entirely been resolved.
  554. *
  555. * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
  556. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=525816
  557. *
  558. * @param {!goog.net.XhrLike.OrNative} xhr The request.
  559. * @return {boolean} True if the request supports level 2 timeout.
  560. * @private
  561. */
  562. goog.net.XhrIo.shouldUseXhr2Timeout_ = function(xhr) {
  563. return goog.userAgent.IE && goog.userAgent.isVersionOrHigher(9) &&
  564. goog.isNumber(xhr[goog.net.XhrIo.XHR2_TIMEOUT_]) &&
  565. goog.isDef(xhr[goog.net.XhrIo.XHR2_ON_TIMEOUT_]);
  566. };
  567. /**
  568. * @param {string} header An HTTP header key.
  569. * @return {boolean} Whether the key is a content type header (ignoring
  570. * case.
  571. * @private
  572. */
  573. goog.net.XhrIo.isContentTypeHeader_ = function(header) {
  574. return goog.string.caseInsensitiveEquals(
  575. goog.net.XhrIo.CONTENT_TYPE_HEADER, header);
  576. };
  577. /**
  578. * Creates a new XHR object.
  579. * @return {!goog.net.XhrLike.OrNative} The newly created XHR object.
  580. * @protected
  581. */
  582. goog.net.XhrIo.prototype.createXhr = function() {
  583. return this.xmlHttpFactory_ ? this.xmlHttpFactory_.createInstance() :
  584. goog.net.XmlHttp();
  585. };
  586. /**
  587. * The request didn't complete after {@link goog.net.XhrIo#timeoutInterval_}
  588. * milliseconds; raises a {@link goog.net.EventType.TIMEOUT} event and aborts
  589. * the request.
  590. * @private
  591. */
  592. goog.net.XhrIo.prototype.timeout_ = function() {
  593. if (typeof goog == 'undefined') {
  594. // If goog is undefined then the callback has occurred as the application
  595. // is unloading and will error. Thus we let it silently fail.
  596. } else if (this.xhr_) {
  597. this.lastError_ =
  598. 'Timed out after ' + this.timeoutInterval_ + 'ms, aborting';
  599. this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT;
  600. goog.log.fine(this.logger_, this.formatMsg_(this.lastError_));
  601. this.dispatchEvent(goog.net.EventType.TIMEOUT);
  602. this.abort(goog.net.ErrorCode.TIMEOUT);
  603. }
  604. };
  605. /**
  606. * Something errorred, so inactivate, fire error callback and clean up
  607. * @param {goog.net.ErrorCode} errorCode The error code.
  608. * @param {Error} err The error object.
  609. * @private
  610. */
  611. goog.net.XhrIo.prototype.error_ = function(errorCode, err) {
  612. this.active_ = false;
  613. if (this.xhr_) {
  614. this.inAbort_ = true;
  615. this.xhr_.abort(); // Ensures XHR isn't hung (FF)
  616. this.inAbort_ = false;
  617. }
  618. this.lastError_ = err;
  619. this.lastErrorCode_ = errorCode;
  620. this.dispatchErrors_();
  621. this.cleanUpXhr_();
  622. };
  623. /**
  624. * Dispatches COMPLETE and ERROR in case of an error. This ensures that we do
  625. * not dispatch multiple error events.
  626. * @private
  627. */
  628. goog.net.XhrIo.prototype.dispatchErrors_ = function() {
  629. if (!this.errorDispatched_) {
  630. this.errorDispatched_ = true;
  631. this.dispatchEvent(goog.net.EventType.COMPLETE);
  632. this.dispatchEvent(goog.net.EventType.ERROR);
  633. }
  634. };
  635. /**
  636. * Abort the current XMLHttpRequest
  637. * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -
  638. * defaults to ABORT.
  639. */
  640. goog.net.XhrIo.prototype.abort = function(opt_failureCode) {
  641. if (this.xhr_ && this.active_) {
  642. goog.log.fine(this.logger_, this.formatMsg_('Aborting'));
  643. this.active_ = false;
  644. this.inAbort_ = true;
  645. this.xhr_.abort();
  646. this.inAbort_ = false;
  647. this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;
  648. this.dispatchEvent(goog.net.EventType.COMPLETE);
  649. this.dispatchEvent(goog.net.EventType.ABORT);
  650. this.cleanUpXhr_();
  651. }
  652. };
  653. /**
  654. * Nullifies all callbacks to reduce risks of leaks.
  655. * @override
  656. * @protected
  657. */
  658. goog.net.XhrIo.prototype.disposeInternal = function() {
  659. if (this.xhr_) {
  660. // We explicitly do not call xhr_.abort() unless active_ is still true.
  661. // This is to avoid unnecessarily aborting a successful request when
  662. // dispose() is called in a callback triggered by a complete response, but
  663. // in which browser cleanup has not yet finished.
  664. // (See http://b/issue?id=1684217.)
  665. if (this.active_) {
  666. this.active_ = false;
  667. this.inAbort_ = true;
  668. this.xhr_.abort();
  669. this.inAbort_ = false;
  670. }
  671. this.cleanUpXhr_(true);
  672. }
  673. goog.net.XhrIo.base(this, 'disposeInternal');
  674. };
  675. /**
  676. * Internal handler for the XHR object's readystatechange event. This method
  677. * checks the status and the readystate and fires the correct callbacks.
  678. * If the request has ended, the handlers are cleaned up and the XHR object is
  679. * nullified.
  680. * @private
  681. */
  682. goog.net.XhrIo.prototype.onReadyStateChange_ = function() {
  683. if (this.isDisposed()) {
  684. // This method is the target of an untracked goog.Timer.callOnce().
  685. return;
  686. }
  687. if (!this.inOpen_ && !this.inSend_ && !this.inAbort_) {
  688. // Were not being called from within a call to this.xhr_.send
  689. // this.xhr_.abort, or this.xhr_.open, so this is an entry point
  690. this.onReadyStateChangeEntryPoint_();
  691. } else {
  692. this.onReadyStateChangeHelper_();
  693. }
  694. };
  695. /**
  696. * Used to protect the onreadystatechange handler entry point. Necessary
  697. * as {#onReadyStateChange_} maybe called from within send or abort, this
  698. * method is only called when {#onReadyStateChange_} is called as an
  699. * entry point.
  700. * {@see #protectEntryPoints}
  701. * @private
  702. */
  703. goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = function() {
  704. this.onReadyStateChangeHelper_();
  705. };
  706. /**
  707. * Helper for {@link #onReadyStateChange_}. This is used so that
  708. * entry point calls to {@link #onReadyStateChange_} can be routed through
  709. * {@link #onReadyStateChangeEntryPoint_}.
  710. * @private
  711. */
  712. goog.net.XhrIo.prototype.onReadyStateChangeHelper_ = function() {
  713. if (!this.active_) {
  714. // can get called inside abort call
  715. return;
  716. }
  717. if (typeof goog == 'undefined') {
  718. // NOTE(user): If goog is undefined then the callback has occurred as the
  719. // application is unloading and will error. Thus we let it silently fail.
  720. } else if (
  721. this.xhrOptions_[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] &&
  722. this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE &&
  723. this.getStatus() == 2) {
  724. // NOTE(user): In IE if send() errors on a *local* request the readystate
  725. // is still changed to COMPLETE. We need to ignore it and allow the
  726. // try/catch around send() to pick up the error.
  727. goog.log.fine(
  728. this.logger_,
  729. this.formatMsg_('Local request error detected and ignored'));
  730. } else {
  731. // In IE when the response has been cached we sometimes get the callback
  732. // from inside the send call and this usually breaks code that assumes that
  733. // XhrIo is asynchronous. If that is the case we delay the callback
  734. // using a timer.
  735. if (this.inSend_ &&
  736. this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE) {
  737. goog.Timer.callOnce(this.onReadyStateChange_, 0, this);
  738. return;
  739. }
  740. this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
  741. // readyState indicates the transfer has finished
  742. if (this.isComplete()) {
  743. goog.log.fine(this.logger_, this.formatMsg_('Request complete'));
  744. this.active_ = false;
  745. try {
  746. // Call the specific callbacks for success or failure. Only call the
  747. // success if the status is 200 (HTTP_OK) or 304 (HTTP_CACHED)
  748. if (this.isSuccess()) {
  749. this.dispatchEvent(goog.net.EventType.COMPLETE);
  750. this.dispatchEvent(goog.net.EventType.SUCCESS);
  751. } else {
  752. this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;
  753. this.lastError_ =
  754. this.getStatusText() + ' [' + this.getStatus() + ']';
  755. this.dispatchErrors_();
  756. }
  757. } finally {
  758. this.cleanUpXhr_();
  759. }
  760. }
  761. }
  762. };
  763. /**
  764. * Internal handler for the XHR object's onprogress event. Fires both a generic
  765. * PROGRESS event and either a DOWNLOAD_PROGRESS or UPLOAD_PROGRESS event to
  766. * allow specific binding for each XHR progress event.
  767. * @param {!ProgressEvent} e XHR progress event.
  768. * @param {boolean=} opt_isDownload Whether the current progress event is from a
  769. * download. Used to determine whether DOWNLOAD_PROGRESS or UPLOAD_PROGRESS
  770. * event should be dispatched.
  771. * @private
  772. */
  773. goog.net.XhrIo.prototype.onProgressHandler_ = function(e, opt_isDownload) {
  774. goog.asserts.assert(
  775. e.type === goog.net.EventType.PROGRESS,
  776. 'goog.net.EventType.PROGRESS is of the same type as raw XHR progress.');
  777. this.dispatchEvent(
  778. goog.net.XhrIo.buildProgressEvent_(e, goog.net.EventType.PROGRESS));
  779. this.dispatchEvent(
  780. goog.net.XhrIo.buildProgressEvent_(
  781. e, opt_isDownload ? goog.net.EventType.DOWNLOAD_PROGRESS :
  782. goog.net.EventType.UPLOAD_PROGRESS));
  783. };
  784. /**
  785. * Creates a representation of the native ProgressEvent. IE doesn't support
  786. * constructing ProgressEvent via "new", and the alternatives (e.g.,
  787. * ProgressEvent.initProgressEvent) are non-standard or deprecated.
  788. * @param {!ProgressEvent} e XHR progress event.
  789. * @param {!goog.net.EventType} eventType The type of the event.
  790. * @return {!ProgressEvent} The progress event.
  791. * @private
  792. */
  793. goog.net.XhrIo.buildProgressEvent_ = function(e, eventType) {
  794. return /** @type {!ProgressEvent} */ ({
  795. type: eventType,
  796. lengthComputable: e.lengthComputable,
  797. loaded: e.loaded,
  798. total: e.total
  799. });
  800. };
  801. /**
  802. * Remove the listener to protect against leaks, and nullify the XMLHttpRequest
  803. * object.
  804. * @param {boolean=} opt_fromDispose If this is from the dispose (don't want to
  805. * fire any events).
  806. * @private
  807. */
  808. goog.net.XhrIo.prototype.cleanUpXhr_ = function(opt_fromDispose) {
  809. if (this.xhr_) {
  810. // Cancel any pending timeout event handler.
  811. this.cleanUpTimeoutTimer_();
  812. // Save reference so we can mark it as closed after the READY event. The
  813. // READY event may trigger another request, thus we must nullify this.xhr_
  814. var xhr = this.xhr_;
  815. var clearedOnReadyStateChange =
  816. this.xhrOptions_[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] ?
  817. goog.nullFunction :
  818. null;
  819. this.xhr_ = null;
  820. this.xhrOptions_ = null;
  821. if (!opt_fromDispose) {
  822. this.dispatchEvent(goog.net.EventType.READY);
  823. }
  824. try {
  825. // NOTE(user): Not nullifying in FireFox can still leak if the callbacks
  826. // are defined in the same scope as the instance of XhrIo. But, IE doesn't
  827. // allow you to set the onreadystatechange to NULL so nullFunction is
  828. // used.
  829. xhr.onreadystatechange = clearedOnReadyStateChange;
  830. } catch (e) {
  831. // This seems to occur with a Gears HTTP request. Delayed the setting of
  832. // this onreadystatechange until after READY is sent out and catching the
  833. // error to see if we can track down the problem.
  834. goog.log.error(
  835. this.logger_,
  836. 'Problem encountered resetting onreadystatechange: ' + e.message);
  837. }
  838. }
  839. };
  840. /**
  841. * Make sure the timeout timer isn't running.
  842. * @private
  843. */
  844. goog.net.XhrIo.prototype.cleanUpTimeoutTimer_ = function() {
  845. if (this.xhr_ && this.useXhr2Timeout_) {
  846. this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] = null;
  847. }
  848. if (goog.isNumber(this.timeoutId_)) {
  849. goog.Timer.clear(this.timeoutId_);
  850. this.timeoutId_ = null;
  851. }
  852. };
  853. /**
  854. * @return {boolean} Whether there is an active request.
  855. */
  856. goog.net.XhrIo.prototype.isActive = function() {
  857. return !!this.xhr_;
  858. };
  859. /**
  860. * @return {boolean} Whether the request has completed.
  861. */
  862. goog.net.XhrIo.prototype.isComplete = function() {
  863. return this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE;
  864. };
  865. /**
  866. * @return {boolean} Whether the request completed with a success.
  867. */
  868. goog.net.XhrIo.prototype.isSuccess = function() {
  869. var status = this.getStatus();
  870. // A zero status code is considered successful for local files.
  871. return goog.net.HttpStatus.isSuccess(status) ||
  872. status === 0 && !this.isLastUriEffectiveSchemeHttp_();
  873. };
  874. /**
  875. * @return {boolean} whether the effective scheme of the last URI that was
  876. * fetched was 'http' or 'https'.
  877. * @private
  878. */
  879. goog.net.XhrIo.prototype.isLastUriEffectiveSchemeHttp_ = function() {
  880. var scheme = goog.uri.utils.getEffectiveScheme(String(this.lastUri_));
  881. return goog.net.XhrIo.HTTP_SCHEME_PATTERN.test(scheme);
  882. };
  883. /**
  884. * Get the readystate from the Xhr object
  885. * Will only return correct result when called from the context of a callback
  886. * @return {goog.net.XmlHttp.ReadyState} goog.net.XmlHttp.ReadyState.*.
  887. */
  888. goog.net.XhrIo.prototype.getReadyState = function() {
  889. return this.xhr_ ?
  890. /** @type {goog.net.XmlHttp.ReadyState} */ (this.xhr_.readyState) :
  891. goog.net.XmlHttp.ReadyState
  892. .UNINITIALIZED;
  893. };
  894. /**
  895. * Get the status from the Xhr object
  896. * Will only return correct result when called from the context of a callback
  897. * @return {number} Http status.
  898. */
  899. goog.net.XhrIo.prototype.getStatus = function() {
  900. /**
  901. * IE doesn't like you checking status until the readystate is greater than 2
  902. * (i.e. it is receiving or complete). The try/catch is used for when the
  903. * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.
  904. */
  905. try {
  906. return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?
  907. this.xhr_.status :
  908. -1;
  909. } catch (e) {
  910. return -1;
  911. }
  912. };
  913. /**
  914. * Get the status text from the Xhr object
  915. * Will only return correct result when called from the context of a callback
  916. * @return {string} Status text.
  917. */
  918. goog.net.XhrIo.prototype.getStatusText = function() {
  919. /**
  920. * IE doesn't like you checking status until the readystate is greater than 2
  921. * (i.e. it is receiving or complete). The try/catch is used for when the
  922. * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.
  923. */
  924. try {
  925. return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?
  926. this.xhr_.statusText :
  927. '';
  928. } catch (e) {
  929. goog.log.fine(this.logger_, 'Can not get status: ' + e.message);
  930. return '';
  931. }
  932. };
  933. /**
  934. * Get the last Uri that was requested
  935. * @return {string} Last Uri.
  936. */
  937. goog.net.XhrIo.prototype.getLastUri = function() {
  938. return String(this.lastUri_);
  939. };
  940. /**
  941. * Get the response text from the Xhr object
  942. * Will only return correct result when called from the context of a callback.
  943. * @return {string} Result from the server, or '' if no result available.
  944. */
  945. goog.net.XhrIo.prototype.getResponseText = function() {
  946. try {
  947. return this.xhr_ ? this.xhr_.responseText : '';
  948. } catch (e) {
  949. // http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute
  950. // states that responseText should return '' (and responseXML null)
  951. // when the state is not LOADING or DONE. Instead, IE can
  952. // throw unexpected exceptions, for example when a request is aborted
  953. // or no data is available yet.
  954. goog.log.fine(this.logger_, 'Can not get responseText: ' + e.message);
  955. return '';
  956. }
  957. };
  958. /**
  959. * Get the response body from the Xhr object. This property is only available
  960. * in IE since version 7 according to MSDN:
  961. * http://msdn.microsoft.com/en-us/library/ie/ms534368(v=vs.85).aspx
  962. * Will only return correct result when called from the context of a callback.
  963. *
  964. * One option is to construct a VBArray from the returned object and convert
  965. * it to a JavaScript array using the toArray method:
  966. * {@code (new window['VBArray'](xhrIo.getResponseBody())).toArray()}
  967. * This will result in an array of numbers in the range of [0..255]
  968. *
  969. * Another option is to use the VBScript CStr method to convert it into a
  970. * string as outlined in http://stackoverflow.com/questions/1919972
  971. *
  972. * @return {Object} Binary result from the server or null if not available.
  973. */
  974. goog.net.XhrIo.prototype.getResponseBody = function() {
  975. try {
  976. if (this.xhr_ && 'responseBody' in this.xhr_) {
  977. return this.xhr_['responseBody'];
  978. }
  979. } catch (e) {
  980. // IE can throw unexpected exceptions, for example when a request is aborted
  981. // or no data is yet available.
  982. goog.log.fine(this.logger_, 'Can not get responseBody: ' + e.message);
  983. }
  984. return null;
  985. };
  986. /**
  987. * Get the response XML from the Xhr object
  988. * Will only return correct result when called from the context of a callback.
  989. * @return {Document} The DOM Document representing the XML file, or null
  990. * if no result available.
  991. */
  992. goog.net.XhrIo.prototype.getResponseXml = function() {
  993. try {
  994. return this.xhr_ ? this.xhr_.responseXML : null;
  995. } catch (e) {
  996. goog.log.fine(this.logger_, 'Can not get responseXML: ' + e.message);
  997. return null;
  998. }
  999. };
  1000. /**
  1001. * Get the response and evaluates it as JSON from the Xhr object
  1002. * Will only return correct result when called from the context of a callback
  1003. * @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for
  1004. * stripping of the response before parsing. This needs to be set only if
  1005. * your backend server prepends the same prefix string to the JSON response.
  1006. * @throws Error if the response text is invalid JSON.
  1007. * @return {Object|undefined} JavaScript object.
  1008. */
  1009. goog.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) {
  1010. if (!this.xhr_) {
  1011. return undefined;
  1012. }
  1013. var responseText = this.xhr_.responseText;
  1014. if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) {
  1015. responseText = responseText.substring(opt_xssiPrefix.length);
  1016. }
  1017. return goog.json.parse(responseText);
  1018. };
  1019. /**
  1020. * Get the response as the type specificed by {@link #setResponseType}. At time
  1021. * of writing, this is only directly supported in very recent versions of WebKit
  1022. * (10.0.612.1 dev and later). If the field is not supported directly, we will
  1023. * try to emulate it.
  1024. *
  1025. * Emulating the response means following the rules laid out at
  1026. * http://www.w3.org/TR/XMLHttpRequest/#the-response-attribute
  1027. *
  1028. * On browsers with no support for this (Chrome < 10, Firefox < 4, etc), only
  1029. * response types of DEFAULT or TEXT may be used, and the response returned will
  1030. * be the text response.
  1031. *
  1032. * On browsers with Mozilla's draft support for array buffers (Firefox 4, 5),
  1033. * only response types of DEFAULT, TEXT, and ARRAY_BUFFER may be used, and the
  1034. * response returned will be either the text response or the Mozilla
  1035. * implementation of the array buffer response.
  1036. *
  1037. * On browsers will full support, any valid response type supported by the
  1038. * browser may be used, and the response provided by the browser will be
  1039. * returned.
  1040. *
  1041. * @return {*} The response.
  1042. */
  1043. goog.net.XhrIo.prototype.getResponse = function() {
  1044. try {
  1045. if (!this.xhr_) {
  1046. return null;
  1047. }
  1048. if ('response' in this.xhr_) {
  1049. return this.xhr_.response;
  1050. }
  1051. switch (this.responseType_) {
  1052. case goog.net.XhrIo.ResponseType.DEFAULT:
  1053. case goog.net.XhrIo.ResponseType.TEXT:
  1054. return this.xhr_.responseText;
  1055. // DOCUMENT and BLOB don't need to be handled here because they are
  1056. // introduced in the same spec that adds the .response field, and would
  1057. // have been caught above.
  1058. // ARRAY_BUFFER needs an implementation for Firefox 4, where it was
  1059. // implemented using a draft spec rather than the final spec.
  1060. case goog.net.XhrIo.ResponseType.ARRAY_BUFFER:
  1061. if ('mozResponseArrayBuffer' in this.xhr_) {
  1062. return this.xhr_.mozResponseArrayBuffer;
  1063. }
  1064. }
  1065. // Fell through to a response type that is not supported on this browser.
  1066. goog.log.error(
  1067. this.logger_, 'Response type ' + this.responseType_ + ' is not ' +
  1068. 'supported on this browser');
  1069. return null;
  1070. } catch (e) {
  1071. goog.log.fine(this.logger_, 'Can not get response: ' + e.message);
  1072. return null;
  1073. }
  1074. };
  1075. /**
  1076. * Get the value of the response-header with the given name from the Xhr object
  1077. * Will only return correct result when called from the context of a callback
  1078. * and the request has completed
  1079. * @param {string} key The name of the response-header to retrieve.
  1080. * @return {string|undefined} The value of the response-header named key.
  1081. */
  1082. goog.net.XhrIo.prototype.getResponseHeader = function(key) {
  1083. if (!this.xhr_ || !this.isComplete()) {
  1084. return undefined;
  1085. }
  1086. var value = this.xhr_.getResponseHeader(key);
  1087. return goog.isNull(value) ? undefined : value;
  1088. };
  1089. /**
  1090. * Gets the text of all the headers in the response.
  1091. * Will only return correct result when called from the context of a callback
  1092. * and the request has completed.
  1093. * @return {string} The value of the response headers or empty string.
  1094. */
  1095. goog.net.XhrIo.prototype.getAllResponseHeaders = function() {
  1096. return this.xhr_ && this.isComplete() ? this.xhr_.getAllResponseHeaders() :
  1097. '';
  1098. };
  1099. /**
  1100. * Returns all response headers as a key-value map.
  1101. * Multiple values for the same header key can be combined into one,
  1102. * separated by a comma and a space.
  1103. * Note that the native getResponseHeader method for retrieving a single header
  1104. * does a case insensitive match on the header name. This method does not
  1105. * include any case normalization logic, it will just return a key-value
  1106. * representation of the headers.
  1107. * See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method
  1108. * @return {!Object<string, string>} An object with the header keys as keys
  1109. * and header values as values.
  1110. */
  1111. goog.net.XhrIo.prototype.getResponseHeaders = function() {
  1112. var headersObject = {};
  1113. var headersArray = this.getAllResponseHeaders().split('\r\n');
  1114. for (var i = 0; i < headersArray.length; i++) {
  1115. if (goog.string.isEmptyOrWhitespace(headersArray[i])) {
  1116. continue;
  1117. }
  1118. var keyValue = goog.string.splitLimit(headersArray[i], ': ', 2);
  1119. if (headersObject[keyValue[0]]) {
  1120. headersObject[keyValue[0]] += ', ' + keyValue[1];
  1121. } else {
  1122. headersObject[keyValue[0]] = keyValue[1];
  1123. }
  1124. }
  1125. return headersObject;
  1126. };
  1127. /**
  1128. * Get the value of the response-header with the given name from the Xhr object.
  1129. * As opposed to {@link #getResponseHeader}, this method does not require that
  1130. * the request has completed.
  1131. * @param {string} key The name of the response-header to retrieve.
  1132. * @return {?string} The value of the response-header, or null if it is
  1133. * unavailable.
  1134. */
  1135. goog.net.XhrIo.prototype.getStreamingResponseHeader = function(key) {
  1136. return this.xhr_ ? this.xhr_.getResponseHeader(key) : null;
  1137. };
  1138. /**
  1139. * Gets the text of all the headers in the response. As opposed to
  1140. * {@link #getAllResponseHeaders}, this method does not require that the request
  1141. * has completed.
  1142. * @return {string} The value of the response headers or empty string.
  1143. */
  1144. goog.net.XhrIo.prototype.getAllStreamingResponseHeaders = function() {
  1145. return this.xhr_ ? this.xhr_.getAllResponseHeaders() : '';
  1146. };
  1147. /**
  1148. * Get the last error message
  1149. * @return {goog.net.ErrorCode} Last error code.
  1150. */
  1151. goog.net.XhrIo.prototype.getLastErrorCode = function() {
  1152. return this.lastErrorCode_;
  1153. };
  1154. /**
  1155. * Get the last error message
  1156. * @return {string} Last error message.
  1157. */
  1158. goog.net.XhrIo.prototype.getLastError = function() {
  1159. return goog.isString(this.lastError_) ? this.lastError_ :
  1160. String(this.lastError_);
  1161. };
  1162. /**
  1163. * Adds the last method, status and URI to the message. This is used to add
  1164. * this information to the logging calls.
  1165. * @param {string} msg The message text that we want to add the extra text to.
  1166. * @return {string} The message with the extra text appended.
  1167. * @private
  1168. */
  1169. goog.net.XhrIo.prototype.formatMsg_ = function(msg) {
  1170. return msg + ' [' + this.lastMethod_ + ' ' + this.lastUri_ + ' ' +
  1171. this.getStatus() + ']';
  1172. };
  1173. // Register the xhr handler as an entry point, so that
  1174. // it can be monitored for exception handling, etc.
  1175. goog.debug.entryPointRegistry.register(
  1176. /**
  1177. * @param {function(!Function): !Function} transformer The transforming
  1178. * function.
  1179. */
  1180. function(transformer) {
  1181. goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =
  1182. transformer(goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);
  1183. });