source: node_modules/axios/lib/adapters/xhr.js@ d24f17c

main
Last change on this file since d24f17c was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 8.6 KB
Line 
1'use strict';
2
3import utils from './../utils.js';
4import settle from './../core/settle.js';
5import cookies from './../helpers/cookies.js';
6import buildURL from './../helpers/buildURL.js';
7import buildFullPath from '../core/buildFullPath.js';
8import isURLSameOrigin from './../helpers/isURLSameOrigin.js';
9import transitionalDefaults from '../defaults/transitional.js';
10import AxiosError from '../core/AxiosError.js';
11import CanceledError from '../cancel/CanceledError.js';
12import parseProtocol from '../helpers/parseProtocol.js';
13import platform from '../platform/index.js';
14import AxiosHeaders from '../core/AxiosHeaders.js';
15import speedometer from '../helpers/speedometer.js';
16
17function progressEventReducer(listener, isDownloadStream) {
18 let bytesNotified = 0;
19 const _speedometer = speedometer(50, 250);
20
21 return e => {
22 const loaded = e.loaded;
23 const total = e.lengthComputable ? e.total : undefined;
24 const progressBytes = loaded - bytesNotified;
25 const rate = _speedometer(progressBytes);
26 const inRange = loaded <= total;
27
28 bytesNotified = loaded;
29
30 const data = {
31 loaded,
32 total,
33 progress: total ? (loaded / total) : undefined,
34 bytes: progressBytes,
35 rate: rate ? rate : undefined,
36 estimated: rate && total && inRange ? (total - loaded) / rate : undefined,
37 event: e
38 };
39
40 data[isDownloadStream ? 'download' : 'upload'] = true;
41
42 listener(data);
43 };
44}
45
46const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
47
48export default isXHRAdapterSupported && function (config) {
49 return new Promise(function dispatchXhrRequest(resolve, reject) {
50 let requestData = config.data;
51 const requestHeaders = AxiosHeaders.from(config.headers).normalize();
52 let {responseType, withXSRFToken} = config;
53 let onCanceled;
54 function done() {
55 if (config.cancelToken) {
56 config.cancelToken.unsubscribe(onCanceled);
57 }
58
59 if (config.signal) {
60 config.signal.removeEventListener('abort', onCanceled);
61 }
62 }
63
64 let contentType;
65
66 if (utils.isFormData(requestData)) {
67 if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv) {
68 requestHeaders.setContentType(false); // Let the browser set it
69 } else if ((contentType = requestHeaders.getContentType()) !== false) {
70 // fix semicolon duplication issue for ReactNative FormData implementation
71 const [type, ...tokens] = contentType ? contentType.split(';').map(token => token.trim()).filter(Boolean) : [];
72 requestHeaders.setContentType([type || 'multipart/form-data', ...tokens].join('; '));
73 }
74 }
75
76 let request = new XMLHttpRequest();
77
78 // HTTP basic authentication
79 if (config.auth) {
80 const username = config.auth.username || '';
81 const password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
82 requestHeaders.set('Authorization', 'Basic ' + btoa(username + ':' + password));
83 }
84
85 const fullPath = buildFullPath(config.baseURL, config.url);
86
87 request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
88
89 // Set the request timeout in MS
90 request.timeout = config.timeout;
91
92 function onloadend() {
93 if (!request) {
94 return;
95 }
96 // Prepare the response
97 const responseHeaders = AxiosHeaders.from(
98 'getAllResponseHeaders' in request && request.getAllResponseHeaders()
99 );
100 const responseData = !responseType || responseType === 'text' || responseType === 'json' ?
101 request.responseText : request.response;
102 const response = {
103 data: responseData,
104 status: request.status,
105 statusText: request.statusText,
106 headers: responseHeaders,
107 config,
108 request
109 };
110
111 settle(function _resolve(value) {
112 resolve(value);
113 done();
114 }, function _reject(err) {
115 reject(err);
116 done();
117 }, response);
118
119 // Clean up request
120 request = null;
121 }
122
123 if ('onloadend' in request) {
124 // Use onloadend if available
125 request.onloadend = onloadend;
126 } else {
127 // Listen for ready state to emulate onloadend
128 request.onreadystatechange = function handleLoad() {
129 if (!request || request.readyState !== 4) {
130 return;
131 }
132
133 // The request errored out and we didn't get a response, this will be
134 // handled by onerror instead
135 // With one exception: request that using file: protocol, most browsers
136 // will return status as 0 even though it's a successful request
137 if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
138 return;
139 }
140 // readystate handler is calling before onerror or ontimeout handlers,
141 // so we should call onloadend on the next 'tick'
142 setTimeout(onloadend);
143 };
144 }
145
146 // Handle browser request cancellation (as opposed to a manual cancellation)
147 request.onabort = function handleAbort() {
148 if (!request) {
149 return;
150 }
151
152 reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
153
154 // Clean up request
155 request = null;
156 };
157
158 // Handle low level network errors
159 request.onerror = function handleError() {
160 // Real errors are hidden from us by the browser
161 // onerror should only fire if it's a network error
162 reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request));
163
164 // Clean up request
165 request = null;
166 };
167
168 // Handle timeout
169 request.ontimeout = function handleTimeout() {
170 let timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
171 const transitional = config.transitional || transitionalDefaults;
172 if (config.timeoutErrorMessage) {
173 timeoutErrorMessage = config.timeoutErrorMessage;
174 }
175 reject(new AxiosError(
176 timeoutErrorMessage,
177 transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
178 config,
179 request));
180
181 // Clean up request
182 request = null;
183 };
184
185 // Add xsrf header
186 // This is only done if running in a standard browser environment.
187 // Specifically not if we're in a web worker, or react-native.
188 if(platform.hasStandardBrowserEnv) {
189 withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(config));
190
191 if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(fullPath))) {
192 // Add xsrf header
193 const xsrfValue = config.xsrfHeaderName && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
194
195 if (xsrfValue) {
196 requestHeaders.set(config.xsrfHeaderName, xsrfValue);
197 }
198 }
199 }
200
201 // Remove Content-Type if data is undefined
202 requestData === undefined && requestHeaders.setContentType(null);
203
204 // Add headers to the request
205 if ('setRequestHeader' in request) {
206 utils.forEach(requestHeaders.toJSON(), function setRequestHeader(val, key) {
207 request.setRequestHeader(key, val);
208 });
209 }
210
211 // Add withCredentials to request if needed
212 if (!utils.isUndefined(config.withCredentials)) {
213 request.withCredentials = !!config.withCredentials;
214 }
215
216 // Add responseType to request if needed
217 if (responseType && responseType !== 'json') {
218 request.responseType = config.responseType;
219 }
220
221 // Handle progress if needed
222 if (typeof config.onDownloadProgress === 'function') {
223 request.addEventListener('progress', progressEventReducer(config.onDownloadProgress, true));
224 }
225
226 // Not all browsers support upload events
227 if (typeof config.onUploadProgress === 'function' && request.upload) {
228 request.upload.addEventListener('progress', progressEventReducer(config.onUploadProgress));
229 }
230
231 if (config.cancelToken || config.signal) {
232 // Handle cancellation
233 // eslint-disable-next-line func-names
234 onCanceled = cancel => {
235 if (!request) {
236 return;
237 }
238 reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
239 request.abort();
240 request = null;
241 };
242
243 config.cancelToken && config.cancelToken.subscribe(onCanceled);
244 if (config.signal) {
245 config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
246 }
247 }
248
249 const protocol = parseProtocol(fullPath);
250
251 if (protocol && platform.protocols.indexOf(protocol) === -1) {
252 reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config));
253 return;
254 }
255
256
257 // Send the request
258 request.send(requestData || null);
259 });
260}
Note: See TracBrowser for help on using the repository browser.