UNPKG

amplify-s3-chunk-upload

Version:

A custom storage upload plugin for AWS Amplify. Instead of reading file completely in memory, it helps to read file chunk by chunk.

113 lines (112 loc) 5.12 kB
"use strict"; /* * Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is located at * * http://aws.amazon.com/apache2.0/ * * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions * and limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AxiosHttpHandler = exports.SEND_PROGRESS_EVENT = void 0; const protocol_http_1 = require("@aws-sdk/protocol-http"); const querystring_builder_1 = require("@aws-sdk/querystring-builder"); const axios_1 = require("axios"); const core_1 = require("@aws-amplify/core"); const logger = new core_1.ConsoleLogger('axios-http-handler'); exports.SEND_PROGRESS_EVENT = 'sendProgress'; class AxiosHttpHandler { constructor(httpOptions = {}, emitter) { this.httpOptions = httpOptions; this.emitter = emitter; } destroy() { // Do nothing. TLS and HTTP/2 connection pooling is handled by the // browser. } handle(request, options) { const requestTimeoutInMs = this.httpOptions.requestTimeout; const emitter = this.emitter; let path = request.path; if (request.query) { const queryString = querystring_builder_1.buildQueryString(request.query); if (queryString) { path += `?${queryString}`; } } const port = request.port; const url = `${request.protocol}//${request.hostname}${port ? `:${port}` : ''}${path}`; const axiosRequest = {}; axiosRequest.url = url; axiosRequest.method = request.method; axiosRequest.headers = request.headers; // The host header is automatically added by the browser and adding it explicitly in the // axios request throws an error https://github.com/aws-amplify/amplify-js/issues/5376 // This is because the host header is a forbidden header for the http client to set // see https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name and // https://fetch.spec.whatwg.org/#forbidden-header-name // The reason we are removing this header here instead of in the aws-sdk's client // middleware is that the host header is required to be in the request signature and if // we remove it from the middlewares, then the request fails because the header is added // by the browser but is absent from the signature. delete axiosRequest.headers['host']; if (request.body) { axiosRequest.data = request.body; } else { // Fix for https://github.com/aws-amplify/amplify-js/issues/5432 // If the POST request body is empty but content-type header is set, axios is forcibly removing it // See https://github.com/axios/axios/issues/1535 and refusing to fix it https://github.com/axios/axios/issues/755 // This change is a workaround to set the data as null (instead of undefined) to prevent axios from // removing the content-type header. Link for the source code // https://github.com/axios/axios/blob/dc4bc49673943e35280e5df831f5c3d0347a9393/lib/adapters/xhr.js#L121-L123 if (axiosRequest.headers['Content-Type']) { axiosRequest.data = null; } } if (emitter) { axiosRequest.onUploadProgress = function (event) { emitter.emit(exports.SEND_PROGRESS_EVENT, event); logger.debug(event); }; } // From gamma release, aws-sdk now expects all response type to be of blob or streams axiosRequest.responseType = 'blob'; const raceOfPromises = [ axios_1.default .request(axiosRequest) .then(response => { return { response: new protocol_http_1.HttpResponse({ headers: response.headers, statusCode: response.status, body: response.data, }), }; }) .catch(error => { // Error logger.error(error); throw error; }), requestTimeout(requestTimeoutInMs), ]; return Promise.race(raceOfPromises); } } exports.AxiosHttpHandler = AxiosHttpHandler; function requestTimeout(timeoutInMs = 0) { return new Promise((resolve, reject) => { if (timeoutInMs) { setTimeout(() => { const timeoutError = new Error(`Request did not complete within ${timeoutInMs} ms`); timeoutError.name = 'TimeoutError'; reject(timeoutError); }, timeoutInMs); } }); }