Skip to content

Commit bcd7336

Browse files
authored
fix/custom metadata header alloc (#231)
* fix: clear buffer when allocating for custom metadata header * test: add composite metadata tests - encodeAndAddCustomMetadata - encodeCustomMetadataHeader Signed-off-by: Kevin Viglucci <[email protected]>
1 parent 7b1fe16 commit bcd7336

File tree

7 files changed

+121
-6
lines changed

7 files changed

+121
-6
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { encodeAndAddCustomMetadata } from "rsocket-composite-metadata";
2+
import { hex } from "./test-utils/hex";
3+
4+
describe("encodeAndAddCustomMetadata", () => {
5+
it("throws if custom mimtype length is less than 1", () => {
6+
expect(() =>
7+
encodeAndAddCustomMetadata(Buffer.from([]), "", Buffer.from("1234"))
8+
).toThrow(
9+
"Custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128"
10+
);
11+
});
12+
13+
it("throws if custom mimtype length is greater than 127", () => {
14+
let mime = "";
15+
while (mime.length < 130) {
16+
mime += "a";
17+
}
18+
expect(() =>
19+
encodeAndAddCustomMetadata(Buffer.from([]), mime, Buffer.from("1234"))
20+
).toThrow(
21+
"Custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128"
22+
);
23+
});
24+
25+
it("encodes the header and payload as per spec", () => {
26+
const { c, u, s, t, o, m } = hex;
27+
const metadata = encodeAndAddCustomMetadata(
28+
Buffer.from([]),
29+
"custom",
30+
Buffer.from("1234")
31+
);
32+
const expectedHeaderLength8 = "05";
33+
const expectedPayloadLength24 = "000004";
34+
const expectedHeader = `${expectedHeaderLength8}${c}${u}${s}${t}${o}${m}${expectedPayloadLength24}`;
35+
const expectedPayload = `${hex["1"]}${hex["2"]}${hex["3"]}${hex["4"]}`;
36+
expect(metadata.toString("hex")).toBe(
37+
`${expectedHeader}${expectedPayload}`
38+
);
39+
});
40+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { encodeCustomMetadataHeader } from "rsocket-composite-metadata";
2+
import { hex } from "./test-utils/hex";
3+
4+
describe("encodeCustomMetadataHeader", () => {
5+
it("throws if length is less than 1", () => {
6+
expect(() => encodeCustomMetadataHeader("", 0)).toThrow(
7+
"Custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128"
8+
);
9+
});
10+
11+
it("throws if length is greater than 127", () => {
12+
let mime = "";
13+
while (mime.length < 130) {
14+
mime += "a";
15+
}
16+
expect(() => encodeCustomMetadataHeader(mime, mime.length)).toThrow(
17+
"Custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128"
18+
);
19+
});
20+
21+
it("encodes the header as per spec", () => {
22+
const { t, e, s } = hex;
23+
const mime = "test";
24+
// length minus 1 (uint8)
25+
const expectedLength8 = "03";
26+
// full length (uint24)
27+
const expectedLength24 = "000004";
28+
const header = encodeCustomMetadataHeader(mime, mime.length);
29+
expect(header.toString("hex")).toBe(
30+
`${expectedLength8}${t}${e}${s}${t}${expectedLength24}`
31+
);
32+
});
33+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
function numHex(s) {
2+
let a = s.toString(16);
3+
if (a.length % 2 > 0) {
4+
a = "0" + a;
5+
}
6+
return a;
7+
}
8+
9+
function strHex(s) {
10+
let a = "";
11+
for (let i = 0; i < s.length; i++) {
12+
a = a + numHex(s.charCodeAt(i));
13+
}
14+
15+
return a;
16+
}
17+
18+
const alphabetNumeric = "abcdefghijklmnopqrstuvqxyz0123456789";
19+
20+
export const hex: any = {};
21+
22+
alphabetNumeric.split("").forEach((c) => {
23+
hex[c] = strHex(c);
24+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { Config } from "@jest/types";
2+
import { pathsToModuleNameMapper } from "ts-jest/utils";
3+
import { compilerOptions } from "../../tsconfig.json";
4+
5+
const config: Config.InitialOptions = {
6+
preset: "ts-jest",
7+
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
8+
// This has to match the baseUrl defined in tsconfig.json.
9+
prefix: "<rootDir>/../../",
10+
}),
11+
modulePathIgnorePatterns: ["<rootDir>/__tests__/test-utils"],
12+
collectCoverage: true,
13+
collectCoverageFrom: ["<rootDir>/src/**/*.ts", "!**/node_modules/**"],
14+
setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
15+
};
16+
17+
export default config;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
expect.extend({});

packages/rsocket-composite-metadata/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"clean": "rimraf -rf ./dist",
1717
"compile": "tsc -p tsconfig.build.json",
1818
"prepublishOnly": "yarn run build",
19-
"test": "echo \"Error: no test specified\" && exit 0"
19+
"test": "yarn jest"
2020
},
2121
"dependencies": {
2222
"rsocket-core": "^1.0.0-alpha.1"

packages/rsocket-composite-metadata/src/CompositeMetadata.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,14 @@ export function encodeCustomMetadataHeader(
170170
customMime: string,
171171
metadataLength: number
172172
): Buffer {
173+
// allocate one byte + the length of the mimetype
173174
const metadataHeader: Buffer = Buffer.allocUnsafe(4 + customMime.length);
174-
// reserve 1 byte for the customMime length
175-
// /!\ careful not to read that first byte, which is random at this point
176-
// int writerIndexInitial = metadataHeader.writerIndex();
177-
// metadataHeader.writerIndex(writerIndexInitial + 1);
175+
176+
// fill the buffer to clear previous memory
177+
metadataHeader.fill(0);
178178

179179
// write the custom mime in UTF8 but validate it is all ASCII-compatible
180-
// (which produces the right result since ASCII chars are still encoded on 1 byte in UTF8)
180+
// (which produces the correct result since ASCII chars are still encoded on 1 byte in UTF8)
181181
const customMimeLength: number = metadataHeader.write(customMime, 1);
182182
if (!isAscii(metadataHeader, 1)) {
183183
throw new Error("Custom mime type must be US_ASCII characters only");

0 commit comments

Comments
 (0)