From 6a48760ced7fb088d4989ec138b55210181c267f Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Fri, 8 Nov 2024 08:15:15 -0800 Subject: [PATCH] feat(middleware-flexible-checksums): use Node.js native CRC32 checksum API (#6641) --- .../package.json | 4 ++ ...tCrc32ChecksumAlgorithmFunction.browser.ts | 3 ++ .../getCrc32ChecksumAlgorithmFunction.spec.ts | 47 +++++++++++++++++++ .../src/getCrc32ChecksumAlgorithmFunction.ts | 28 +++++++++++ .../src/selectChecksumAlgorithmFunction.ts | 4 +- yarn.lock | 2 +- 6 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.browser.ts create mode 100644 packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.spec.ts create mode 100644 packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.ts diff --git a/packages/middleware-flexible-checksums/package.json b/packages/middleware-flexible-checksums/package.json index b929c541b01b4..c26fe92c26657 100644 --- a/packages/middleware-flexible-checksums/package.json +++ b/packages/middleware-flexible-checksums/package.json @@ -17,10 +17,13 @@ "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", "browser": { + "./dist-es/getCrc32ChecksumAlgorithmFunction": "./dist-es/getCrc32ChecksumAlgorithmFunction.browser", "./dist-es/streams/create-read-stream-on-buffer": "./dist-es/streams/create-read-stream-on-buffer.browser" }, "react-native": { + "./dist-es/getCrc32ChecksumAlgorithmFunction": "./dist-es/getCrc32ChecksumAlgorithmFunction.browser", "./dist-es/streams/create-read-stream-on-buffer": "./dist-es/streams/create-read-stream-on-buffer.browser", + "./dist-cjs/getCrc32ChecksumAlgorithmFunction": "./dist-cjs/getCrc32ChecksumAlgorithmFunction.browser", "./dist-cjs/streams/create-read-stream-on-buffer": "./dist-cjs/streams/create-read-stream-on-buffer.browser" }, "types": "./dist-types/index.d.ts", @@ -32,6 +35,7 @@ "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "*", "@aws-sdk/types": "*", "@smithy/is-array-buffer": "^3.0.0", diff --git a/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.browser.ts b/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.browser.ts new file mode 100644 index 0000000000000..1c482b41706c2 --- /dev/null +++ b/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.browser.ts @@ -0,0 +1,3 @@ +import { AwsCrc32 } from "@aws-crypto/crc32"; + +export const getCrc32ChecksumAlgorithmFunction = () => AwsCrc32; diff --git a/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.spec.ts b/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.spec.ts new file mode 100644 index 0000000000000..053895b815f02 --- /dev/null +++ b/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.spec.ts @@ -0,0 +1,47 @@ +import { AwsCrc32 } from "@aws-crypto/crc32"; +import { numToUint8 } from "@aws-crypto/util"; +import { describe, expect, test as it, vi } from "vitest"; +import * as zlib from "zlib"; + +import { getCrc32ChecksumAlgorithmFunction } from "./getCrc32ChecksumAlgorithmFunction"; + +describe(getCrc32ChecksumAlgorithmFunction.name, () => { + it("returns AwsCrc32 if zlib.crc32 is undefined", () => { + vi.mock("zlib", () => ({ crc32: undefined })); + expect(getCrc32ChecksumAlgorithmFunction()).toBe(AwsCrc32); + }); + + it("returns NodeCrc32 if zlib.crc32 is defined", async () => { + const mockData = new Uint8Array([1, 2, 3]); + const mockChecksum = 42; + + // @ts-expect-error crc32 is defined only for Node.js >=v20.15.0 and >=v22.2.0. + zlib.crc32 = vi + .fn() + .mockReturnValueOnce(mockChecksum) + .mockReturnValueOnce(2 * mockChecksum); + + const crc32Fn = getCrc32ChecksumAlgorithmFunction(); + expect(crc32Fn).not.toBe(AwsCrc32); + + // @ts-expect-error crc32 is defined only for Node.js >=v20.15.0 and >=v22.2.0. + expect(zlib.crc32).not.toHaveBeenCalled(); + const crc32 = new crc32Fn(); + // @ts-expect-error crc32 is defined only for Node.js >=v20.15.0 and >=v22.2.0. + expect(zlib.crc32).not.toHaveBeenCalled(); + expect(await crc32.digest()).toEqual(numToUint8(0)); + + crc32.update(mockData); + // @ts-expect-error crc32 is defined only for Node.js >=v20.15.0 and >=v22.2.0. + expect(zlib.crc32).toHaveBeenCalledWith(mockData, 0); + expect(await crc32.digest()).toEqual(numToUint8(mockChecksum)); + + crc32.update(mockData); + // @ts-expect-error crc32 is defined only for Node.js >=v20.15.0 and >=v22.2.0. + expect(zlib.crc32).toHaveBeenCalledWith(mockData, mockChecksum); + expect(await crc32.digest()).toEqual(numToUint8(2 * mockChecksum)); + + crc32.reset(); + expect(await crc32.digest()).toEqual(numToUint8(0)); + }); +}); diff --git a/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.ts b/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.ts new file mode 100644 index 0000000000000..3803ccfdedae0 --- /dev/null +++ b/packages/middleware-flexible-checksums/src/getCrc32ChecksumAlgorithmFunction.ts @@ -0,0 +1,28 @@ +import { AwsCrc32 } from "@aws-crypto/crc32"; +import { numToUint8 } from "@aws-crypto/util"; +import { Checksum } from "@smithy/types"; +import * as zlib from "zlib"; + +export const getCrc32ChecksumAlgorithmFunction = () => { + // @ts-expect-error crc32 is defined only for Node.js >=v20.15.0 and >=v22.2.0. + if (typeof zlib.crc32 === "undefined") { + return AwsCrc32; + } + + return class NodeCrc32 implements Checksum { + checksum = 0; + + update(data: Uint8Array) { + // @ts-expect-error crc32 is defined only for Node.js >=v20.15.0 and >=v22.2.0. + this.checksum = zlib.crc32(data, this.checksum); + } + + async digest() { + return numToUint8(this.checksum); + } + + reset() { + this.checksum = 0; + } + }; +}; diff --git a/packages/middleware-flexible-checksums/src/selectChecksumAlgorithmFunction.ts b/packages/middleware-flexible-checksums/src/selectChecksumAlgorithmFunction.ts index 39e0f192245a0..74a09add4a300 100644 --- a/packages/middleware-flexible-checksums/src/selectChecksumAlgorithmFunction.ts +++ b/packages/middleware-flexible-checksums/src/selectChecksumAlgorithmFunction.ts @@ -1,9 +1,9 @@ -import { AwsCrc32 } from "@aws-crypto/crc32"; import { AwsCrc32c } from "@aws-crypto/crc32c"; import { ChecksumConstructor, HashConstructor } from "@smithy/types"; import { PreviouslyResolved } from "./configuration"; import { ChecksumAlgorithm } from "./constants"; +import { getCrc32ChecksumAlgorithmFunction } from "./getCrc32ChecksumAlgorithmFunction"; /** * Returns the function that will compute the checksum for the given {@link ChecksumAlgorithm}. @@ -14,7 +14,7 @@ export const selectChecksumAlgorithmFunction = ( ): ChecksumConstructor | HashConstructor => ({ [ChecksumAlgorithm.MD5]: config.md5, - [ChecksumAlgorithm.CRC32]: AwsCrc32, + [ChecksumAlgorithm.CRC32]: getCrc32ChecksumAlgorithmFunction(), [ChecksumAlgorithm.CRC32C]: AwsCrc32c, [ChecksumAlgorithm.SHA1]: config.sha1, [ChecksumAlgorithm.SHA256]: config.sha256, diff --git a/yarn.lock b/yarn.lock index cecbf306d72da..5c0b0a0276059 100644 --- a/yarn.lock +++ b/yarn.lock @@ -74,7 +74,7 @@ dependencies: tslib "^2.6.2" -"@aws-crypto/util@^5.2.0": +"@aws-crypto/util@5.2.0", "@aws-crypto/util@^5.2.0": version "5.2.0" resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz" integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==