netkitty
Version:
Network tools kit
357 lines (356 loc) • 11.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseHeader = void 0;
const SortPostHandlers_1 = require("../lib/SortPostHandlers");
const FlexibleObject_1 = require("../lib/FlexibleObject");
const ajv_1 = require("ajv");
const CodecSchemaValidateError_1 = require("../../../errors/CodecSchemaValidateError");
const CONSTRUCTOR_VALIDATE_KEY = '__validate';
class BaseHeader {
static get CODEC_INSTANCE() {
return new this();
}
static CREATE_CODEC_INSTANCE_WITCH_CODEC_MODULES(codecData, codecModules) {
return this.CREATE_INSTANCE(codecData, codecModules);
}
static get PROTOCOL_ID() {
return this.CODEC_INSTANCE.id;
}
static get PROTOCOL_NAME() {
return this.CODEC_INSTANCE.name;
}
static get PROTOCOL_SCHEMA() {
const schema = JSON.parse(JSON.stringify(this.CODEC_INSTANCE.SCHEMA));
if (!Object.hasOwn(this, CONSTRUCTOR_VALIDATE_KEY)) {
const validate = new ajv_1.Ajv({
strict: false,
useDefaults: true,
coerceTypes: true
}).compile(schema);
Object.defineProperty(this, CONSTRUCTOR_VALIDATE_KEY, {
enumerable: false,
configurable: false,
value: validate
});
}
return schema;
}
static MATCH(codecData, codecModules) {
return this.CREATE_CODEC_INSTANCE_WITCH_CODEC_MODULES(codecData, codecModules ? codecModules : []).match();
}
static CREATE_INSTANCE(codecData, codecModules) {
return new this(codecData, codecModules);
}
/**
* Entire packet buffer data getter
*/
get packet() {
return this.codecData.packet;
}
/**
* Entire packet buffer data setter
* @param packet
*/
set packet(packet) {
this.codecData.packet = packet;
}
/**
* The end position of this header in the entire packet's buffer
*/
get endPos() {
return this.startPos + this.headerLength;
}
/**
* Readonly Header length
*/
get length() {
return this.headerLength;
}
/**
* Post packet handlers
* @protected
*/
get postPacketHandlers() {
if (this.codecData.postHandlers[this.headerIndex] === undefined)
this.codecData.postHandlers[this.headerIndex] = [];
return this.codecData.postHandlers[this.headerIndex];
}
constructor(codecData, codecModules) {
/**
* Current header is a protocol or not
*/
this.isProtocol = true;
/**
* Encode/Decode error info objects
*/
this.errors = [];
/**
* Header schema instance
*/
this.instance = new FlexibleObject_1.FlexibleObject();
/**
* The start position of this header in the entire packet's buffer
*/
this.startPos = 0;
/**
* Header length
* @protected
*/
this.headerLength = 0;
/**
* Current header index in packet headers
* @protected
*/
this.headerIndex = 0;
/**
* Registered post encode handlers (CodecModule)
* @protected
*/
this.postSelfEncodeHandlers = [];
/**
* Registered post decode handlers (CodecModule)
* @protected
*/
this.postSelfDecodeHandlers = [];
this.codecData = codecData;
this.startPos = codecData?.startPos ? codecData.startPos : 0;
codecModules = codecModules ? codecModules : [];
this.codecModules = codecModules;
this.prevCodecModules = [...codecModules];
const prevCodecModuleIndex = this.prevCodecModules.length - 1;
this.prevCodecModule = this.prevCodecModules[prevCodecModuleIndex > -1 ? prevCodecModuleIndex : 0];
this.headerIndex = this.prevCodecModules.length;
}
/**
* Get packet data offset
* @param offset
* @protected
*/
getPacketOffset(offset) {
return this.startPos + offset;
}
/**
* Internal read bytes from buffer
* @param offset
* @param length
* @param expandBufferLength
* @param changeHeaderLength
* @private
*/
#readBytes(offset, length, expandBufferLength, changeHeaderLength) {
const packetOffset = this.getPacketOffset(offset);
let readEndPos = packetOffset + length;
if (this.packet.length < readEndPos) {
if (expandBufferLength) {
this.packet = Buffer.concat([this.packet, Buffer.alloc(readEndPos - this.packet.length, 0)]);
}
else {
readEndPos = this.packet.length;
}
}
const headerLength = readEndPos - this.startPos;
if (changeHeaderLength)
this.headerLength = this.headerLength < headerLength ? headerLength : this.headerLength;
return this.packet.subarray(packetOffset, packetOffset + length);
}
/**
* Read bytes from buffer
* @param offset
* @param length
* @param dryRun
* @protected
*/
readBytes(offset, length, dryRun = false) {
return this.#readBytes(offset, length, false, !dryRun);
}
/**
* Write bytes to buffer
* @param offset
* @param buffer
* @protected
*/
writeBytes(offset, buffer) {
const packetOffset = this.getPacketOffset(offset);
const writeEndPos = packetOffset + buffer.length;
this.#readBytes(offset, buffer.length, true, true);
this.packet.fill(buffer, packetOffset, writeEndPos);
}
/**
* Read bits from buffer
* @param offset
* @param length
* @param bitOffset
* @param bitLength
* @protected
*/
readBits(offset, length, bitOffset, bitLength) {
const buffer = this.#readBytes(offset, length, false, true);
const bitString = parseInt(buffer.toString('hex'), 16).toString(2).padStart(length * 8, '0');
return parseInt(bitString.substring(bitOffset, bitOffset + bitLength), 2);
}
/**
* Write bits to buffer
* @param offset
* @param length
* @param bitOffset
* @param bitLength
* @param value
* @protected
*/
writeBits(offset, length, bitOffset, bitLength, value) {
const buffer = this.#readBytes(offset, length, true, true);
let bitArray = Array.from(parseInt(buffer.toString('hex'), 16).toString(2).padStart(buffer.length * 8, '0'));
const valueBitArray = Array.from(value.toString(2).padStart(bitLength, '0'));
bitArray = bitArray.map((bit, index) => {
if (index < bitOffset)
return bit;
if (index >= (bitOffset + bitLength))
return bit;
return valueBitArray[index - bitOffset];
});
this.writeBytes(offset, Buffer.from(parseInt(bitArray.join(''), 2).toString(16).padStart(buffer.length * 2, '0'), 'hex'));
}
/**
* Get field codecs from schema tree
* @param schema
* @param codecName
* @param execBeforeSubCodecs
* @param codecs
* @protected
*/
getFieldCodecs(schema, codecName, execBeforeSubCodecs, codecs = []) {
if (!schema.properties)
return codecs;
for (const propertyName of Object.keys(schema.properties)) {
const fieldSchema = schema.properties[propertyName];
let codec = fieldSchema[codecName];
if (!codec)
codec = async () => (void (0));
if (execBeforeSubCodecs)
codecs.push(async () => await codec());
if (fieldSchema.properties)
this.getFieldCodecs(fieldSchema, codecName, execBeforeSubCodecs, codecs);
if (!execBeforeSubCodecs)
codecs.push(async () => await codec());
}
return codecs;
}
/**
* Record encode/decode error
* @param path
* @param message
* @protected
*/
recordError(path, message) {
this.errors.push({
id: this.id,
path: path,
message: message
});
}
/**
* Register post encode handler for current codec
* @param handler
* @param priority
* @protected
*/
addPostSelfEncodeHandler(handler, priority = 0) {
this.postSelfEncodeHandlers.push({
priority: priority,
handler: handler
});
}
/**
* Register post decode handler for current codec
* @param handler
* @param priority
* @protected
*/
addPostSelfDecodeHandler(handler, priority = 0) {
this.postSelfDecodeHandlers.push({
priority: priority,
handler: handler
});
}
/**
* Register post encode handler for packet
* @description Registered handler call sequence: LIFO (Last In First Out)
* @param handler
* @param priority
* @protected
*/
addPostPacketEncodeHandler(handler, priority = 0) {
this.postPacketHandlers.push({
priority: priority,
handler: handler
});
}
/**
* Register post decode handler for packet
* @description Registered handler call sequence: FIFO (First In First Out)
* @param handler
* @param priority
* @protected
*/
addPostPacketDecodeHandler(handler, priority = 0) {
this.postPacketHandlers.push({
priority: priority,
handler: handler
});
}
/**
* Validate input json node is valid
* @param headerTreeNode
*/
validate(headerTreeNode) {
let validate = this.constructor[CONSTRUCTOR_VALIDATE_KEY];
if (!validate) {
validate = new ajv_1.Ajv({
strict: false,
useDefaults: true,
coerceTypes: true
}).compile(this.SCHEMA);
this.constructor[CONSTRUCTOR_VALIDATE_KEY] = validate;
}
const isValid = validate(headerTreeNode);
if (!isValid) {
let errorObject;
if (validate.errors)
errorObject = validate.errors[0];
const errorMessage = errorObject?.message ? errorObject.message : 'Unknown Error';
throw new CodecSchemaValidateError_1.CodecSchemaValidateError(errorMessage);
}
return headerTreeNode;
}
/**
* Decode packet header field by field
*/
async decode() {
const decodes = this.getFieldCodecs(this.SCHEMA, 'decode', true);
for (const decode of decodes) {
await decode();
}
const postSelfDecodeHandlers = (0, SortPostHandlers_1.SortPostHandlers)(this.postSelfDecodeHandlers);
let postDecodeHandler = postSelfDecodeHandlers.shift();
while (postDecodeHandler) {
await postDecodeHandler.handler();
postDecodeHandler = postSelfDecodeHandlers.shift();
}
}
/**
* Encode packet header field by field
*/
async encode() {
const encodes = this.getFieldCodecs(this.SCHEMA, 'encode', false);
for (const encode of encodes) {
await encode();
}
const postSelfEncodeHandlers = (0, SortPostHandlers_1.SortPostHandlers)(this.postSelfEncodeHandlers);
let postEncodeHandler = postSelfEncodeHandlers.shift();
while (postEncodeHandler) {
await postEncodeHandler.handler();
postEncodeHandler = postSelfEncodeHandlers.shift();
}
}
}
exports.BaseHeader = BaseHeader;