1 | import utils from './../utils.js';
|
---|
2 | import settle from './../core/settle.js';
|
---|
3 | import transitionalDefaults from '../defaults/transitional.js';
|
---|
4 | import AxiosError from '../core/AxiosError.js';
|
---|
5 | import CanceledError from '../cancel/CanceledError.js';
|
---|
6 | import parseProtocol from '../helpers/parseProtocol.js';
|
---|
7 | import platform from '../platform/index.js';
|
---|
8 | import AxiosHeaders from '../core/AxiosHeaders.js';
|
---|
9 | import {progressEventReducer} from '../helpers/progressEventReducer.js';
|
---|
10 | import resolveConfig from "../helpers/resolveConfig.js";
|
---|
11 |
|
---|
12 | const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
|
---|
13 |
|
---|
14 | export default isXHRAdapterSupported && function (config) {
|
---|
15 | return new Promise(function dispatchXhrRequest(resolve, reject) {
|
---|
16 | const _config = resolveConfig(config);
|
---|
17 | let requestData = _config.data;
|
---|
18 | const requestHeaders = AxiosHeaders.from(_config.headers).normalize();
|
---|
19 | let {responseType, onUploadProgress, onDownloadProgress} = _config;
|
---|
20 | let onCanceled;
|
---|
21 | let uploadThrottled, downloadThrottled;
|
---|
22 | let flushUpload, flushDownload;
|
---|
23 |
|
---|
24 | function done() {
|
---|
25 | flushUpload && flushUpload(); // flush events
|
---|
26 | flushDownload && flushDownload(); // flush events
|
---|
27 |
|
---|
28 | _config.cancelToken && _config.cancelToken.unsubscribe(onCanceled);
|
---|
29 |
|
---|
30 | _config.signal && _config.signal.removeEventListener('abort', onCanceled);
|
---|
31 | }
|
---|
32 |
|
---|
33 | let request = new XMLHttpRequest();
|
---|
34 |
|
---|
35 | request.open(_config.method.toUpperCase(), _config.url, true);
|
---|
36 |
|
---|
37 | // Set the request timeout in MS
|
---|
38 | request.timeout = _config.timeout;
|
---|
39 |
|
---|
40 | function onloadend() {
|
---|
41 | if (!request) {
|
---|
42 | return;
|
---|
43 | }
|
---|
44 | // Prepare the response
|
---|
45 | const responseHeaders = AxiosHeaders.from(
|
---|
46 | 'getAllResponseHeaders' in request && request.getAllResponseHeaders()
|
---|
47 | );
|
---|
48 | const responseData = !responseType || responseType === 'text' || responseType === 'json' ?
|
---|
49 | request.responseText : request.response;
|
---|
50 | const response = {
|
---|
51 | data: responseData,
|
---|
52 | status: request.status,
|
---|
53 | statusText: request.statusText,
|
---|
54 | headers: responseHeaders,
|
---|
55 | config,
|
---|
56 | request
|
---|
57 | };
|
---|
58 |
|
---|
59 | settle(function _resolve(value) {
|
---|
60 | resolve(value);
|
---|
61 | done();
|
---|
62 | }, function _reject(err) {
|
---|
63 | reject(err);
|
---|
64 | done();
|
---|
65 | }, response);
|
---|
66 |
|
---|
67 | // Clean up request
|
---|
68 | request = null;
|
---|
69 | }
|
---|
70 |
|
---|
71 | if ('onloadend' in request) {
|
---|
72 | // Use onloadend if available
|
---|
73 | request.onloadend = onloadend;
|
---|
74 | } else {
|
---|
75 | // Listen for ready state to emulate onloadend
|
---|
76 | request.onreadystatechange = function handleLoad() {
|
---|
77 | if (!request || request.readyState !== 4) {
|
---|
78 | return;
|
---|
79 | }
|
---|
80 |
|
---|
81 | // The request errored out and we didn't get a response, this will be
|
---|
82 | // handled by onerror instead
|
---|
83 | // With one exception: request that using file: protocol, most browsers
|
---|
84 | // will return status as 0 even though it's a successful request
|
---|
85 | if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
|
---|
86 | return;
|
---|
87 | }
|
---|
88 | // readystate handler is calling before onerror or ontimeout handlers,
|
---|
89 | // so we should call onloadend on the next 'tick'
|
---|
90 | setTimeout(onloadend);
|
---|
91 | };
|
---|
92 | }
|
---|
93 |
|
---|
94 | // Handle browser request cancellation (as opposed to a manual cancellation)
|
---|
95 | request.onabort = function handleAbort() {
|
---|
96 | if (!request) {
|
---|
97 | return;
|
---|
98 | }
|
---|
99 |
|
---|
100 | reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
|
---|
101 |
|
---|
102 | // Clean up request
|
---|
103 | request = null;
|
---|
104 | };
|
---|
105 |
|
---|
106 | // Handle low level network errors
|
---|
107 | request.onerror = function handleError() {
|
---|
108 | // Real errors are hidden from us by the browser
|
---|
109 | // onerror should only fire if it's a network error
|
---|
110 | reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request));
|
---|
111 |
|
---|
112 | // Clean up request
|
---|
113 | request = null;
|
---|
114 | };
|
---|
115 |
|
---|
116 | // Handle timeout
|
---|
117 | request.ontimeout = function handleTimeout() {
|
---|
118 | let timeoutErrorMessage = _config.timeout ? 'timeout of ' + _config.timeout + 'ms exceeded' : 'timeout exceeded';
|
---|
119 | const transitional = _config.transitional || transitionalDefaults;
|
---|
120 | if (_config.timeoutErrorMessage) {
|
---|
121 | timeoutErrorMessage = _config.timeoutErrorMessage;
|
---|
122 | }
|
---|
123 | reject(new AxiosError(
|
---|
124 | timeoutErrorMessage,
|
---|
125 | transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
|
---|
126 | config,
|
---|
127 | request));
|
---|
128 |
|
---|
129 | // Clean up request
|
---|
130 | request = null;
|
---|
131 | };
|
---|
132 |
|
---|
133 | // Remove Content-Type if data is undefined
|
---|
134 | requestData === undefined && requestHeaders.setContentType(null);
|
---|
135 |
|
---|
136 | // Add headers to the request
|
---|
137 | if ('setRequestHeader' in request) {
|
---|
138 | utils.forEach(requestHeaders.toJSON(), function setRequestHeader(val, key) {
|
---|
139 | request.setRequestHeader(key, val);
|
---|
140 | });
|
---|
141 | }
|
---|
142 |
|
---|
143 | // Add withCredentials to request if needed
|
---|
144 | if (!utils.isUndefined(_config.withCredentials)) {
|
---|
145 | request.withCredentials = !!_config.withCredentials;
|
---|
146 | }
|
---|
147 |
|
---|
148 | // Add responseType to request if needed
|
---|
149 | if (responseType && responseType !== 'json') {
|
---|
150 | request.responseType = _config.responseType;
|
---|
151 | }
|
---|
152 |
|
---|
153 | // Handle progress if needed
|
---|
154 | if (onDownloadProgress) {
|
---|
155 | ([downloadThrottled, flushDownload] = progressEventReducer(onDownloadProgress, true));
|
---|
156 | request.addEventListener('progress', downloadThrottled);
|
---|
157 | }
|
---|
158 |
|
---|
159 | // Not all browsers support upload events
|
---|
160 | if (onUploadProgress && request.upload) {
|
---|
161 | ([uploadThrottled, flushUpload] = progressEventReducer(onUploadProgress));
|
---|
162 |
|
---|
163 | request.upload.addEventListener('progress', uploadThrottled);
|
---|
164 |
|
---|
165 | request.upload.addEventListener('loadend', flushUpload);
|
---|
166 | }
|
---|
167 |
|
---|
168 | if (_config.cancelToken || _config.signal) {
|
---|
169 | // Handle cancellation
|
---|
170 | // eslint-disable-next-line func-names
|
---|
171 | onCanceled = cancel => {
|
---|
172 | if (!request) {
|
---|
173 | return;
|
---|
174 | }
|
---|
175 | reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
|
---|
176 | request.abort();
|
---|
177 | request = null;
|
---|
178 | };
|
---|
179 |
|
---|
180 | _config.cancelToken && _config.cancelToken.subscribe(onCanceled);
|
---|
181 | if (_config.signal) {
|
---|
182 | _config.signal.aborted ? onCanceled() : _config.signal.addEventListener('abort', onCanceled);
|
---|
183 | }
|
---|
184 | }
|
---|
185 |
|
---|
186 | const protocol = parseProtocol(_config.url);
|
---|
187 |
|
---|
188 | if (protocol && platform.protocols.indexOf(protocol) === -1) {
|
---|
189 | reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config));
|
---|
190 | return;
|
---|
191 | }
|
---|
192 |
|
---|
193 |
|
---|
194 | // Send the request
|
---|
195 | request.send(requestData || null);
|
---|
196 | });
|
---|
197 | }
|
---|