diff --git a/src/internal/client.ts b/src/internal/client.ts new file mode 100644 index 00000000..399758ff --- /dev/null +++ b/src/internal/client.ts @@ -0,0 +1,382 @@ +import * as http from 'node:http' +import * as https from 'node:https' + +import _ from 'lodash' + +import { CredentialProvider } from '../CredentialProvider.ts' +import * as errors from '../errors.ts' +import { + isAmazonEndpoint, + isBoolean, + isDefined, + isEmpty, + isObject, + isString, + isValidEndpoint, + isValidPort, + isVirtualHostStyle, + uriResourceEscape, +} from './helper.ts' +import type { Region } from './s3-endpoints.ts' +import { getS3Endpoint } from './s3-endpoints.ts' +import type { IRequest, RequestHeaders, Transport } from './type.ts' + +// will be replaced by bundler. +const Package = { version: process.env.MINIO_JS_PACKAGE_VERSION || 'development' } + +const requestOptionProperties = [ + 'agent', + 'ca', + 'cert', + 'ciphers', + 'clientCertEngine', + 'crl', + 'dhparam', + 'ecdhCurve', + 'family', + 'honorCipherOrder', + 'key', + 'passphrase', + 'pfx', + 'rejectUnauthorized', + 'secureOptions', + 'secureProtocol', + 'servername', + 'sessionIdContext', +] as const + +export interface ClientOptions { + endPoint: string + accessKey: string + secretKey: string + useSSL?: boolean + port?: number + region?: Region + transport?: Transport + sessionToken?: string + partSize?: number + pathStyle?: boolean + credentialsProvider?: CredentialProvider + s3AccelerateEndpoint?: string + transportAgent?: http.Agent +} + +export type RequestOption = Partial & { + method: string + bucketName?: string + objectName?: string + region?: string + query?: string + pathStyle?: boolean +} + +export class TypedClient { + protected transport: Transport + protected host: string + protected port: number + protected protocol: string + protected accessKey: string + protected secretKey: string + protected sessionToken?: string + protected userAgent: string + protected anonymous: boolean + protected pathStyle: boolean + protected regionMap: Record + public region?: string + protected credentialsProvider?: CredentialProvider + partSize: number = 64 * 1024 * 1024 + protected overRidePartSize?: boolean + + protected maximumPartSize = 5 * 1024 * 1024 * 1024 + protected maxObjectSize = 5 * 1024 * 1024 * 1024 * 1024 + public enableSHA256: boolean + protected s3AccelerateEndpoint?: string + protected reqOptions: Record + + protected transportAgent: http.Agent + + constructor(params: ClientOptions) { + // @ts-expect-error deprecated property + if (params.secure !== undefined) { + throw new Error('"secure" option deprecated, "useSSL" should be used instead') + } + // Default values if not specified. + if (params.useSSL === undefined) { + params.useSSL = true + } + if (!params.port) { + params.port = 0 + } + // Validate input params. + if (!isValidEndpoint(params.endPoint)) { + throw new errors.InvalidEndpointError(`Invalid endPoint : ${params.endPoint}`) + } + if (!isValidPort(params.port)) { + throw new errors.InvalidArgumentError(`Invalid port : ${params.port}`) + } + if (!isBoolean(params.useSSL)) { + throw new errors.InvalidArgumentError( + `Invalid useSSL flag type : ${params.useSSL}, expected to be of type "boolean"`, + ) + } + + // Validate region only if its set. + if (params.region) { + if (!isString(params.region)) { + throw new errors.InvalidArgumentError(`Invalid region : ${params.region}`) + } + } + + const host = params.endPoint.toLowerCase() + let port = params.port + let protocol: string + let transport + let transportAgent: http.Agent + // Validate if configuration is not using SSL + // for constructing relevant endpoints. + if (params.useSSL) { + // Defaults to secure. + transport = https + protocol = 'https:' + port = port || 443 + transportAgent = https.globalAgent + } else { + transport = http + protocol = 'http:' + port = port || 80 + transportAgent = http.globalAgent + } + + // if custom transport is set, use it. + if (params.transport) { + if (!isObject(params.transport)) { + throw new errors.InvalidArgumentError( + `Invalid transport type : ${params.transport}, expected to be type "object"`, + ) + } + transport = params.transport + } + + // if custom transport agent is set, use it. + if (params.transportAgent) { + if (!isObject(params.transportAgent)) { + throw new errors.InvalidArgumentError( + `Invalid transportAgent type: ${params.transportAgent}, expected to be type "object"`, + ) + } + + transportAgent = params.transportAgent + } + + // User Agent should always following the below style. + // Please open an issue to discuss any new changes here. + // + // MinIO (OS; ARCH) LIB/VER APP/VER + // + const libraryComments = `(${process.platform}; ${process.arch})` + const libraryAgent = `MinIO ${libraryComments} minio-js/${Package.version}` + // User agent block ends. + + this.transport = transport + this.transportAgent = transportAgent + this.host = host + this.port = port + this.protocol = protocol + this.userAgent = `${libraryAgent}` + + // Default path style is true + if (params.pathStyle === undefined) { + this.pathStyle = true + } else { + this.pathStyle = params.pathStyle + } + + this.accessKey = params.accessKey ?? '' + this.secretKey = params.secretKey ?? '' + this.sessionToken = params.sessionToken + this.anonymous = !this.accessKey || !this.secretKey + + if (params.credentialsProvider) { + this.credentialsProvider = params.credentialsProvider + } + + this.regionMap = {} + if (params.region) { + this.region = params.region + } + + if (params.partSize) { + this.partSize = params.partSize + this.overRidePartSize = true + } + if (this.partSize < 5 * 1024 * 1024) { + throw new errors.InvalidArgumentError(`Part size should be greater than 5MB`) + } + if (this.partSize > 5 * 1024 * 1024 * 1024) { + throw new errors.InvalidArgumentError(`Part size should be less than 5GB`) + } + + // SHA256 is enabled only for authenticated http requests. If the request is authenticated + // and the connection is https we use x-amz-content-sha256=UNSIGNED-PAYLOAD + // header for signature calculation. + this.enableSHA256 = !this.anonymous && !params.useSSL + + this.s3AccelerateEndpoint = params.s3AccelerateEndpoint || undefined + this.reqOptions = {} + } + + /** + * @param endPoint - valid S3 acceleration end point + */ + setS3TransferAccelerate(endPoint: string) { + this.s3AccelerateEndpoint = endPoint + } + + /** + * Sets the supported request options. + */ + public setRequestOptions(options: Pick) { + if (!isObject(options)) { + throw new TypeError('request options should be of type "object"') + } + this.reqOptions = _.pick(options, requestOptionProperties) + } + + /** + * This is s3 Specific and does not hold validity in any other Object storage. + */ + private getAccelerateEndPointIfSet(bucketName?: string, objectName?: string) { + if (!isEmpty(this.s3AccelerateEndpoint) && !isEmpty(bucketName) && !isEmpty(objectName)) { + // http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html + // Disable transfer acceleration for non-compliant bucket names. + if (bucketName.includes('.')) { + throw new Error(`Transfer Acceleration is not supported for non compliant bucket:${bucketName}`) + } + // If transfer acceleration is requested set new host. + // For more details about enabling transfer acceleration read here. + // http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html + return this.s3AccelerateEndpoint + } + return false + } + + /** + * returns options object that can be used with http.request() + * Takes care of constructing virtual-host-style or path-style hostname + */ + protected getRequestOptions(opts: RequestOption): IRequest & { host: string; headers: Record } { + const method = opts.method + const region = opts.region + const bucketName = opts.bucketName + let objectName = opts.objectName + const headers = opts.headers + const query = opts.query + + let reqOptions = { + method, + headers: {} as RequestHeaders, + protocol: this.protocol, + // If custom transportAgent was supplied earlier, we'll inject it here + agent: this.transportAgent, + } + + // Verify if virtual host supported. + let virtualHostStyle + if (bucketName) { + virtualHostStyle = isVirtualHostStyle(this.host, this.protocol, bucketName, this.pathStyle) + } + + let path = '/' + let host = this.host + + let port: undefined | number + if (this.port) { + port = this.port + } + + if (objectName) { + objectName = uriResourceEscape(objectName) + } + + // For Amazon S3 endpoint, get endpoint based on region. + if (isAmazonEndpoint(host)) { + const accelerateEndPoint = this.getAccelerateEndPointIfSet(bucketName, objectName) + if (accelerateEndPoint) { + host = `${accelerateEndPoint}` + } else { + host = getS3Endpoint(region!) + } + } + + if (virtualHostStyle && !opts.pathStyle) { + // For all hosts which support virtual host style, `bucketName` + // is part of the hostname in the following format: + // + // var host = 'bucketName.example.com' + // + if (bucketName) { + host = `${bucketName}.${host}` + } + if (objectName) { + path = `/${objectName}` + } + } else { + // For all S3 compatible storage services we will fallback to + // path style requests, where `bucketName` is part of the URI + // path. + if (bucketName) { + path = `/${bucketName}` + } + if (objectName) { + path = `/${bucketName}/${objectName}` + } + } + + if (query) { + path += `?${query}` + } + reqOptions.headers.host = host + if ((reqOptions.protocol === 'http:' && port !== 80) || (reqOptions.protocol === 'https:' && port !== 443)) { + reqOptions.headers.host = `${host}:${port}` + } + reqOptions.headers['user-agent'] = this.userAgent + if (headers) { + // have all header keys in lower case - to make signing easy + for (const [k, v] of Object.entries(headers)) { + reqOptions.headers[k.toLowerCase()] = v + } + } + + // Use any request option specified in minioClient.setRequestOptions() + reqOptions = Object.assign({}, this.reqOptions, reqOptions) + + return { + ...reqOptions, + headers: _.mapValues(_.pickBy(reqOptions.headers, isDefined), (v) => v.toString()), + host, + port, + path, + } satisfies https.RequestOptions + } + + public async setCredentialsProvider(credentialsProvider: CredentialProvider) { + if (!(credentialsProvider instanceof CredentialProvider)) { + throw new Error('Unable to get credentials. Expected instance of CredentialProvider') + } + this.credentialsProvider = credentialsProvider + await this.checkAndRefreshCreds() + } + + private async checkAndRefreshCreds() { + if (this.credentialsProvider) { + try { + const credentialsConf = await this.credentialsProvider.getCredentials() + this.accessKey = credentialsConf.getAccessKey() + this.secretKey = credentialsConf.getSecretKey() + this.sessionToken = credentialsConf.getSessionToken() + } catch (e) { + throw new Error(`Unable to get credentials: ${e}`, { cause: e }) + } + } + } +} diff --git a/src/internal/helper.ts b/src/internal/helper.ts index 98c15888..de58c485 100644 --- a/src/internal/helper.ts +++ b/src/internal/helper.ts @@ -271,6 +271,10 @@ export function isEmptyObject(o: Record): boolean { return Object.values(o).filter((x) => x !== undefined).length !== 0 } +export function isDefined(o: T): o is Exclude { + return o !== null && o !== undefined +} + /** * check if arg is a valid date */ diff --git a/src/internal/type.ts b/src/internal/type.ts index a0352d3a..130df6dc 100644 --- a/src/internal/type.ts +++ b/src/internal/type.ts @@ -1,5 +1,4 @@ import type * as http from 'node:http' -import type * as https from 'node:https' export type Binary = string | Buffer @@ -46,7 +45,7 @@ export enum LEGAL_HOLD_STATUS { DISABLED = 'OFF', } -export type Transport = typeof http | typeof https +export type Transport = Pick export interface IRequest { protocol: string diff --git a/src/minio.d.ts b/src/minio.d.ts index 5e966499..7929e027 100644 --- a/src/minio.d.ts +++ b/src/minio.d.ts @@ -1,8 +1,7 @@ // imported from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/93cfb0ec069731dcdfc31464788613f7cddb8192/types/minio/index.d.ts +/* eslint-disable @typescript-eslint/no-explicit-any */ import { EventEmitter } from 'node:events' -import type { Agent as HttpAgent } from 'node:http' -import type { Agent as HttpsAgent, RequestOptions } from 'node:https' import type { Readable as ReadableStream } from 'node:stream' import type { @@ -12,14 +11,15 @@ import type { RETENTION_MODES, RETENTION_VALIDITY_UNITS, } from './helpers.ts' +import type { ClientOptions } from './internal/client.ts' import { CopyConditions } from './internal/copy-conditions.ts' import { PostPolicy } from './internal/post-policy.ts' import type { Region } from './internal/s3-endpoints.ts' -import type { Transport } from './internal/type.ts' export * from './helpers.ts' export type { Region } from './internal/s3-endpoints.ts' export { CopyConditions, PostPolicy } +export type { ClientOptions } // Exports only from typings export type NotificationEvent = @@ -68,20 +68,6 @@ export type Encryption = EncryptionConfig | EmptyObject export type Retention = RetentionOptions | EmptyObject export type IsoDate = string -export interface ClientOptions { - endPoint: string - accessKey: string - secretKey: string - useSSL?: boolean | undefined - port?: number | undefined - region?: Region | undefined - transport?: Transport - sessionToken?: string | undefined - partSize?: number | undefined - pathStyle?: boolean | undefined - transportAgent?: HttpAgent | HttpsAgent -} - export interface BucketItemFromList { name: string creationDate: Date @@ -649,14 +635,9 @@ export class Client { events: NotificationEvent[], ): NotificationPoller - // Custom Settings - setS3TransferAccelerate(endpoint: string): void - // Other newPostPolicy(): PostPolicy - setRequestOptions(options: RequestOptions): void - // Minio extensions that aren't necessary present for Amazon S3 compatible storage servers extensions: { listObjectsV2WithMetadata( diff --git a/src/minio.js b/src/minio.js index eee05e1c..c9a445f3 100644 --- a/src/minio.js +++ b/src/minio.js @@ -15,8 +15,6 @@ */ import * as fs from 'node:fs' -import * as Http from 'node:http' -import * as Https from 'node:https' import * as path from 'node:path' import * as Stream from 'node:stream' @@ -28,10 +26,10 @@ import { TextEncoder } from 'web-encoding' import Xml from 'xml' import xml2js from 'xml2js' -import { CredentialProvider } from './CredentialProvider.ts' import * as errors from './errors.ts' import { extensions } from './extensions.js' import { CopyDestinationOptions, CopySourceOptions, DEFAULT_REGION } from './helpers.ts' +import { TypedClient } from './internal/client.ts' import { CopyConditions } from './internal/copy-conditions.ts' import { calculateEvenSplits, @@ -40,7 +38,6 @@ import { getSourceVersionId, getVersionId, insertContentType, - isAmazonEndpoint, isBoolean, isFunction, isNumber, @@ -49,11 +46,8 @@ import { isString, isValidBucketName, isValidDate, - isValidEndpoint, isValidObjectName, - isValidPort, isValidPrefix, - isVirtualHostStyle, makeDateLong, PART_CONSTRAINTS, partsRequired, @@ -67,7 +61,6 @@ import { uriResourceEscape, } from './internal/helper.ts' import { PostPolicy } from './internal/post-policy.ts' -import { getS3Endpoint } from './internal/s3-endpoints.ts' import { LEGAL_HOLD_STATUS, RETENTION_MODES, RETENTION_VALIDITY_UNITS } from './internal/type.ts' import { NotificationConfig, NotificationPoller } from './notification.js' import { ObjectUploader } from './object-uploader.js' @@ -80,294 +73,7 @@ export * from './helpers.ts' export * from './notification.js' export { CopyConditions, PostPolicy } -// will be replaced by bundler -const Package = { version: process.env.MINIO_JS_PACKAGE_VERSION || 'development' } - -export class Client { - constructor(params) { - if (typeof params.secure !== 'undefined') { - throw new Error('"secure" option deprecated, "useSSL" should be used instead') - } - // Default values if not specified. - if (typeof params.useSSL === 'undefined') { - params.useSSL = true - } - if (!params.port) { - params.port = 0 - } - // Validate input params. - if (!isValidEndpoint(params.endPoint)) { - throw new errors.InvalidEndpointError(`Invalid endPoint : ${params.endPoint}`) - } - if (!isValidPort(params.port)) { - throw new errors.InvalidArgumentError(`Invalid port : ${params.port}`) - } - if (!isBoolean(params.useSSL)) { - throw new errors.InvalidArgumentError( - `Invalid useSSL flag type : ${params.useSSL}, expected to be of type "boolean"`, - ) - } - - // Validate region only if its set. - if (params.region) { - if (!isString(params.region)) { - throw new errors.InvalidArgumentError(`Invalid region : ${params.region}`) - } - } - - var host = params.endPoint.toLowerCase() - var port = params.port - var protocol = '' - var transport - var transportAgent - // Validate if configuration is not using SSL - // for constructing relevant endpoints. - if (params.useSSL === false) { - transport = Http - protocol = 'http:' - if (port === 0) { - port = 80 - } - transportAgent = Http.globalAgent - } else { - // Defaults to secure. - transport = Https - protocol = 'https:' - if (port === 0) { - port = 443 - } - transportAgent = Https.globalAgent - } - - // if custom transport is set, use it. - if (params.transport) { - if (!isObject(params.transport)) { - throw new errors.InvalidArgumentError( - `Invalid transport type : ${params.transport}, expected to be type "object"`, - ) - } - transport = params.transport - } - - // if custom transport agent is set, use it. - if (params.transportAgent) { - if (!isObject(params.transportAgent)) { - throw new errors.InvalidArgumentError( - `Invalid transportAgent type: ${params.transportAgent}, expected to be type "object"`, - ) - } - - transportAgent = params.transportAgent - } - - // User Agent should always following the below style. - // Please open an issue to discuss any new changes here. - // - // MinIO (OS; ARCH) LIB/VER APP/VER - // - var libraryComments = `(${process.platform}; ${process.arch})` - var libraryAgent = `MinIO ${libraryComments} minio-js/${Package.version}` - // User agent block ends. - - this.transport = transport - this.transportAgent = transportAgent - this.host = host - this.port = port - this.protocol = protocol - this.accessKey = params.accessKey - this.secretKey = params.secretKey - this.sessionToken = params.sessionToken - this.userAgent = `${libraryAgent}` - - // Default path style is true - if (params.pathStyle === undefined) { - this.pathStyle = true - } else { - this.pathStyle = params.pathStyle - } - - if (!this.accessKey) { - this.accessKey = '' - } - if (!this.secretKey) { - this.secretKey = '' - } - this.anonymous = !this.accessKey || !this.secretKey - - if (params.credentialsProvider) { - this.credentialsProvider = params.credentialsProvider - this.checkAndRefreshCreds() - } - - this.regionMap = {} - if (params.region) { - this.region = params.region - } - - this.partSize = 64 * 1024 * 1024 - if (params.partSize) { - this.partSize = params.partSize - this.overRidePartSize = true - } - if (this.partSize < 5 * 1024 * 1024) { - throw new errors.InvalidArgumentError(`Part size should be greater than 5MB`) - } - if (this.partSize > 5 * 1024 * 1024 * 1024) { - throw new errors.InvalidArgumentError(`Part size should be less than 5GB`) - } - - this.maximumPartSize = 5 * 1024 * 1024 * 1024 - this.maxObjectSize = 5 * 1024 * 1024 * 1024 * 1024 - // SHA256 is enabled only for authenticated http requests. If the request is authenticated - // and the connection is https we use x-amz-content-sha256=UNSIGNED-PAYLOAD - // header for signature calculation. - this.enableSHA256 = !this.anonymous && !params.useSSL - - this.s3AccelerateEndpoint = params.s3AccelerateEndpoint || null - this.reqOptions = {} - } - - // This is s3 Specific and does not hold validity in any other Object storage. - getAccelerateEndPointIfSet(bucketName, objectName) { - if (!_.isEmpty(this.s3AccelerateEndpoint) && !_.isEmpty(bucketName) && !_.isEmpty(objectName)) { - // http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html - // Disable transfer acceleration for non-compliant bucket names. - if (bucketName.indexOf('.') !== -1) { - throw new Error(`Transfer Acceleration is not supported for non compliant bucket:${bucketName}`) - } - // If transfer acceleration is requested set new host. - // For more details about enabling transfer acceleration read here. - // http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html - return this.s3AccelerateEndpoint - } - return false - } - - /** - * @param endPoint _string_ valid S3 acceleration end point - */ - setS3TransferAccelerate(endPoint) { - this.s3AccelerateEndpoint = endPoint - } - - // Sets the supported request options. - setRequestOptions(options) { - if (!isObject(options)) { - throw new TypeError('request options should be of type "object"') - } - this.reqOptions = _.pick(options, [ - 'agent', - 'ca', - 'cert', - 'ciphers', - 'clientCertEngine', - 'crl', - 'dhparam', - 'ecdhCurve', - 'family', - 'honorCipherOrder', - 'key', - 'passphrase', - 'pfx', - 'rejectUnauthorized', - 'secureOptions', - 'secureProtocol', - 'servername', - 'sessionIdContext', - ]) - } - - // returns *options* object that can be used with http.request() - // Takes care of constructing virtual-host-style or path-style hostname - getRequestOptions(opts) { - var method = opts.method - var region = opts.region - var bucketName = opts.bucketName - var objectName = opts.objectName - var headers = opts.headers - var query = opts.query - - var reqOptions = { method } - reqOptions.headers = {} - - // If custom transportAgent was supplied earlier, we'll inject it here - reqOptions.agent = this.transportAgent - - // Verify if virtual host supported. - var virtualHostStyle - if (bucketName) { - virtualHostStyle = isVirtualHostStyle(this.host, this.protocol, bucketName, this.pathStyle) - } - - if (this.port) { - reqOptions.port = this.port - } - reqOptions.protocol = this.protocol - - if (objectName) { - objectName = `${uriResourceEscape(objectName)}` - } - - reqOptions.path = '/' - - // Save host. - reqOptions.host = this.host - // For Amazon S3 endpoint, get endpoint based on region. - if (isAmazonEndpoint(reqOptions.host)) { - const accelerateEndPoint = this.getAccelerateEndPointIfSet(bucketName, objectName) - if (accelerateEndPoint) { - reqOptions.host = `${accelerateEndPoint}` - } else { - reqOptions.host = getS3Endpoint(region) - } - } - - if (virtualHostStyle && !opts.pathStyle) { - // For all hosts which support virtual host style, `bucketName` - // is part of the hostname in the following format: - // - // var host = 'bucketName.example.com' - // - if (bucketName) { - reqOptions.host = `${bucketName}.${reqOptions.host}` - } - if (objectName) { - reqOptions.path = `/${objectName}` - } - } else { - // For all S3 compatible storage services we will fallback to - // path style requests, where `bucketName` is part of the URI - // path. - if (bucketName) { - reqOptions.path = `/${bucketName}` - } - if (objectName) { - reqOptions.path = `/${bucketName}/${objectName}` - } - } - - if (query) { - reqOptions.path += `?${query}` - } - reqOptions.headers.host = reqOptions.host - if ( - (reqOptions.protocol === 'http:' && reqOptions.port !== 80) || - (reqOptions.protocol === 'https:' && reqOptions.port !== 443) - ) { - reqOptions.headers.host = `${reqOptions.host}:${reqOptions.port}` - } - reqOptions.headers['user-agent'] = this.userAgent - if (headers) { - // have all header keys in lower case - to make signing easy - _.map(headers, (v, k) => (reqOptions.headers[k.toLowerCase()] = v)) - } - - // Use any request option specified in minioClient.setRequestOptions() - reqOptions = Object.assign({}, this.reqOptions, reqOptions) - - return reqOptions - } - +export class Client extends TypedClient { // Set application specific information. // // Generates User-Agent in the following style. @@ -3454,34 +3160,6 @@ export class Client { this.makeRequest({ method, bucketName, objectName, query, headers }, payload, [200], '', false, cb) } - async setCredentialsProvider(credentialsProvider) { - if (!(credentialsProvider instanceof CredentialProvider)) { - throw new Error('Unable to get credentials. Expected instance of CredentialProvider') - } - this.credentialsProvider = credentialsProvider - await this.checkAndRefreshCreds() - } - - async checkAndRefreshCreds() { - if (this.credentialsProvider) { - return await this.fetchCredentials() - } - } - - async fetchCredentials() { - if (this.credentialsProvider) { - const credentialsConf = await this.credentialsProvider.getCredentials() - if (credentialsConf) { - this.accessKey = credentialsConf.getAccessKey() - this.secretKey = credentialsConf.getSecretKey() - this.sessionToken = credentialsConf.getSessionToken() - } else { - throw new Error('Unable to get credentials. Expected instance of BaseCredentialsProvider') - } - } else { - throw new Error('Unable to get credentials. Expected instance of BaseCredentialsProvider') - } - } /** * Internal Method to abort a multipart upload request in case of any errors.