{"version":3,"file":"xhrTransferHandler.mjs","sources":["../../../../../../../src/providers/s3/utils/client/runtime/xhrTransferHandler.ts"],"sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nimport { withMemoization, } from '@aws-amplify/core/internals/aws-client-utils';\nimport { ConsoleLogger } from '@aws-amplify/core';\nimport { CanceledError } from '../../../../../errors/CanceledError';\nimport { StorageError } from '../../../../../errors/StorageError';\nimport { ABORT_ERROR_CODE, ABORT_ERROR_MESSAGE, CANCELED_ERROR_CODE, CANCELED_ERROR_MESSAGE, NETWORK_ERROR_CODE, NETWORK_ERROR_MESSAGE, } from './constants';\nconst logger = new ConsoleLogger('xhr-http-handler');\n/**\n * Base transfer handler implementation using XMLHttpRequest to support upload and download progress events.\n *\n * @param request - The request object.\n * @param options - The request options.\n * @returns A promise that will be resolved with the response object.\n *\n * @internal\n */\nexport const xhrTransferHandler = (request, options) => {\n    const { url, method, headers, body } = request;\n    const { onDownloadProgress, onUploadProgress, responseType, abortSignal } = options;\n    return new Promise((resolve, reject) => {\n        let xhr = new XMLHttpRequest();\n        xhr.open(method.toUpperCase(), url.toString());\n        Object.entries(headers)\n            .filter(([header]) => !FORBIDDEN_HEADERS.includes(header))\n            .forEach(([header, value]) => {\n            xhr.setRequestHeader(header, value);\n        });\n        xhr.responseType = responseType;\n        if (onDownloadProgress) {\n            xhr.addEventListener('progress', event => {\n                onDownloadProgress(convertToTransferProgressEvent(event));\n                logger.debug(event);\n            });\n        }\n        if (onUploadProgress) {\n            xhr.upload.addEventListener('progress', event => {\n                onUploadProgress(convertToTransferProgressEvent(event));\n                logger.debug(event);\n            });\n        }\n        xhr.addEventListener('error', () => {\n            const networkError = new StorageError({\n                message: NETWORK_ERROR_MESSAGE,\n                name: NETWORK_ERROR_CODE,\n            });\n            logger.error(NETWORK_ERROR_MESSAGE);\n            reject(networkError);\n            xhr = null; // clean up request\n        });\n        // Handle browser request cancellation (as opposed to a manual cancellation)\n        xhr.addEventListener('abort', () => {\n            // The abort event can be triggered after the error or load event. So we need to check if the xhr is null.\n            // When request is aborted by AbortSignal, the promise is rejected in the abortSignal's 'abort' event listener.\n            if (!xhr || abortSignal?.aborted)\n                return;\n            // Handle abort request caused by browser instead of AbortController\n            // see: https://github.com/axios/axios/issues/537\n            const error = buildHandlerError(ABORT_ERROR_MESSAGE, ABORT_ERROR_CODE);\n            logger.error(ABORT_ERROR_MESSAGE);\n            reject(error);\n            xhr = null; // clean up request\n        });\n        // Skip handling timeout error since we don't have a timeout\n        xhr.addEventListener('readystatechange', () => {\n            if (!xhr || xhr.readyState !== xhr.DONE) {\n                return;\n            }\n            const onloadend = () => {\n                // The load event is triggered after the error/abort/load event. So we need to check if the xhr is null.\n                if (!xhr)\n                    return;\n                const responseHeaders = convertResponseHeaders(xhr.getAllResponseHeaders());\n                const { responseType: loadEndResponseType } = xhr;\n                const responseBlob = xhr.response;\n                const responseText = loadEndResponseType === 'text' ? xhr.responseText : '';\n                const bodyMixIn = {\n                    blob: () => Promise.resolve(responseBlob),\n                    text: withMemoization(() => loadEndResponseType === 'blob'\n                        ? readBlobAsText(responseBlob)\n                        : Promise.resolve(responseText)),\n                    json: () => Promise.reject(\n                    // S3 does not support JSON response. So fail-fast here with nicer error message.\n                    new Error('Parsing response to JSON is not implemented. Please use response.text() instead.')),\n                };\n                const response = {\n                    statusCode: xhr.status,\n                    headers: responseHeaders,\n                    // The xhr.responseType is only set to 'blob' for streaming binary S3 object data. The streaming data is\n                    // exposed via public interface of Storage.get(). So we need to return the response as a Blob object for\n                    // backward compatibility. In other cases, the response payload is only used internally, we return it is\n                    // {@link ResponseBodyMixin}\n                    body: (xhr.responseType === 'blob'\n                        ? Object.assign(responseBlob, bodyMixIn)\n                        : bodyMixIn),\n                };\n                resolve(response);\n                xhr = null; // clean up request\n            };\n            // readystate handler is calling before onerror or ontimeout handlers,\n            // so we should call onloadend on the next 'tick'\n            // @see https://github.com/axios/axios/blob/9588fcdec8aca45c3ba2f7968988a5d03f23168c/lib/adapters/xhr.js#L98-L99\n            setTimeout(onloadend);\n        });\n        if (abortSignal) {\n            const onCanceled = () => {\n                // The abort event is triggered after the error or load event. So we need to check if the xhr is null.\n                if (!xhr) {\n                    return;\n                }\n                const canceledError = new CanceledError({\n                    name: CANCELED_ERROR_CODE,\n                    message: CANCELED_ERROR_MESSAGE,\n                });\n                reject(canceledError);\n                xhr.abort();\n                xhr = null;\n            };\n            abortSignal.aborted\n                ? onCanceled()\n                : abortSignal.addEventListener('abort', onCanceled);\n        }\n        if (typeof ReadableStream === 'function' &&\n            body instanceof ReadableStream) {\n            // This does not matter as previous implementation uses Axios which does not support ReadableStream anyway.\n            throw new Error('ReadableStream request payload is not supported.');\n        }\n        xhr.send(body ?? null);\n    });\n};\nconst convertToTransferProgressEvent = (event) => ({\n    // `loaded` can exceed the `total` in some cases due to platform issues/bugs e.g. React Native & Expo Android\n    // clamp `loaded` values down to `total` if possible.\n    transferredBytes: event.lengthComputable\n        ? Math.min(event.loaded, event.total)\n        : event.loaded,\n    totalBytes: event.lengthComputable ? event.total : undefined,\n});\nconst buildHandlerError = (message, name) => {\n    const error = new Error(message);\n    error.name = name;\n    return error;\n};\n/**\n * Convert xhr.getAllResponseHeaders() string to a Record<string, string>. Note that modern browser already returns\n * header names in lowercase.\n * @param xhrHeaders - string of headers returned from xhr.getAllResponseHeaders()\n */\nconst convertResponseHeaders = (xhrHeaders) => {\n    if (!xhrHeaders) {\n        return {};\n    }\n    return xhrHeaders\n        .split('\\r\\n')\n        .reduce((headerMap, line) => {\n        const parts = line.split(': ');\n        const header = parts.shift();\n        const value = parts.join(': ');\n        headerMap[header.toLowerCase()] = value;\n        return headerMap;\n    }, {});\n};\nconst readBlobAsText = (blob) => {\n    const reader = new FileReader();\n    return new Promise((resolve, reject) => {\n        reader.onloadend = () => {\n            if (reader.readyState !== FileReader.DONE) {\n                return;\n            }\n            resolve(reader.result);\n        };\n        reader.onerror = () => {\n            reject(reader.error);\n        };\n        reader.readAsText(blob);\n    });\n};\n// To add more forbidden headers as found set by S3. Intentionally NOT list all of them here to save bundle size.\n// https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name\nconst FORBIDDEN_HEADERS = ['host'];\n"],"names":[],"mappings":"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"}