mirror of
https://github.com/tenrok/axios.git
synced 2026-06-17 19:21:29 +03:00
fix(adapter): fix progress event emitting; (#6518)
This commit is contained in:
+29
-27
@@ -4,19 +4,10 @@ import AxiosError from "../core/AxiosError.js";
|
||||
import composeSignals from "../helpers/composeSignals.js";
|
||||
import {trackStream} from "../helpers/trackStream.js";
|
||||
import AxiosHeaders from "../core/AxiosHeaders.js";
|
||||
import progressEventReducer from "../helpers/progressEventReducer.js";
|
||||
import {progressEventReducer, progressEventDecorator, asyncDecorator} from "../helpers/progressEventReducer.js";
|
||||
import resolveConfig from "../helpers/resolveConfig.js";
|
||||
import settle from "../core/settle.js";
|
||||
|
||||
const fetchProgressDecorator = (total, fn) => {
|
||||
const lengthComputable = total != null;
|
||||
return (loaded) => setTimeout(() => fn({
|
||||
lengthComputable,
|
||||
total,
|
||||
loaded
|
||||
}));
|
||||
}
|
||||
|
||||
const isFetchSupported = typeof fetch === 'function' && typeof Request === 'function' && typeof Response === 'function';
|
||||
const isReadableStreamSupported = isFetchSupported && typeof ReadableStream === 'function';
|
||||
|
||||
@@ -26,7 +17,15 @@ const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ?
|
||||
async (str) => new Uint8Array(await new Response(str).arrayBuffer())
|
||||
);
|
||||
|
||||
const supportsRequestStream = isReadableStreamSupported && (() => {
|
||||
const test = (fn, ...args) => {
|
||||
try {
|
||||
return !!fn(...args);
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const supportsRequestStream = isReadableStreamSupported && test(() => {
|
||||
let duplexAccessed = false;
|
||||
|
||||
const hasContentType = new Request(platform.origin, {
|
||||
@@ -39,17 +38,13 @@ const supportsRequestStream = isReadableStreamSupported && (() => {
|
||||
}).headers.has('Content-Type');
|
||||
|
||||
return duplexAccessed && !hasContentType;
|
||||
})();
|
||||
});
|
||||
|
||||
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||
|
||||
const supportsResponseStream = isReadableStreamSupported && !!(()=> {
|
||||
try {
|
||||
return utils.isReadableStream(new Response('').body);
|
||||
} catch(err) {
|
||||
// return undefined
|
||||
}
|
||||
})();
|
||||
const supportsResponseStream = isReadableStreamSupported &&
|
||||
test(() => utils.isReadableStream(new Response('').body));
|
||||
|
||||
|
||||
const resolvers = {
|
||||
stream: supportsResponseStream && ((res) => res.body)
|
||||
@@ -77,7 +72,7 @@ const getBodyLength = async (body) => {
|
||||
return (await new Request(body).arrayBuffer()).byteLength;
|
||||
}
|
||||
|
||||
if(utils.isArrayBufferView(body)) {
|
||||
if(utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
|
||||
return body.byteLength;
|
||||
}
|
||||
|
||||
@@ -147,10 +142,12 @@ export default isFetchSupported && (async (config) => {
|
||||
}
|
||||
|
||||
if (_request.body) {
|
||||
data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, fetchProgressDecorator(
|
||||
const [onProgress, flush] = progressEventDecorator(
|
||||
requestContentLength,
|
||||
progressEventReducer(onUploadProgress)
|
||||
), null, encodeText);
|
||||
progressEventReducer(asyncDecorator(onUploadProgress))
|
||||
);
|
||||
|
||||
data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush, encodeText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,11 +178,16 @@ export default isFetchSupported && (async (config) => {
|
||||
|
||||
const responseContentLength = utils.toFiniteNumber(response.headers.get('content-length'));
|
||||
|
||||
const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
|
||||
responseContentLength,
|
||||
progressEventReducer(asyncDecorator(onDownloadProgress), true)
|
||||
) || [];
|
||||
|
||||
response = new Response(
|
||||
trackStream(response.body, DEFAULT_CHUNK_SIZE, onDownloadProgress && fetchProgressDecorator(
|
||||
responseContentLength,
|
||||
progressEventReducer(onDownloadProgress, true)
|
||||
), isStreamResponse && onFinish, encodeText),
|
||||
trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
|
||||
flush && flush();
|
||||
isStreamResponse && onFinish();
|
||||
}, encodeText),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
+25
-15
@@ -24,6 +24,7 @@ import formDataToStream from "../helpers/formDataToStream.js";
|
||||
import readBlob from "../helpers/readBlob.js";
|
||||
import ZlibHeaderTransformStream from '../helpers/ZlibHeaderTransformStream.js';
|
||||
import callbackify from "../helpers/callbackify.js";
|
||||
import {progressEventReducer, progressEventDecorator, asyncDecorator} from "../helpers/progressEventReducer.js";
|
||||
|
||||
const zlibOptions = {
|
||||
flush: zlib.constants.Z_SYNC_FLUSH,
|
||||
@@ -45,6 +46,14 @@ const supportedProtocols = platform.protocols.map(protocol => {
|
||||
return protocol + ':';
|
||||
});
|
||||
|
||||
const flushOnFinish = (stream, [throttled, flush]) => {
|
||||
stream
|
||||
.on('end', flush)
|
||||
.on('error', flush);
|
||||
|
||||
return throttled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the proxy or config beforeRedirects functions are defined, call them with the options
|
||||
* object.
|
||||
@@ -278,8 +287,7 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
||||
// Only set header if it hasn't been set in config
|
||||
headers.set('User-Agent', 'axios/' + VERSION, false);
|
||||
|
||||
const onDownloadProgress = config.onDownloadProgress;
|
||||
const onUploadProgress = config.onUploadProgress;
|
||||
const {onUploadProgress, onDownloadProgress} = config;
|
||||
const maxRate = config.maxRate;
|
||||
let maxUploadRate = undefined;
|
||||
let maxDownloadRate = undefined;
|
||||
@@ -352,15 +360,16 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
||||
}
|
||||
|
||||
data = stream.pipeline([data, new AxiosTransformStream({
|
||||
length: contentLength,
|
||||
maxRate: utils.toFiniteNumber(maxUploadRate)
|
||||
})], utils.noop);
|
||||
|
||||
onUploadProgress && data.on('progress', progress => {
|
||||
onUploadProgress(Object.assign(progress, {
|
||||
upload: true
|
||||
}));
|
||||
});
|
||||
onUploadProgress && data.on('progress', flushOnFinish(
|
||||
data,
|
||||
progressEventDecorator(
|
||||
contentLength,
|
||||
progressEventReducer(asyncDecorator(onUploadProgress), false, 3)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
// HTTP basic authentication
|
||||
@@ -459,17 +468,18 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
||||
|
||||
const responseLength = +res.headers['content-length'];
|
||||
|
||||
if (onDownloadProgress) {
|
||||
if (onDownloadProgress || maxDownloadRate) {
|
||||
const transformStream = new AxiosTransformStream({
|
||||
length: utils.toFiniteNumber(responseLength),
|
||||
maxRate: utils.toFiniteNumber(maxDownloadRate)
|
||||
});
|
||||
|
||||
onDownloadProgress && transformStream.on('progress', progress => {
|
||||
onDownloadProgress(Object.assign(progress, {
|
||||
download: true
|
||||
}));
|
||||
});
|
||||
onDownloadProgress && transformStream.on('progress', flushOnFinish(
|
||||
transformStream,
|
||||
progressEventDecorator(
|
||||
responseLength,
|
||||
progressEventReducer(asyncDecorator(onDownloadProgress), true, 3)
|
||||
)
|
||||
));
|
||||
|
||||
streams.push(transformStream);
|
||||
}
|
||||
|
||||
+20
-13
@@ -6,7 +6,7 @@ import CanceledError from '../cancel/CanceledError.js';
|
||||
import parseProtocol from '../helpers/parseProtocol.js';
|
||||
import platform from '../platform/index.js';
|
||||
import AxiosHeaders from '../core/AxiosHeaders.js';
|
||||
import progressEventReducer from '../helpers/progressEventReducer.js';
|
||||
import {progressEventReducer} from '../helpers/progressEventReducer.js';
|
||||
import resolveConfig from "../helpers/resolveConfig.js";
|
||||
|
||||
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
|
||||
@@ -16,16 +16,18 @@ export default isXHRAdapterSupported && function (config) {
|
||||
const _config = resolveConfig(config);
|
||||
let requestData = _config.data;
|
||||
const requestHeaders = AxiosHeaders.from(_config.headers).normalize();
|
||||
let {responseType} = _config;
|
||||
let {responseType, onUploadProgress, onDownloadProgress} = _config;
|
||||
let onCanceled;
|
||||
function done() {
|
||||
if (_config.cancelToken) {
|
||||
_config.cancelToken.unsubscribe(onCanceled);
|
||||
}
|
||||
let uploadThrottled, downloadThrottled;
|
||||
let flushUpload, flushDownload;
|
||||
|
||||
if (_config.signal) {
|
||||
_config.signal.removeEventListener('abort', onCanceled);
|
||||
}
|
||||
function done() {
|
||||
flushUpload && flushUpload(); // flush events
|
||||
flushDownload && flushDownload(); // flush events
|
||||
|
||||
_config.cancelToken && _config.cancelToken.unsubscribe(onCanceled);
|
||||
|
||||
_config.signal && _config.signal.removeEventListener('abort', onCanceled);
|
||||
}
|
||||
|
||||
let request = new XMLHttpRequest();
|
||||
@@ -149,13 +151,18 @@ export default isXHRAdapterSupported && function (config) {
|
||||
}
|
||||
|
||||
// Handle progress if needed
|
||||
if (typeof _config.onDownloadProgress === 'function') {
|
||||
request.addEventListener('progress', progressEventReducer(_config.onDownloadProgress, true));
|
||||
if (onDownloadProgress) {
|
||||
([downloadThrottled, flushDownload] = progressEventReducer(onDownloadProgress, true));
|
||||
request.addEventListener('progress', downloadThrottled);
|
||||
}
|
||||
|
||||
// Not all browsers support upload events
|
||||
if (typeof _config.onUploadProgress === 'function' && request.upload) {
|
||||
request.upload.addEventListener('progress', progressEventReducer(_config.onUploadProgress));
|
||||
if (onUploadProgress && request.upload) {
|
||||
([uploadThrottled, flushUpload] = progressEventReducer(onUploadProgress));
|
||||
|
||||
request.upload.addEventListener('progress', uploadThrottled);
|
||||
|
||||
request.upload.addEventListener('loadend', flushUpload);
|
||||
}
|
||||
|
||||
if (_config.cancelToken || _config.signal) {
|
||||
|
||||
Reference in New Issue
Block a user