UNPKG

6.16 kBJavaScriptView Raw
1'use strict';
2
3var utils = require('./../utils');
4var settle = require('./../core/settle');
5var cookies = require('./../helpers/cookies');
6var buildURL = require('./../helpers/buildURL');
7var buildFullPath = require('../core/buildFullPath');
8var parseHeaders = require('./../helpers/parseHeaders');
9var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
10var createError = require('../core/createError');
11
12module.exports = function xhrAdapter(config) {
13 return new Promise(function dispatchXhrRequest(resolve, reject) {
14 var requestData = config.data;
15 var requestHeaders = config.headers;
16 var responseType = config.responseType;
17
18 if (utils.isFormData(requestData)) {
19 delete requestHeaders['Content-Type']; // Let the browser set it
20 }
21
22 var request = new XMLHttpRequest();
23
24 // HTTP basic authentication
25 if (config.auth) {
26 var username = config.auth.username || '';
27 var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
28 requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
29 }
30
31 var fullPath = buildFullPath(config.baseURL, config.url);
32 request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
33
34 // Set the request timeout in MS
35 request.timeout = config.timeout;
36
37 function onloadend() {
38 if (!request) {
39 return;
40 }
41 // Prepare the response
42 var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
43 var responseData = !responseType || responseType === 'text' || responseType === 'json' ?
44 request.responseText : request.response;
45 var response = {
46 data: responseData,
47 status: request.status,
48 statusText: request.statusText,
49 headers: responseHeaders,
50 config: config,
51 request: request
52 };
53
54 settle(resolve, reject, response);
55
56 // Clean up request
57 request = null;
58 }
59
60 if ('onloadend' in request) {
61 // Use onloadend if available
62 request.onloadend = onloadend;
63 } else {
64 // Listen for ready state to emulate onloadend
65 request.onreadystatechange = function handleLoad() {
66 if (!request || request.readyState !== 4) {
67 return;
68 }
69
70 // The request errored out and we didn't get a response, this will be
71 // handled by onerror instead
72 // With one exception: request that using file: protocol, most browsers
73 // will return status as 0 even though it's a successful request
74 if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
75 return;
76 }
77 // readystate handler is calling before onerror or ontimeout handlers,
78 // so we should call onloadend on the next 'tick'
79 setTimeout(onloadend);
80 };
81 }
82
83 // Handle browser request cancellation (as opposed to a manual cancellation)
84 request.onabort = function handleAbort() {
85 if (!request) {
86 return;
87 }
88
89 reject(createError('Request aborted', config, 'ECONNABORTED', request));
90
91 // Clean up request
92 request = null;
93 };
94
95 // Handle low level network errors
96 request.onerror = function handleError() {
97 // Real errors are hidden from us by the browser
98 // onerror should only fire if it's a network error
99 reject(createError('Network Error', config, null, request));
100
101 // Clean up request
102 request = null;
103 };
104
105 // Handle timeout
106 request.ontimeout = function handleTimeout() {
107 var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';
108 if (config.timeoutErrorMessage) {
109 timeoutErrorMessage = config.timeoutErrorMessage;
110 }
111 reject(createError(
112 timeoutErrorMessage,
113 config,
114 config.transitional && config.transitional.clarifyTimeoutError ? 'ETIMEDOUT' : 'ECONNABORTED',
115 request));
116
117 // Clean up request
118 request = null;
119 };
120
121 // Add xsrf header
122 // This is only done if running in a standard browser environment.
123 // Specifically not if we're in a web worker, or react-native.
124 if (utils.isStandardBrowserEnv()) {
125 // Add xsrf header
126 var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
127 cookies.read(config.xsrfCookieName) :
128 undefined;
129
130 if (xsrfValue) {
131 requestHeaders[config.xsrfHeaderName] = xsrfValue;
132 }
133 }
134
135 // Add headers to the request
136 if ('setRequestHeader' in request) {
137 utils.forEach(requestHeaders, function setRequestHeader(val, key) {
138 if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
139 // Remove Content-Type if data is undefined
140 delete requestHeaders[key];
141 } else {
142 // Otherwise add header to the request
143 request.setRequestHeader(key, val);
144 }
145 });
146 }
147
148 // Add withCredentials to request if needed
149 if (!utils.isUndefined(config.withCredentials)) {
150 request.withCredentials = !!config.withCredentials;
151 }
152
153 // Add responseType to request if needed
154 if (responseType && responseType !== 'json') {
155 request.responseType = config.responseType;
156 }
157
158 // Handle progress if needed
159 if (typeof config.onDownloadProgress === 'function') {
160 request.addEventListener('progress', config.onDownloadProgress);
161 }
162
163 // Not all browsers support upload events
164 if (typeof config.onUploadProgress === 'function' && request.upload) {
165 request.upload.addEventListener('progress', config.onUploadProgress);
166 }
167
168 if (config.cancelToken) {
169 // Handle cancellation
170 config.cancelToken.promise.then(function onCanceled(cancel) {
171 if (!request) {
172 return;
173 }
174
175 request.abort();
176 reject(cancel);
177 // Clean up request
178 request = null;
179 });
180 }
181
182 if (!requestData) {
183 requestData = null;
184 }
185
186 // Send the request
187 request.send(requestData);
188 });
189};