121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
|
|
function splitAt(input: string, searchString: string, position: number): [string, number] {
|
|
let index = input.indexOf(searchString, position);
|
|
if (index < 0) {
|
|
return [input.substr(position), input.length];
|
|
}
|
|
let left = input.substring(position, index);
|
|
return [left, index + searchString.length];
|
|
}
|
|
|
|
export namespace TextMessageFormat {
|
|
const InvalidPayloadError = new Error("Invalid text message payload");
|
|
const LengthRegex = /^[0-9]+$/;
|
|
|
|
function hasSpace(input: string, offset: number, length: number): boolean {
|
|
let requiredLength = offset + length;
|
|
return input.length >= requiredLength;
|
|
}
|
|
|
|
function parseMessage(input: string, position: number): [number, string] {
|
|
var offset = position;
|
|
|
|
// Read the length
|
|
var [lenStr, offset] = splitAt(input, ":", offset);
|
|
|
|
// parseInt is too leniant, we need a strict check to see if the string is an int
|
|
|
|
if (!LengthRegex.test(lenStr)) {
|
|
throw new Error(`Invalid length: '${lenStr}'`);
|
|
}
|
|
let length = Number.parseInt(lenStr);
|
|
|
|
// Required space is: (";") + length (payload len)
|
|
if (!hasSpace(input, offset, 1 + length)) {
|
|
throw new Error("Message is incomplete");
|
|
}
|
|
|
|
// Read the payload
|
|
var payload = input.substr(offset, length);
|
|
offset += length;
|
|
|
|
// Verify the final trailing character
|
|
if (input[offset] != ';') {
|
|
throw new Error("Message missing trailer character");
|
|
}
|
|
offset += 1;
|
|
|
|
return [offset, payload];
|
|
}
|
|
|
|
export function write(output: string): string {
|
|
return `${output.length}:${output};`;
|
|
}
|
|
|
|
export function parse(input: string): string[] {
|
|
if (input.length == 0) {
|
|
return []
|
|
}
|
|
|
|
let messages = [];
|
|
var offset = 0;
|
|
while (offset < input.length) {
|
|
let message;
|
|
[offset, message] = parseMessage(input, offset);
|
|
messages.push(message);
|
|
}
|
|
return messages;
|
|
}
|
|
}
|
|
|
|
export namespace BinaryMessageFormat {
|
|
export function write(output: Uint8Array): ArrayBuffer {
|
|
let size = output.byteLength;
|
|
let buffer = new Uint8Array(size + 8);
|
|
|
|
// javascript bitwise operators only support 32-bit integers
|
|
for (let i = 7; i >= 4; i--) {
|
|
buffer[i] = size & 0xff;
|
|
size = size >> 8;
|
|
}
|
|
|
|
buffer.set(output, 8);
|
|
|
|
return buffer.buffer;
|
|
}
|
|
|
|
export function parse(input: ArrayBuffer): Uint8Array[] {
|
|
let result: Uint8Array[] = [];
|
|
let uint8Array = new Uint8Array(input);
|
|
// 8 - the length prefix size
|
|
for (let offset = 0; offset < input.byteLength;) {
|
|
|
|
if (input.byteLength < offset + 8) {
|
|
throw new Error("Cannot read message size")
|
|
}
|
|
|
|
// Note javascript bitwise operators only support 32-bit integers - for now cutting bigger messages.
|
|
// Tracking bug https://github.com/aspnet/SignalR/issues/613
|
|
if (!(uint8Array[offset] == 0 && uint8Array[offset + 1] == 0 && uint8Array[offset + 2] == 0
|
|
&& uint8Array[offset + 3] == 0 && (uint8Array[offset + 4] & 0x80) == 0)) {
|
|
throw new Error("Messages bigger than 2147483647 bytes are not supported");
|
|
}
|
|
|
|
let size = 0;
|
|
for (let i = 4; i < 8; i++) {
|
|
size = (size << 8) | uint8Array[offset + i];
|
|
}
|
|
|
|
if (uint8Array.byteLength >= (offset + 8 + size)) {
|
|
result.push(uint8Array.slice(offset + 8, offset + 8 + size))
|
|
}
|
|
else {
|
|
throw new Error("Incomplete message");
|
|
}
|
|
|
|
offset = offset + 8 + size;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
} |