From c1b74bd524a8f1407727849d50a5a262c5a22d2c Mon Sep 17 00:00:00 2001 From: Kevin Viglucci Date: Sat, 11 Jun 2022 11:43:17 -0500 Subject: [PATCH 1/3] fix: clear buffer when allocating for custom metadata header Signed-off-by: Kevin Viglucci --- .../src/CompositeMetadata.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/rsocket-composite-metadata/src/CompositeMetadata.ts b/packages/rsocket-composite-metadata/src/CompositeMetadata.ts index 2bb7f48..d780d0b 100644 --- a/packages/rsocket-composite-metadata/src/CompositeMetadata.ts +++ b/packages/rsocket-composite-metadata/src/CompositeMetadata.ts @@ -170,14 +170,14 @@ export function encodeCustomMetadataHeader( customMime: string, metadataLength: number ): Buffer { + // allocate one byte + the length of the mimetype const metadataHeader: Buffer = Buffer.allocUnsafe(4 + customMime.length); - // reserve 1 byte for the customMime length - // /!\ careful not to read that first byte, which is random at this point - // int writerIndexInitial = metadataHeader.writerIndex(); - // metadataHeader.writerIndex(writerIndexInitial + 1); + + // fill the buffer to clear previous memory + metadataHeader.fill(0); // write the custom mime in UTF8 but validate it is all ASCII-compatible - // (which produces the right result since ASCII chars are still encoded on 1 byte in UTF8) + // (which produces the correct result since ASCII chars are still encoded on 1 byte in UTF8) const customMimeLength: number = metadataHeader.write(customMime, 1); if (!isAscii(metadataHeader, 1)) { throw new Error("Custom mime type must be US_ASCII characters only"); From 7acf04aae00f36f3eb62e9cbc22a8d7a2309705a Mon Sep 17 00:00:00 2001 From: Kevin Viglucci Date: Sat, 11 Jun 2022 11:46:32 -0500 Subject: [PATCH 2/3] test: add composite metadata tests - encodeAndAddCustomMetadata - encodeCustomMetadataHeader Signed-off-by: Kevin Viglucci --- .../encodeAndAddCustomMetadata.spec.ts | 40 +++++++++++++++++++ .../__tests__/encodeCustomMetadataHeader.ts | 33 +++++++++++++++ .../__tests__/test-utils/hex.ts | 26 ++++++++++++ .../rsocket-composite-metadata/jest.config.ts | 17 ++++++++ .../rsocket-composite-metadata/jest.setup.ts | 1 + .../rsocket-composite-metadata/package.json | 2 +- 6 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 packages/rsocket-composite-metadata/__tests__/encodeAndAddCustomMetadata.spec.ts create mode 100644 packages/rsocket-composite-metadata/__tests__/encodeCustomMetadataHeader.ts create mode 100644 packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts create mode 100644 packages/rsocket-composite-metadata/jest.config.ts create mode 100644 packages/rsocket-composite-metadata/jest.setup.ts diff --git a/packages/rsocket-composite-metadata/__tests__/encodeAndAddCustomMetadata.spec.ts b/packages/rsocket-composite-metadata/__tests__/encodeAndAddCustomMetadata.spec.ts new file mode 100644 index 0000000..d591f93 --- /dev/null +++ b/packages/rsocket-composite-metadata/__tests__/encodeAndAddCustomMetadata.spec.ts @@ -0,0 +1,40 @@ +import { encodeAndAddCustomMetadata } from "rsocket-composite-metadata"; +import { hex } from "./test-utils/hex"; + +describe("encodeAndAddCustomMetadata", () => { + it("throws if custom mimtype length is less than 1", () => { + expect(() => + encodeAndAddCustomMetadata(Buffer.from([]), "", Buffer.from("1234")) + ).toThrow( + "Custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128" + ); + }); + + it("throws if custom mimtype length is greater than 127", () => { + let mime = ""; + while (mime.length < 130) { + mime += "a"; + } + expect(() => + encodeAndAddCustomMetadata(Buffer.from([]), mime, Buffer.from("1234")) + ).toThrow( + "Custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128" + ); + }); + + it("encodes the header and payload as per spec", () => { + const { c, u, s, t, o, m } = hex; + const metadata = encodeAndAddCustomMetadata( + Buffer.from([]), + "custom", + Buffer.from("1234") + ); + const expectedHeaderLength8 = "05"; + const expectedPayloadLength24 = "000004"; + const expectedHeader = `${expectedHeaderLength8}${c}${u}${s}${t}${o}${m}${expectedPayloadLength24}`; + const expectedPayload = `${hex["1"]}${hex["2"]}${hex["3"]}${hex["4"]}`; + expect(metadata.toString("hex")).toBe( + `${expectedHeader}${expectedPayload}` + ); + }); +}); diff --git a/packages/rsocket-composite-metadata/__tests__/encodeCustomMetadataHeader.ts b/packages/rsocket-composite-metadata/__tests__/encodeCustomMetadataHeader.ts new file mode 100644 index 0000000..e8b1b5d --- /dev/null +++ b/packages/rsocket-composite-metadata/__tests__/encodeCustomMetadataHeader.ts @@ -0,0 +1,33 @@ +import { encodeCustomMetadataHeader } from "rsocket-composite-metadata"; +import { hex } from "./test-utils/hex"; + +describe("encodeCustomMetadataHeader", () => { + it("throws if length is less than 1", () => { + expect(() => encodeCustomMetadataHeader("", 0)).toThrow( + "Custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128" + ); + }); + + it("throws if length is greater than 127", () => { + let mime = ""; + while (mime.length < 130) { + mime += "a"; + } + expect(() => encodeCustomMetadataHeader(mime, mime.length)).toThrow( + "Custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128" + ); + }); + + it("encodes the header as per spec", () => { + const { t, e, s } = hex; + const mime = "test"; + // length minus 1 (uint8) + const expectedLength8 = "03"; + // full length (uint24) + const expectedLength24 = "000004"; + const header = encodeCustomMetadataHeader(mime, mime.length); + expect(header.toString("hex")).toBe( + `${expectedLength8}${t}${e}${s}${t}${expectedLength24}` + ); + }); +}); diff --git a/packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts b/packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts new file mode 100644 index 0000000..f95daae --- /dev/null +++ b/packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts @@ -0,0 +1,26 @@ +function numHex(s) { + let a = s.toString(16); + if (a.length % 2 > 0) { + a = "0" + a; + } + return a; +} + +function strHex(s) { + let a = ""; + for (let i = 0; i < s.length; i++) { + a = a + numHex(s.charCodeAt(i)); + } + + return a; +} + +const numeric = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; +// const numericChars = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; +const alphabetNumeric = "abcdefghijklmnopqrstuvqxyz0123456789"; + +export const hex: any = {}; + +alphabetNumeric.split("").forEach((c) => { + hex[c] = strHex(c); +}); diff --git a/packages/rsocket-composite-metadata/jest.config.ts b/packages/rsocket-composite-metadata/jest.config.ts new file mode 100644 index 0000000..aa6e88e --- /dev/null +++ b/packages/rsocket-composite-metadata/jest.config.ts @@ -0,0 +1,17 @@ +import type { Config } from "@jest/types"; +import { pathsToModuleNameMapper } from "ts-jest/utils"; +import { compilerOptions } from "../../tsconfig.json"; + +const config: Config.InitialOptions = { + preset: "ts-jest", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { + // This has to match the baseUrl defined in tsconfig.json. + prefix: "/../../", + }), + modulePathIgnorePatterns: ["/__tests__/test-utils"], + collectCoverage: true, + collectCoverageFrom: ["/src/**/*.ts", "!**/node_modules/**"], + setupFilesAfterEnv: ["/jest.setup.ts"], +}; + +export default config; diff --git a/packages/rsocket-composite-metadata/jest.setup.ts b/packages/rsocket-composite-metadata/jest.setup.ts new file mode 100644 index 0000000..2289a34 --- /dev/null +++ b/packages/rsocket-composite-metadata/jest.setup.ts @@ -0,0 +1 @@ +expect.extend({}); diff --git a/packages/rsocket-composite-metadata/package.json b/packages/rsocket-composite-metadata/package.json index b927ee1..cf766f3 100644 --- a/packages/rsocket-composite-metadata/package.json +++ b/packages/rsocket-composite-metadata/package.json @@ -16,7 +16,7 @@ "clean": "rimraf -rf ./dist", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", - "test": "echo \"Error: no test specified\" && exit 0" + "test": "yarn jest" }, "dependencies": { "rsocket-core": "^1.0.0-alpha.1" From 01e3e2cb4b95d3725eecce16683b66226e4b68b7 Mon Sep 17 00:00:00 2001 From: Kevin Viglucci Date: Sun, 12 Jun 2022 09:43:52 -0500 Subject: [PATCH 3/3] refactor: remove unusd variables from test-utils Signed-off-by: Kevin Viglucci --- packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts b/packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts index f95daae..4bfe6bd 100644 --- a/packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts +++ b/packages/rsocket-composite-metadata/__tests__/test-utils/hex.ts @@ -15,8 +15,6 @@ function strHex(s) { return a; } -const numeric = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; -// const numericChars = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; const alphabetNumeric = "abcdefghijklmnopqrstuvqxyz0123456789"; export const hex: any = {};