xhr.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. 'use strict';
  2. var utils = require('./../utils');
  3. var settle = require('./../core/settle');
  4. var buildURL = require('./../helpers/buildURL');
  5. var parseHeaders = require('./../helpers/parseHeaders');
  6. var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
  7. var createError = require('../core/createError');
  8. var btoa = (typeof window !== 'undefined' && window.btoa && window.btoa.bind(window)) || require('./../helpers/btoa');
  9. module.exports = function xhrAdapter(config) {
  10. return new Promise(function dispatchXhrRequest(resolve, reject) {
  11. var requestData = config.data;
  12. var requestHeaders = config.headers;
  13. if (utils.isFormData(requestData)) {
  14. delete requestHeaders['Content-Type']; // Let the browser set it
  15. }
  16. var request = new XMLHttpRequest();
  17. var loadEvent = 'onreadystatechange';
  18. var xDomain = false;
  19. // For IE 8/9 CORS support
  20. // Only supports POST and GET calls and doesn't returns the response headers.
  21. // DON'T do this for testing b/c XMLHttpRequest is mocked, not XDomainRequest.
  22. if (process.env.NODE_ENV !== 'test' &&
  23. typeof window !== 'undefined' &&
  24. window.XDomainRequest && !('withCredentials' in request) &&
  25. !isURLSameOrigin(config.url)) {
  26. request = new window.XDomainRequest();
  27. loadEvent = 'onload';
  28. xDomain = true;
  29. request.onprogress = function handleProgress() {};
  30. request.ontimeout = function handleTimeout() {};
  31. }
  32. // HTTP basic authentication
  33. if (config.auth) {
  34. var username = config.auth.username || '';
  35. var password = config.auth.password || '';
  36. requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
  37. }
  38. request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
  39. // Set the request timeout in MS
  40. request.timeout = config.timeout;
  41. // Listen for ready state
  42. request[loadEvent] = function handleLoad() {
  43. if (!request || (request.readyState !== 4 && !xDomain)) {
  44. return;
  45. }
  46. // The request errored out and we didn't get a response, this will be
  47. // handled by onerror instead
  48. // With one exception: request that using file: protocol, most browsers
  49. // will return status as 0 even though it's a successful request
  50. if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
  51. return;
  52. }
  53. // Prepare the response
  54. var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
  55. var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
  56. var response = {
  57. data: responseData,
  58. // IE sends 1223 instead of 204 (https://github.com/axios/axios/issues/201)
  59. status: request.status === 1223 ? 204 : request.status,
  60. statusText: request.status === 1223 ? 'No Content' : request.statusText,
  61. headers: responseHeaders,
  62. config: config,
  63. request: request
  64. };
  65. settle(resolve, reject, response);
  66. // Clean up request
  67. request = null;
  68. };
  69. // Handle low level network errors
  70. request.onerror = function handleError() {
  71. // Real errors are hidden from us by the browser
  72. // onerror should only fire if it's a network error
  73. reject(createError('Network Error', config, null, request));
  74. // Clean up request
  75. request = null;
  76. };
  77. // Handle timeout
  78. request.ontimeout = function handleTimeout() {
  79. reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
  80. request));
  81. // Clean up request
  82. request = null;
  83. };
  84. // Add xsrf header
  85. // This is only done if running in a standard browser environment.
  86. // Specifically not if we're in a web worker, or react-native.
  87. if (utils.isStandardBrowserEnv()) {
  88. var cookies = require('./../helpers/cookies');
  89. // Add xsrf header
  90. var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
  91. cookies.read(config.xsrfCookieName) :
  92. undefined;
  93. if (xsrfValue) {
  94. requestHeaders[config.xsrfHeaderName] = xsrfValue;
  95. }
  96. }
  97. // Add headers to the request
  98. if ('setRequestHeader' in request) {
  99. utils.forEach(requestHeaders, function setRequestHeader(val, key) {
  100. if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
  101. // Remove Content-Type if data is undefined
  102. delete requestHeaders[key];
  103. } else {
  104. // Otherwise add header to the request
  105. request.setRequestHeader(key, val);
  106. }
  107. });
  108. }
  109. // Add withCredentials to request if needed
  110. if (config.withCredentials) {
  111. request.withCredentials = true;
  112. }
  113. // Add responseType to request if needed
  114. if (config.responseType) {
  115. try {
  116. request.responseType = config.responseType;
  117. } catch (e) {
  118. // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
  119. // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
  120. if (config.responseType !== 'json') {
  121. throw e;
  122. }
  123. }
  124. }
  125. // Handle progress if needed
  126. if (typeof config.onDownloadProgress === 'function') {
  127. request.addEventListener('progress', config.onDownloadProgress);
  128. }
  129. // Not all browsers support upload events
  130. if (typeof config.onUploadProgress === 'function' && request.upload) {
  131. request.upload.addEventListener('progress', config.onUploadProgress);
  132. }
  133. if (config.cancelToken) {
  134. // Handle cancellation
  135. config.cancelToken.promise.then(function onCanceled(cancel) {
  136. if (!request) {
  137. return;
  138. }
  139. request.abort();
  140. reject(cancel);
  141. // Clean up request
  142. request = null;
  143. });
  144. }
  145. if (requestData === undefined) {
  146. requestData = null;
  147. }
  148. // Send the request
  149. request.send(requestData);
  150. });
  151. };