diff --git a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.spec.ts b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.spec.ts index 9d284db11a2a..3d173e584117 100644 --- a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.spec.ts +++ b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.spec.ts @@ -8,6 +8,7 @@ import { flexibleChecksumsMiddleware } from "./flexibleChecksumsMiddleware"; import { getChecksumAlgorithmForRequest } from "./getChecksumAlgorithmForRequest"; import { getChecksumLocationName } from "./getChecksumLocationName"; import { hasHeader } from "./hasHeader"; +import { hasHeaderWithPrefix } from "./hasHeaderWithPrefix"; import { isStreaming } from "./isStreaming"; import { selectChecksumAlgorithmFunction } from "./selectChecksumAlgorithmFunction"; import { stringHasher } from "./stringHasher"; @@ -16,6 +17,7 @@ vi.mock("@smithy/protocol-http"); vi.mock("./getChecksumAlgorithmForRequest"); vi.mock("./getChecksumLocationName"); vi.mock("./hasHeader"); +vi.mock("./hasHeaderWithPrefix"); vi.mock("./isStreaming"); vi.mock("./selectChecksumAlgorithmFunction"); vi.mock("./stringHasher"); @@ -44,6 +46,7 @@ describe(flexibleChecksumsMiddleware.name, () => { vi.mocked(getChecksumAlgorithmForRequest).mockReturnValue(ChecksumAlgorithm.MD5); vi.mocked(getChecksumLocationName).mockReturnValue(mockChecksumLocationName); vi.mocked(hasHeader).mockReturnValue(true); + vi.mocked(hasHeaderWithPrefix).mockReturnValue(false); vi.mocked(isStreaming).mockReturnValue(false); vi.mocked(selectChecksumAlgorithmFunction).mockReturnValue(mockChecksumAlgorithmFunction); }); @@ -63,31 +66,28 @@ describe(flexibleChecksumsMiddleware.name, () => { }); describe("request checksum", () => { - afterEach(() => { - expect(getChecksumAlgorithmForRequest).toHaveBeenCalledTimes(1); - }); - it("if checksumAlgorithm is not defined", async () => { vi.mocked(getChecksumAlgorithmForRequest).mockReturnValue(undefined); const handler = flexibleChecksumsMiddleware(mockConfig, mockMiddlewareConfig)(mockNext, {}); await handler(mockArgs); + expect(hasHeaderWithPrefix).toHaveBeenCalledTimes(1); expect(getChecksumLocationName).not.toHaveBeenCalled(); expect(mockNext).toHaveBeenCalledWith(mockArgs); expect(selectChecksumAlgorithmFunction).not.toHaveBeenCalled(); + expect(getChecksumAlgorithmForRequest).toHaveBeenCalledTimes(1); }); it("if header is already present", async () => { const handler = flexibleChecksumsMiddleware(mockConfig, mockMiddlewareConfig)(mockNext, {}); - const mockHeadersWithChecksumHeader = { ...mockHeaders, [mockChecksumLocationName]: "mockHeaderValue" }; - const mockArgsWithChecksumHeader = { - ...mockArgs, - request: { ...mockRequest, headers: mockHeadersWithChecksumHeader }, - }; - await handler(mockArgsWithChecksumHeader); - expect(getChecksumLocationName).toHaveBeenCalledWith(ChecksumAlgorithm.MD5); - expect(selectChecksumAlgorithmFunction).toHaveBeenCalledWith(ChecksumAlgorithm.MD5, mockConfig); - expect(mockNext).toHaveBeenCalledWith(mockArgsWithChecksumHeader); - expect(hasHeader).toHaveBeenCalledWith(mockChecksumLocationName, mockHeadersWithChecksumHeader); + vi.mocked(hasHeaderWithPrefix).mockReturnValue(true); + + await handler(mockArgs); + + expect(hasHeaderWithPrefix).toHaveBeenCalledTimes(1); + expect(getChecksumLocationName).not.toHaveBeenCalled(); + expect(selectChecksumAlgorithmFunction).not.toHaveBeenCalled(); + expect(hasHeader).not.toHaveBeenCalled(); + expect(mockNext).toHaveBeenCalledWith(mockArgs); }); }); }); diff --git a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts index 7a19714e8755..470a2fcc08e6 100644 --- a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts +++ b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts @@ -15,6 +15,7 @@ import { ChecksumAlgorithm } from "./constants"; import { getChecksumAlgorithmForRequest } from "./getChecksumAlgorithmForRequest"; import { getChecksumLocationName } from "./getChecksumLocationName"; import { hasHeader } from "./hasHeader"; +import { hasHeaderWithPrefix } from "./hasHeaderWithPrefix"; import { isStreaming } from "./isStreaming"; import { selectChecksumAlgorithmFunction } from "./selectChecksumAlgorithmFunction"; import { stringHasher } from "./stringHasher"; @@ -64,6 +65,10 @@ export const flexibleChecksumsMiddleware = return next(args); } + if (hasHeaderWithPrefix("x-amz-checksum-", args.request.headers)) { + return next(args); + } + const { request, input } = args; const { body: requestBody, headers } = request; const { base64Encoder, streamHasher } = config; diff --git a/packages/middleware-flexible-checksums/src/hasHeaderWithPrefix.spec.ts b/packages/middleware-flexible-checksums/src/hasHeaderWithPrefix.spec.ts new file mode 100644 index 000000000000..c69652d7935e --- /dev/null +++ b/packages/middleware-flexible-checksums/src/hasHeaderWithPrefix.spec.ts @@ -0,0 +1,29 @@ +import { HeaderBag } from "@smithy/types"; +import { describe, expect, test as it } from "vitest"; + +import { hasHeaderWithPrefix } from "./hasHeaderWithPrefix"; + +describe(hasHeaderWithPrefix.name, () => { + const mockHeaders: HeaderBag = { + "header-prefix-lowercase-1": "header-value-1", + "HEADER-PREFIX-UPPERCASE-2": "header-value-2", + }; + + describe("contains header prefix", () => { + it("when headerPrefix is exact", () => { + expect(hasHeaderWithPrefix("header-prefix-lowercase-", mockHeaders)).toBe(true); + }); + + it("when headerPrefix is in different case", () => { + expect(hasHeaderWithPrefix("HEADER-PREFIX-LOWERCASE-", mockHeaders)).toBe(true); + }); + + it("when headerPrefix in headers is in different case", () => { + expect(hasHeaderWithPrefix("header-prefix-uppercase-", mockHeaders)).toBe(true); + }); + }); + + it("doesn't contain header with headerPrefix", () => { + expect(hasHeaderWithPrefix("header-prefix-3", mockHeaders)).toBe(false); + }); +}); diff --git a/packages/middleware-flexible-checksums/src/hasHeaderWithPrefix.ts b/packages/middleware-flexible-checksums/src/hasHeaderWithPrefix.ts new file mode 100644 index 000000000000..9ef8401fecf7 --- /dev/null +++ b/packages/middleware-flexible-checksums/src/hasHeaderWithPrefix.ts @@ -0,0 +1,15 @@ +import { HeaderBag } from "@smithy/types"; + +/** + * Returns true if header with headerPrefix is present in headers. + * Comparisons are case-insensitive. + */ +export const hasHeaderWithPrefix = (headerPrefix: string, headers: HeaderBag): boolean => { + const soughtHeaderPrefix = headerPrefix.toLowerCase(); + for (const headerName of Object.keys(headers)) { + if (headerName.toLowerCase().startsWith(soughtHeaderPrefix)) { + return true; + } + } + return false; +};