From 5a360a94614c6c16fbfa9ac9df0366849abb7858 Mon Sep 17 00:00:00 2001 From: Matteo Paramatti Date: Mon, 19 Sep 2022 17:38:11 +0100 Subject: [PATCH 1/2] Split classes into multiple files and added FileInfoMessage --- jest.config.js | 6 - jest.config.ts | 9 ++ package-lock.json | 259 +++++++++++++++++++++++++++++++++- package.json | 3 +- src/AckMessage.ts | 23 +++ src/FileChunkMessage.ts | 72 ++++++++++ src/FileInfoMessage.ts | 75 ++++++++++ src/Message.ts | 30 ++++ src/MessageType.ts | 15 ++ src/main.ts | 134 +----------------- test/AckMessage.test.ts | 22 +++ test/FileChunkMessage.test.ts | 66 +++++++++ test/FileInfoMessage.test.ts | 85 +++++++++++ test/main.test.ts | 80 ----------- tsconfig.json | 3 +- 15 files changed, 663 insertions(+), 219 deletions(-) delete mode 100644 jest.config.js create mode 100644 jest.config.ts create mode 100644 src/AckMessage.ts create mode 100644 src/FileChunkMessage.ts create mode 100644 src/FileInfoMessage.ts create mode 100644 src/Message.ts create mode 100644 src/MessageType.ts create mode 100644 test/AckMessage.test.ts create mode 100644 test/FileChunkMessage.test.ts create mode 100644 test/FileInfoMessage.test.ts delete mode 100644 test/main.test.ts diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 2a9ec31..0000000 --- a/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - transform: {'^.+\\.ts?$': 'ts-jest'}, - testEnvironment: 'node', - testRegex: '/test/.*\\.(test|spec)?\\.(ts|tsx)$', - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] -}; diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 0000000..b240fbd --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,9 @@ +import type { Config } from 'jest'; + +const config: Config = { + verbose: true, + preset: 'ts-jest', + testEnvironment: 'node', +}; + +export default config; diff --git a/package-lock.json b/package-lock.json index 3b5dee6..0988eb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "@doggofrens/filesharing-ws-proto", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@doggofrens/filesharing-ws-proto", - "version": "0.1.1", + "version": "0.1.2", "license": "ISC", "devDependencies": { "@types/jest": "^29.0.3", "jest": "^29.0.3", "ts-jest": "^29.0.1", + "ts-node": "^10.9.1", "typescript": "^4.8.3" } }, @@ -593,6 +594,28 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -968,6 +991,30 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.1.19", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", @@ -1085,6 +1132,27 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1137,6 +1205,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1451,6 +1525,12 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1506,6 +1586,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.0.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.0.0.tgz", @@ -3346,6 +3435,49 @@ "node": ">=10" } }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -3406,6 +3538,12 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -3522,6 +3660,15 @@ "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -3970,6 +4117,27 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -4272,6 +4440,30 @@ "@sinonjs/commons": "^1.7.0" } }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, "@types/babel__core": { "version": "7.1.19", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", @@ -4389,6 +4581,18 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -4423,6 +4627,12 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -4662,6 +4872,12 @@ "safe-buffer": "~5.1.1" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4700,6 +4916,12 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, "diff-sequences": { "version": "29.0.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.0.0.tgz", @@ -6060,6 +6282,27 @@ } } }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -6088,6 +6331,12 @@ "picocolors": "^1.0.0" } }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -6177,6 +6426,12 @@ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 38ee3e2..4539b74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@doggofrens/filesharing-ws-proto", - "version": "0.1.2", + "version": "0.2.0", "description": "Protocol for 2-way WebSocket communication", "main": "lib/main.js", "types": "lib/main.d.ts", @@ -23,6 +23,7 @@ "@types/jest": "^29.0.3", "jest": "^29.0.3", "ts-jest": "^29.0.1", + "ts-node": "^10.9.1", "typescript": "^4.8.3" } } diff --git a/src/AckMessage.ts b/src/AckMessage.ts new file mode 100644 index 0000000..b0112e4 --- /dev/null +++ b/src/AckMessage.ts @@ -0,0 +1,23 @@ +import { MessageType } from './MessageType'; +import { Message } from "./Message"; + +/** + * A message that is sent to acknowledge a received message. + * + * @extends Message + */ + +export class AckMessage extends Message { + + constructor() { + super(MessageType.Ack); + } + + /** + * @override + */ + toUint8Array(): Uint8Array { + return new Uint8Array([this.type]); + } + +} diff --git a/src/FileChunkMessage.ts b/src/FileChunkMessage.ts new file mode 100644 index 0000000..731f59e --- /dev/null +++ b/src/FileChunkMessage.ts @@ -0,0 +1,72 @@ +import { MessageType } from './MessageType'; +import { Message } from "./Message"; + +/** + * A message that contains a chunk of data. + * + * @extends Message + */ + +export class FileChunkMessage extends Message { + + /** + * The number of the chunk, where 0 is the first chunk. + * + * @type {number} + * @readonly + */ + readonly chunkNumber: number; + + /** + * The chunk data. + * + * @type {Uint8Array} + * @readonly + */ + readonly chunkBytes: Uint8Array; + + /** + * @param chunkNumber The number of the chunk, where 0 is the first chunk. + * @param chunkBytes The chunk data. + */ + constructor(chunkNumber: number, chunkBytes: Uint8Array) { + super(MessageType.FileChunk); + this.chunkNumber = chunkNumber; + this.chunkBytes = chunkBytes; + } + + /** + * Parses a FileChunkMessage from a Uint8Array. + * + * @param byteArray The byte array to parse. + * @param expectedChunkSize The expected size of the chunk, in bytes. + * @returns The parsed FileChunkMessage, or null if the byte array is invalid. + */ + static fromUint8Array(byteArray: Uint8Array, expectedChunkSize?: number): FileChunkMessage | null { + if (expectedChunkSize && byteArray.byteLength != 2 + expectedChunkSize) { + return null; + } + + if (byteArray[0] !== MessageType.FileChunk) { + return null; + } + + const chunkNumber = byteArray[1]; + const chunkBytes = byteArray.slice(2); + + return new FileChunkMessage(chunkNumber, chunkBytes); + } + + /** + * @override + */ + toUint8Array(): Uint8Array { + const byteArray = new Uint8Array(2 + this.chunkBytes.byteLength); + byteArray[0] = this.type; + byteArray[1] = this.chunkNumber; + byteArray.set(this.chunkBytes, 2); + + return byteArray; + } + +} diff --git a/src/FileInfoMessage.ts b/src/FileInfoMessage.ts new file mode 100644 index 0000000..05799e0 --- /dev/null +++ b/src/FileInfoMessage.ts @@ -0,0 +1,75 @@ +import { MessageType } from './MessageType'; +import { Message } from "./Message"; + +/** + * A message that contains information about a file. + * + * @extends Message + */ +export class FileInfoMessage extends Message { + + /** + * The name of the file. + * + * @type {string} + * @readonly + */ + readonly name: string; + + /** + * The size of the file in bytes. + * + * @type {number} + * @readonly + */ + readonly size: number; + + /** + * @param name The name of the file. + * @param size The size of the file in bytes. + */ + constructor(name: string, size: number) { + super(MessageType.FileInfo); + this.name = name; + this.size = size; + } + + /** + * Parses a FileInfoMessage from a Uint8Array. + * + * @param byteArray The byte array to parse. + * @returns The parsed FileInfoMessage, or null if the byte array is invalid. + */ + static fromUint8Array(byteArray: Uint8Array): FileInfoMessage | null { + // Minimum size: 1 (type) + 4 (size) + 1 (name with at least one byte) + if (byteArray.length < 6) { + return null; + } + + //TODO: Return null if size is too big + if (byteArray[0] !== MessageType.FileInfo) { + return null; + } + + const size: number = new Uint32Array(byteArray.slice(1, 5).buffer)[0]; + const name: string = Buffer.from(byteArray.slice(5)).toString('utf8'); + + return new FileInfoMessage(name, size); + } + + /** + * @override + */ + toUint8Array(): Uint8Array { + const nameBytes: Uint8Array = Buffer.from(this.name, 'utf8'); + const sizeBytes: Uint8Array = new Uint8Array(new Uint32Array([this.size]).buffer); + + const byteArray = new Uint8Array(1 + 4 + nameBytes.byteLength); + byteArray[0] = this.type; + byteArray.set(sizeBytes, 1); + byteArray.set(nameBytes, 5); + + return byteArray; + } + +} diff --git a/src/Message.ts b/src/Message.ts new file mode 100644 index 0000000..dc90a01 --- /dev/null +++ b/src/Message.ts @@ -0,0 +1,30 @@ +import { MessageType } from './MessageType'; + +/** + * An abstract class that represents a WebSocket message. + */ + +export abstract class Message { + + /** + * The type of the message. + * + * @type {MessageType} + * @readonly + */ + readonly type: MessageType; + + /** + * @param type The type of the message. + * @see MessageType + */ + constructor(type: MessageType) { + this.type = type; + } + + /** + * Returns the whole message as a Uint8Array. + */ + abstract toUint8Array(): Uint8Array; + +} diff --git a/src/MessageType.ts b/src/MessageType.ts new file mode 100644 index 0000000..f7f4062 --- /dev/null +++ b/src/MessageType.ts @@ -0,0 +1,15 @@ +/** + * Enum of all known message types. + * + * @enum {number} + */ +// NOTE: Use values between 0 and 255 to avoid overflow, as the value is converted to a single byte. +export enum MessageType { + Ack = 0, + + FileInfo = 1, + FileChunk = 2, + + FileInfoReq = 50, + UploadStartReq = 51, +} diff --git a/src/main.ts b/src/main.ts index a452ac5..2d2d166 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,129 +1,5 @@ -/** - * Enum of all known message types. - * - * @enum {number} - */ -// NOTE: Use values between 0 and 255 to avoid overflow, as the value is converted to a single byte. -export enum MessageType { - Ack = 0, - Chunk = 1 -} - - -/** - * An abstract class that represents a WebSocket message. - */ -export abstract class Message { - - /** - * The type of the message. - * - * @type {MessageType} - * @readonly - */ - readonly type: MessageType; - - /** - * @param type The type of the message. - * @see MessageType - */ - constructor(type: MessageType) { - this.type = type; - } - - /** - * Returns the whole message as a Uint8Array. - */ - abstract toUint8Array(): Uint8Array; - -} - -/** - * A message that is sent to acknowledge a received message. - * - * @extends Message - */ -export class AckMessage extends Message { - - constructor() { - super(MessageType.Ack); - } - - /** - * @override - */ - toUint8Array(): Uint8Array { - return new Uint8Array([this.type]); - } - -} - - -/** - * A message that contains a chunk of data. - * - * @extends Message - */ -export class ChunkMessage extends Message { - - /** - * The number of the chunk, where 0 is the first chunk. - * - * @type {number} - * @readonly - */ - readonly chunkNumber: number; - - /** - * The chunk data. - * - * @type {Uint8Array} - * @readonly - */ - readonly chunkBytes: Uint8Array; - - /** - * @param chunkNumber The number of the chunk, where 0 is the first chunk. - * @param chunkBytes The chunk data. - */ - constructor(chunkNumber: number, chunkBytes: Uint8Array) { - super(MessageType.Chunk); - this.chunkNumber = chunkNumber; - this.chunkBytes = chunkBytes; - } - - /** - * Parses a ChunkMessage from a Uint8Array. - * - * @param byteArray The byte array to parse. - * @param expectedChunkSize The expected size of the chunk, in bytes. - * @returns The parsed message. - */ - static fromUint8Array(byteArray: Uint8Array, expectedChunkSize?: number): ChunkMessage | null { - if (expectedChunkSize && byteArray.byteLength != 2 + expectedChunkSize) { - return null; - } - - if (byteArray[0] != MessageType.Chunk) { - return null; - } - - const chunkNumber = byteArray[1]; - const chunkBytes = byteArray.slice(2); - - return new ChunkMessage(chunkNumber, chunkBytes); - } - - /** - * @override - */ - toUint8Array(): Uint8Array { - const byteArray = new Uint8Array(2 + this.chunkBytes.byteLength); - byteArray[0] = this.type; - byteArray[1] = this.chunkNumber; - byteArray.set(this.chunkBytes, 2); - - return byteArray; - } - -} +export { MessageType } from './MessageType'; +export { Message } from './Message'; +export { AckMessage } from './AckMessage'; +export { FileInfoMessage } from './FileInfoMessage'; +export { FileChunkMessage } from './FileChunkMessage'; diff --git a/test/AckMessage.test.ts b/test/AckMessage.test.ts new file mode 100644 index 0000000..c996acc --- /dev/null +++ b/test/AckMessage.test.ts @@ -0,0 +1,22 @@ +import { MessageType } from '../src/MessageType'; +import { AckMessage } from '../src/AckMessage'; + +describe('AckMessage', () => { + + describe('constructor', () => { + it('should set the correct type', () => { + expect(new AckMessage().type).toBe(MessageType.Ack); + }); + }); + + describe('toUint8Array', () => { + it('should return a byte array with the correct length', () => { + expect(new AckMessage().toUint8Array().length).toBe(1); + }); + + it('should serialise correctly', () => { + expect(new AckMessage().toUint8Array()).toEqual(new Uint8Array([MessageType.Ack])); + }); + }); + +}); diff --git a/test/FileChunkMessage.test.ts b/test/FileChunkMessage.test.ts new file mode 100644 index 0000000..74bb5d7 --- /dev/null +++ b/test/FileChunkMessage.test.ts @@ -0,0 +1,66 @@ +import { FileChunkMessage } from '../src/FileChunkMessage'; +import { MessageType } from '../src/MessageType'; + +describe('FileChunkMessage', () => { + + describe('constructor', () => { + it('should set the correct type', () => { + expect(new FileChunkMessage(0, new Uint8Array([0])).type).toBe(MessageType.FileChunk); + }); + + it('should set the correct chunk number', () => { + expect(new FileChunkMessage(0, new Uint8Array([0])).chunkNumber).toBe(0); + expect(new FileChunkMessage(1, new Uint8Array([0])).chunkNumber).toBe(1); + expect(new FileChunkMessage(123, new Uint8Array([0])).chunkNumber).toBe(123); + }); + + it('should set the correct chunk bytes', () => { + expect(new FileChunkMessage(0, new Uint8Array([])).chunkBytes).toEqual(new Uint8Array([])); + expect(new FileChunkMessage(0, new Uint8Array([0])).chunkBytes).toEqual(new Uint8Array([0])); + expect(new FileChunkMessage(0, new Uint8Array([0, 1, 2, 3])).chunkBytes).toEqual(new Uint8Array([0, 1, 2, 3])); + }); + }); + + describe('toUint8Array', () => { + it('should return a byte array with the correct length', () => { + expect(new FileChunkMessage(0, new Uint8Array([])).toUint8Array().length).toBe(2); + expect(new FileChunkMessage(0, new Uint8Array([0])).toUint8Array().length).toBe(3); + expect(new FileChunkMessage(0, new Uint8Array([0, 1, 2, 3])).toUint8Array().length).toBe(6); + }); + + it('should serialise correctly', () => { + expect(new FileChunkMessage(0, new Uint8Array([])).toUint8Array()).toEqual(new Uint8Array([MessageType.FileChunk, 0])); + expect(new FileChunkMessage(0, new Uint8Array([0])).toUint8Array()).toEqual(new Uint8Array([MessageType.FileChunk, 0, 0])); + expect(new FileChunkMessage(0, new Uint8Array([0, 1, 2, 3])).toUint8Array()).toEqual(new Uint8Array([MessageType.FileChunk, 0, 0, 1, 2, 3])); + expect(new FileChunkMessage(255, new Uint8Array([0, 1, 2, 3])).toUint8Array()).toEqual(new Uint8Array([MessageType.FileChunk, 255, 0, 1, 2, 3])); + }); + }); + + describe('fromUint8Array', () => { + it('should return null if the message type is incorrect', () => { + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.Ack, 0]))).toBeNull(); + }); + + it('should return null if the chunk size is different than expected', () => { + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.FileChunk, 0, 0, 0]), 1)).toBeNull(); + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.FileChunk, 0, 0, 0]), 3)).toBeNull(); + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.FileChunk, 0, 0, 0, 0, 0, 0]), 3)).toBeNull(); + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.FileChunk, 0, 0, 0, 0, 0, 0]), 6)).toBeNull(); + }); + + it('should parse a byte array correctly', () => { + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.FileChunk, 0]))).toEqual(new FileChunkMessage(0, new Uint8Array([]))); + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.FileChunk, 0, 0]))).toEqual(new FileChunkMessage(0, new Uint8Array([0]))); + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.FileChunk, 0, 0, 1, 2, 3]))).toEqual(new FileChunkMessage(0, new Uint8Array([0, 1, 2, 3]))); + expect(FileChunkMessage.fromUint8Array(new Uint8Array([MessageType.FileChunk, 255, 0, 1, 2, 3]))).toEqual(new FileChunkMessage(255, new Uint8Array([0, 1, 2, 3]))); + }); + + it('should parse a message that was serialised by toUint8Array correctly', () => { + expect(FileChunkMessage.fromUint8Array(new FileChunkMessage(0, new Uint8Array([])).toUint8Array())).toEqual(new FileChunkMessage(0, new Uint8Array([]))); + expect(FileChunkMessage.fromUint8Array(new FileChunkMessage(0, new Uint8Array([0])).toUint8Array())).toEqual(new FileChunkMessage(0, new Uint8Array([0]))); + expect(FileChunkMessage.fromUint8Array(new FileChunkMessage(0, new Uint8Array([0, 1, 2, 3])).toUint8Array())).toEqual(new FileChunkMessage(0, new Uint8Array([0, 1, 2, 3]))); + expect(FileChunkMessage.fromUint8Array(new FileChunkMessage(255, new Uint8Array([0, 1, 2, 3])).toUint8Array())).toEqual(new FileChunkMessage(255, new Uint8Array([0, 1, 2, 3]))); + }); + }); + +}); diff --git a/test/FileInfoMessage.test.ts b/test/FileInfoMessage.test.ts new file mode 100644 index 0000000..83ff15d --- /dev/null +++ b/test/FileInfoMessage.test.ts @@ -0,0 +1,85 @@ +import { MessageType } from '../src/MessageType'; +import { FileInfoMessage } from '../src/FileInfoMessage'; + +describe('FileInfoMessage', () => { + + // UTF-8 Character sizes: Ϊ 2, Ϋ 2, ψ 2, И 2, д 2, ҡ 2, Ӫ 2, ← 3, Ⅱ 3, . 1, m 1, p 1, 3 1; TOTAL: 24 + const utf8Filename = 'ΪΫψИдҡӪ←Ⅱ.mp3'; + const utf8FilenameLength = 24; + const utf8Bytes = [0xce, 0xaa, 0xce, 0xab, 0xcf, 0x88, 0xd0, 0x98, 0xd0, 0xb4, 0xd2, 0xa1, 0xd3, 0xaa, 0xe2, 0x86, 0x90, 0xe2, 0x85, 0xa1, 0x2e, 0x6d, 0x70, 0x33]; + + describe('constructor', () => { + it('should set the correct type', () => { + expect(new FileInfoMessage('test', 0).type).toBe(MessageType.FileInfo); + }); + + it('should set the correct name', () => { + expect(new FileInfoMessage('test', 0).name).toBe('test'); + expect(new FileInfoMessage('test123.txt', 0).name).toBe('test123.txt'); + expect(new FileInfoMessage(utf8Filename, 0).name).toBe(utf8Filename); + }); + + it('should set the correct size', () => { + expect(new FileInfoMessage('test', 0).size).toBe(0); + expect(new FileInfoMessage('test', 123).size).toBe(123); + }); + }); + + describe('toUint8Array', () => { + it('should return a byte array with the correct length', () => { + expect(new FileInfoMessage('test', 0).toUint8Array().length).toBe(9); + expect(new FileInfoMessage('test', 123).toUint8Array().length).toBe(9); + expect(new FileInfoMessage('test.txt', 123).toUint8Array().length).toBe(13); + + expect(new FileInfoMessage(utf8Filename, 123).toUint8Array().length).toBe(1 + 4 + utf8FilenameLength); + }); + + it('should serialise correctly', () => { + expect(new FileInfoMessage('test', 0).toUint8Array()).toEqual(new Uint8Array([MessageType.FileInfo, 0, 0, 0, 0, 0x74, 0x65, 0x73, 0x74])); + // 32-bit size is little-endian + expect(new FileInfoMessage('test', 123).toUint8Array()).toEqual(new Uint8Array([MessageType.FileInfo, 0x7b, 0, 0, 0, 0x74, 0x65, 0x73, 0x74])); + expect(new FileInfoMessage('test.txt', 123).toUint8Array()).toEqual(new Uint8Array([MessageType.FileInfo, 0x7b, 0, 0, 0, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74])); + + const expectedBytes_1 = new Uint8Array([MessageType.FileInfo, 0x7b, 0, 0, 0, ...utf8Bytes]); + expect(new FileInfoMessage(utf8Filename, 123).toUint8Array()).toEqual(expectedBytes_1); + + const expectedBytes_2 = new Uint8Array([MessageType.FileInfo, 0x15, 0xcd, 0x5b, 0x07, ...utf8Bytes]); + expect(new FileInfoMessage(utf8Filename, 123456789).toUint8Array()).toEqual(expectedBytes_2); + + const expectedBytes_3 = new Uint8Array([MessageType.FileInfo, 0xff, 0xff, 0xff, 0xff, ...utf8Bytes]); + expect(new FileInfoMessage(utf8Filename, 4294967295).toUint8Array()).toEqual(expectedBytes_3); + }); + }); + + describe('fromUint8Array', () => { + it('should return null if the message type is incorrect', () => { + expect(FileInfoMessage.fromUint8Array(new Uint8Array([MessageType.Ack, 0, 0, 0, 0, 0]))).toBeNull(); + }); + + it('should return null if the byte array is too short', () => { + expect(FileInfoMessage.fromUint8Array(new Uint8Array([MessageType.FileInfo, 0]))).toBeNull(); + expect(FileInfoMessage.fromUint8Array(new Uint8Array([MessageType.FileInfo, 0, 0, 0, 0]))).toBeNull(); + expect(FileInfoMessage.fromUint8Array(new Uint8Array([MessageType.FileInfo, 0x74, 0x65, 0x73, 0x00]))).toBeNull(); + }); + + it('should parse a byte array correctly', () => { + const fileInfoMessage_1 = FileInfoMessage.fromUint8Array(new Uint8Array([MessageType.FileInfo, 0x7b, 0, 0, 0, 0x74, 0x65, 0x73, 0x74])); + expect(fileInfoMessage_1).toEqual(new FileInfoMessage('test', 123)); + + const fileInfoMessage_2 = FileInfoMessage.fromUint8Array(new Uint8Array([MessageType.FileInfo, 0x15, 0xcd, 0x5b, 0x07, ...utf8Bytes])); + expect(fileInfoMessage_2).toEqual(new FileInfoMessage(utf8Filename, 123456789)); + }); + + it('should parse a message that was serialised by toUint8Array correctly', () => { + const fileInfoMessage_1 = new FileInfoMessage('test', 123); + expect(FileInfoMessage.fromUint8Array(fileInfoMessage_1.toUint8Array())).toEqual(fileInfoMessage_1); + + const fileInfoMessage_2 = new FileInfoMessage('test123.txt', 4294967295); + expect(FileInfoMessage.fromUint8Array(fileInfoMessage_2.toUint8Array())).toEqual(fileInfoMessage_2); + + const fileInfoMessage_3 = new FileInfoMessage(utf8Filename, 123456789); + expect(FileInfoMessage.fromUint8Array(fileInfoMessage_3.toUint8Array())).toEqual(fileInfoMessage_3); + }); + }); + +}); diff --git a/test/main.test.ts b/test/main.test.ts deleted file mode 100644 index 7b2fa11..0000000 --- a/test/main.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { MessageType, AckMessage, ChunkMessage } from '../src/main'; - - -describe('AckMessage', () => { - - describe('constructor', () => { - it('should set the correct type', () => { - expect(new AckMessage().type).toBe(MessageType.Ack); - }); - }); - - describe('toUint8Array', () => { - it('should return a byte array with the correct length', () => { - expect(new AckMessage().toUint8Array().length).toBe(1); - }); - - it('should serialise correctly', () => { - expect(new AckMessage().toUint8Array()).toEqual(new Uint8Array([MessageType.Ack])); - }); - }); - -}); - - -describe('ChunkMessage', () => { - - describe('constructor', () => { - it('should set the correct type', () => { - expect(new ChunkMessage(0, new Uint8Array([0])).type).toBe(MessageType.Chunk); - }); - - it('should set the correct chunk number', () => { - expect(new ChunkMessage(0, new Uint8Array([0])).chunkNumber).toBe(0); - expect(new ChunkMessage(1, new Uint8Array([0])).chunkNumber).toBe(1); - expect(new ChunkMessage(123, new Uint8Array([0])).chunkNumber).toBe(123); - }); - - it('should set the correct chunk bytes', () => { - expect(new ChunkMessage(0, new Uint8Array([])).chunkBytes).toEqual(new Uint8Array([])); - expect(new ChunkMessage(0, new Uint8Array([0])).chunkBytes).toEqual(new Uint8Array([0])); - expect(new ChunkMessage(0, new Uint8Array([0, 1, 2, 3])).chunkBytes).toEqual(new Uint8Array([0, 1, 2, 3])); - }); - }); - - describe('toUint8Array', () => { - it('should return a byte array with the correct length', () => { - expect(new ChunkMessage(0, new Uint8Array([])).toUint8Array().length).toBe(2); - expect(new ChunkMessage(0, new Uint8Array([0])).toUint8Array().length).toBe(3); - expect(new ChunkMessage(0, new Uint8Array([0, 1, 2, 3])).toUint8Array().length).toBe(6); - }); - - it('should serialise correctly', () => { - expect(new ChunkMessage(0, new Uint8Array([])).toUint8Array()).toEqual(new Uint8Array([MessageType.Chunk, 0])); - expect(new ChunkMessage(0, new Uint8Array([0])).toUint8Array()).toEqual(new Uint8Array([MessageType.Chunk, 0, 0])); - expect(new ChunkMessage(0, new Uint8Array([0, 1, 2, 3])).toUint8Array()).toEqual(new Uint8Array([MessageType.Chunk, 0, 0, 1, 2, 3])); - expect(new ChunkMessage(255, new Uint8Array([0, 1, 2, 3])).toUint8Array()).toEqual(new Uint8Array([MessageType.Chunk, 255, 0, 1, 2, 3])); - }); - }); - - describe('fromUint8Array', () => { - it('should parse correctly', () => { - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Chunk, 0]))).toEqual(new ChunkMessage(0, new Uint8Array([]))); - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Chunk, 0, 0]))).toEqual(new ChunkMessage(0, new Uint8Array([0]))); - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Chunk, 0, 0, 1, 2, 3]))).toEqual(new ChunkMessage(0, new Uint8Array([0, 1, 2, 3]))); - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Chunk, 255, 0, 1, 2, 3]))).toEqual(new ChunkMessage(255, new Uint8Array([0, 1, 2, 3]))); - }); - - it('should return null if the message type is not correct', () => { - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Ack, 0]))).toBeNull(); - }); - - it('should return null if the chunk size is different than expected', () => { - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Chunk, 0, 0, 0]), 1)).toBeNull(); - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Chunk, 0, 0, 0]), 3)).toBeNull(); - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Chunk, 0, 0, 0, 0, 0, 0]), 3)).toBeNull(); - expect(ChunkMessage.fromUint8Array(new Uint8Array([MessageType.Chunk, 0, 0, 0, 0, 0, 0]), 6)).toBeNull(); - }); - }); - -}); diff --git a/tsconfig.json b/tsconfig.json index 75e780c..fd8e504 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,8 @@ "module": "commonjs", "declaration": true, "outDir": "./lib", - "strict": true + "strict": true, + "sourceMap": true }, "include": ["src"], "exclude": ["node_modules"] From c4dc2a476b03d782c9cb4d805b70dc328bcb6bfc Mon Sep 17 00:00:00 2001 From: Matteo Paramatti Date: Sat, 24 Sep 2022 20:21:37 +0100 Subject: [PATCH 2/2] Force 32-bit uint endianness when sending and receiving --- src/AckMessage.ts | 1 - src/FileChunkMessage.ts | 1 - src/FileInfoMessage.ts | 11 ++++++++--- src/Message.ts | 1 - 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/AckMessage.ts b/src/AckMessage.ts index b0112e4..ce556cd 100644 --- a/src/AckMessage.ts +++ b/src/AckMessage.ts @@ -6,7 +6,6 @@ import { Message } from "./Message"; * * @extends Message */ - export class AckMessage extends Message { constructor() { diff --git a/src/FileChunkMessage.ts b/src/FileChunkMessage.ts index 731f59e..e88dd02 100644 --- a/src/FileChunkMessage.ts +++ b/src/FileChunkMessage.ts @@ -6,7 +6,6 @@ import { Message } from "./Message"; * * @extends Message */ - export class FileChunkMessage extends Message { /** diff --git a/src/FileInfoMessage.ts b/src/FileInfoMessage.ts index 05799e0..6b90358 100644 --- a/src/FileInfoMessage.ts +++ b/src/FileInfoMessage.ts @@ -42,16 +42,18 @@ export class FileInfoMessage extends Message { */ static fromUint8Array(byteArray: Uint8Array): FileInfoMessage | null { // Minimum size: 1 (type) + 4 (size) + 1 (name with at least one byte) - if (byteArray.length < 6) { + if (byteArray.length <= 5) { return null; } //TODO: Return null if size is too big + if (byteArray[0] !== MessageType.FileInfo) { return null; } - const size: number = new Uint32Array(byteArray.slice(1, 5).buffer)[0]; + // Size is received as a 32-bit unsigned integer in little endian + const size: number = new DataView(byteArray.slice(1, 5).buffer).getUint32(0, true); const name: string = Buffer.from(byteArray.slice(5)).toString('utf8'); return new FileInfoMessage(name, size); @@ -62,7 +64,10 @@ export class FileInfoMessage extends Message { */ toUint8Array(): Uint8Array { const nameBytes: Uint8Array = Buffer.from(this.name, 'utf8'); - const sizeBytes: Uint8Array = new Uint8Array(new Uint32Array([this.size]).buffer); + // Size is sent as a 32-bit unsigned integer in little endian + const sizeDataView = new DataView(new ArrayBuffer(4)); + sizeDataView.setUint32(0, this.size, true); + const sizeBytes: Uint8Array = new Uint8Array(sizeDataView.buffer); const byteArray = new Uint8Array(1 + 4 + nameBytes.byteLength); byteArray[0] = this.type; diff --git a/src/Message.ts b/src/Message.ts index dc90a01..7c844e6 100644 --- a/src/Message.ts +++ b/src/Message.ts @@ -3,7 +3,6 @@ import { MessageType } from './MessageType'; /** * An abstract class that represents a WebSocket message. */ - export abstract class Message { /**