diff --git a/packages/middleware-flexible-checksums/src/configuration.ts b/packages/middleware-flexible-checksums/src/configuration.ts index e92871b491b5..d2dbf210766e 100644 --- a/packages/middleware-flexible-checksums/src/configuration.ts +++ b/packages/middleware-flexible-checksums/src/configuration.ts @@ -4,6 +4,7 @@ import { Encoder, GetAwsChunkedEncodingStream, HashConstructor, + Provider, StreamCollector, StreamHasher, } from "@smithy/types"; @@ -31,6 +32,11 @@ export interface PreviouslyResolved { */ md5: ChecksumConstructor | HashConstructor; + /** + * Determines when a checksum will be calculated for request payloads + */ + requestChecksumCalculation: Provider; + /** * A constructor for a class implementing the {@link Hash} interface that computes SHA1 hashes. * @internal diff --git a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.spec.ts b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.spec.ts index 995ae1db5480..bfc5c4085d6a 100644 --- a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.spec.ts +++ b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.spec.ts @@ -2,7 +2,7 @@ import { HttpRequest } from "@smithy/protocol-http"; import { BuildHandlerArguments } from "@smithy/types"; import { PreviouslyResolved } from "./configuration"; -import { ChecksumAlgorithm } from "./constants"; +import { ChecksumAlgorithm, RequestChecksumCalculation } from "./constants"; import { flexibleChecksumsMiddleware } from "./flexibleChecksumsMiddleware"; import { getChecksumAlgorithmForRequest } from "./getChecksumAlgorithmForRequest"; import { getChecksumLocationName } from "./getChecksumLocationName"; @@ -27,7 +27,9 @@ describe(flexibleChecksumsMiddleware.name, () => { const mockChecksumLocationName = "mock-checksum-location-name"; const mockInput = {}; - const mockConfig = {} as PreviouslyResolved; + const mockConfig = { + requestChecksumCalculation: () => Promise.resolve(RequestChecksumCalculation.WHEN_REQUIRED), + } as PreviouslyResolved; const mockMiddlewareConfig = { input: mockInput, requestChecksumRequired: false }; const mockBody = { body: "mockRequestBody" }; diff --git a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts index 5f426f35050c..294b0e7fea8f 100644 --- a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts +++ b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts @@ -58,6 +58,7 @@ export const flexibleChecksumsMiddleware = const { request } = args; const { body: requestBody, headers } = request; const { base64Encoder, streamHasher } = config; + const requestChecksumCalculation = await config.requestChecksumCalculation(); const { input, requestChecksumRequired, requestAlgorithmMember } = middlewareConfig; const checksumAlgorithm = getChecksumAlgorithmForRequest( @@ -65,6 +66,7 @@ export const flexibleChecksumsMiddleware = { requestChecksumRequired, requestAlgorithmMember, + requestChecksumCalculation, }, !!context.isS3ExpressBucket ); diff --git a/packages/middleware-flexible-checksums/src/getChecksumAlgorithmForRequest.spec.ts b/packages/middleware-flexible-checksums/src/getChecksumAlgorithmForRequest.spec.ts index ffefd6efd2b6..5b7998923131 100644 --- a/packages/middleware-flexible-checksums/src/getChecksumAlgorithmForRequest.spec.ts +++ b/packages/middleware-flexible-checksums/src/getChecksumAlgorithmForRequest.spec.ts @@ -1,4 +1,4 @@ -import { ChecksumAlgorithm } from "./constants"; +import { DEFAULT_CHECKSUM_ALGORITHM, RequestChecksumCalculation } from "./constants"; import { getChecksumAlgorithmForRequest } from "./getChecksumAlgorithmForRequest"; import { CLIENT_SUPPORTED_ALGORITHMS } from "./types"; @@ -6,36 +6,88 @@ describe(getChecksumAlgorithmForRequest.name, () => { const mockRequestAlgorithmMember = "mockRequestAlgorithmMember"; describe("when requestAlgorithmMember is not provided", () => { - it("returns MD5 if requestChecksumRequired is set", () => { - expect(getChecksumAlgorithmForRequest({}, { requestChecksumRequired: true })).toEqual(ChecksumAlgorithm.MD5); + describe(`when requestChecksumCalculation is '${RequestChecksumCalculation.WHEN_REQUIRED}'`, () => { + const mockOptions = { requestChecksumCalculation: RequestChecksumCalculation.WHEN_REQUIRED }; + + it(`returns ${DEFAULT_CHECKSUM_ALGORITHM} if requestChecksumRequired is set`, () => { + expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: true })).toEqual( + DEFAULT_CHECKSUM_ALGORITHM + ); + }); + + it("returns undefined if requestChecksumRequired is false", () => { + expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: false })).toBeUndefined(); + }); }); - it("returns undefined if requestChecksumRequired is false", () => { - expect(getChecksumAlgorithmForRequest({}, { requestChecksumRequired: false })).toBeUndefined(); + describe(`when requestChecksumCalculation is '${RequestChecksumCalculation.WHEN_SUPPORTED}'`, () => { + const mockOptions = { requestChecksumCalculation: RequestChecksumCalculation.WHEN_SUPPORTED }; + + it(`returns ${DEFAULT_CHECKSUM_ALGORITHM} if requestChecksumRequired is set`, () => { + expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: true })).toEqual( + DEFAULT_CHECKSUM_ALGORITHM + ); + }); + + it(`returns ${DEFAULT_CHECKSUM_ALGORITHM} if requestChecksumRequired is false`, () => { + expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: false })).toEqual( + DEFAULT_CHECKSUM_ALGORITHM + ); + }); }); }); describe("when requestAlgorithmMember is not set in input", () => { - const mockOptions = { requestAlgorithmMember: mockRequestAlgorithmMember }; + const mockOptionsWithAlgoMember = { requestAlgorithmMember: mockRequestAlgorithmMember }; - it("returns MD5 if requestChecksumRequired is set", () => { - expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: true })).toEqual( - ChecksumAlgorithm.MD5 - ); + describe(`when requestChecksumCalculation is '${RequestChecksumCalculation.WHEN_REQUIRED}'`, () => { + const mockOptions = { + ...mockOptionsWithAlgoMember, + requestChecksumCalculation: RequestChecksumCalculation.WHEN_REQUIRED, + }; + + it(`returns ${DEFAULT_CHECKSUM_ALGORITHM} if requestChecksumRequired is set`, () => { + expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: true })).toEqual( + DEFAULT_CHECKSUM_ALGORITHM + ); + }); + + it("returns undefined if requestChecksumRequired is false", () => { + expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: false })).toBeUndefined(); + }); }); - it("returns undefined if requestChecksumRequired is false", () => { - expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: false })).toBeUndefined(); + describe(`when requestChecksumCalculation is '${RequestChecksumCalculation.WHEN_SUPPORTED}'`, () => { + const mockOptions = { + ...mockOptionsWithAlgoMember, + requestChecksumCalculation: RequestChecksumCalculation.WHEN_SUPPORTED, + }; + + it(`returns ${DEFAULT_CHECKSUM_ALGORITHM} if requestChecksumRequired is set`, () => { + expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: true })).toEqual( + DEFAULT_CHECKSUM_ALGORITHM + ); + }); + + it(`returns ${DEFAULT_CHECKSUM_ALGORITHM} if requestChecksumRequired is false`, () => { + expect(getChecksumAlgorithmForRequest({}, { ...mockOptions, requestChecksumRequired: false })).toEqual( + DEFAULT_CHECKSUM_ALGORITHM + ); + }); }); }); it("throws error if input[requestAlgorithmMember] if not supported by client", () => { const unsupportedAlgo = "unsupportedAlgo"; const mockInput = { [mockRequestAlgorithmMember]: unsupportedAlgo }; - const mockOptions = { requestChecksumRequired: true, requestAlgorithmMember: mockRequestAlgorithmMember }; + const mockOptions = { + requestChecksumRequired: true, + requestAlgorithmMember: mockRequestAlgorithmMember, + requestChecksumCalculation: RequestChecksumCalculation.WHEN_REQUIRED, + }; expect(() => { getChecksumAlgorithmForRequest(mockInput, mockOptions); - }).toThrowError( + }).toThrow( `The checksum algorithm "${unsupportedAlgo}" is not supported by the client.` + ` Select one of ${CLIENT_SUPPORTED_ALGORITHMS}.` ); @@ -44,7 +96,11 @@ describe(getChecksumAlgorithmForRequest.name, () => { describe("returns input[requestAlgorithmMember] if supported by client", () => { it.each(CLIENT_SUPPORTED_ALGORITHMS)("Supported algorithm: %s", (supportedAlgorithm) => { const mockInput = { [mockRequestAlgorithmMember]: supportedAlgorithm }; - const mockOptions = { requestChecksumRequired: true, requestAlgorithmMember: mockRequestAlgorithmMember }; + const mockOptions = { + requestChecksumRequired: true, + requestAlgorithmMember: mockRequestAlgorithmMember, + requestChecksumCalculation: RequestChecksumCalculation.WHEN_REQUIRED, + }; expect(getChecksumAlgorithmForRequest(mockInput, mockOptions)).toEqual(supportedAlgorithm); }); }); diff --git a/packages/middleware-flexible-checksums/src/getChecksumAlgorithmForRequest.ts b/packages/middleware-flexible-checksums/src/getChecksumAlgorithmForRequest.ts index f3cba4b2313f..78f4568a5c0b 100644 --- a/packages/middleware-flexible-checksums/src/getChecksumAlgorithmForRequest.ts +++ b/packages/middleware-flexible-checksums/src/getChecksumAlgorithmForRequest.ts @@ -1,4 +1,9 @@ -import { ChecksumAlgorithm, DEFAULT_CHECKSUM_ALGORITHM, S3_EXPRESS_DEFAULT_CHECKSUM_ALGORITHM } from "./constants"; +import { + ChecksumAlgorithm, + DEFAULT_CHECKSUM_ALGORITHM, + RequestChecksumCalculation, + S3_EXPRESS_DEFAULT_CHECKSUM_ALGORITHM, +} from "./constants"; import { CLIENT_SUPPORTED_ALGORITHMS } from "./types"; export interface GetChecksumAlgorithmForRequestOptions { @@ -11,6 +16,11 @@ export interface GetChecksumAlgorithmForRequestOptions { * Defines a top-level operation input member that is used to configure request checksum behavior. */ requestAlgorithmMember?: string; + + /** + * Determines when a checksum will be calculated for request payloads + */ + requestChecksumCalculation: string; } /** @@ -20,7 +30,11 @@ export interface GetChecksumAlgorithmForRequestOptions { */ export const getChecksumAlgorithmForRequest = ( input: any, - { requestChecksumRequired, requestAlgorithmMember }: GetChecksumAlgorithmForRequestOptions, + { + requestChecksumRequired, + requestAlgorithmMember, + requestChecksumCalculation, + }: GetChecksumAlgorithmForRequestOptions, isS3Express?: boolean ): ChecksumAlgorithm | undefined => { const defaultAlgorithm = isS3Express ? S3_EXPRESS_DEFAULT_CHECKSUM_ALGORITHM : DEFAULT_CHECKSUM_ALGORITHM; @@ -28,8 +42,11 @@ export const getChecksumAlgorithmForRequest = ( // Either the Operation input member that is used to configure request checksum behavior is not set, or // the value for input member to configure flexible checksum is not set. if (!requestAlgorithmMember || !input[requestAlgorithmMember]) { - // Select an algorithm only if request checksum is required. - return requestChecksumRequired ? defaultAlgorithm : undefined; + // Select an algorithm only if request checksum calculation is supported + // or request checksum is required. + return requestChecksumCalculation === RequestChecksumCalculation.WHEN_SUPPORTED || requestChecksumRequired + ? defaultAlgorithm + : undefined; } const checksumAlgorithm = input[requestAlgorithmMember];