170 lines
5.3 KiB
JavaScript
170 lines
5.3 KiB
JavaScript
import { BYTE } from './byte';
|
|
/**
|
|
* Frame class represents a STOMP frame.
|
|
*
|
|
* @internal
|
|
*/
|
|
export class FrameImpl {
|
|
/**
|
|
* Frame constructor. `command`, `headers` and `body` are available as properties.
|
|
*
|
|
* @internal
|
|
*/
|
|
constructor(params) {
|
|
const { command, headers, body, binaryBody, escapeHeaderValues, skipContentLengthHeader, } = params;
|
|
this.command = command;
|
|
this.headers = Object.assign({}, headers || {});
|
|
if (binaryBody) {
|
|
this._binaryBody = binaryBody;
|
|
this.isBinaryBody = true;
|
|
}
|
|
else {
|
|
this._body = body || '';
|
|
this.isBinaryBody = false;
|
|
}
|
|
this.escapeHeaderValues = escapeHeaderValues || false;
|
|
this.skipContentLengthHeader = skipContentLengthHeader || false;
|
|
}
|
|
/**
|
|
* body of the frame
|
|
*/
|
|
get body() {
|
|
if (!this._body && this.isBinaryBody) {
|
|
this._body = new TextDecoder().decode(this._binaryBody);
|
|
}
|
|
return this._body;
|
|
}
|
|
/**
|
|
* body as Uint8Array
|
|
*/
|
|
get binaryBody() {
|
|
if (!this._binaryBody && !this.isBinaryBody) {
|
|
this._binaryBody = new TextEncoder().encode(this._body);
|
|
}
|
|
return this._binaryBody;
|
|
}
|
|
/**
|
|
* deserialize a STOMP Frame from raw data.
|
|
*
|
|
* @internal
|
|
*/
|
|
static fromRawFrame(rawFrame, escapeHeaderValues) {
|
|
const headers = {};
|
|
const trim = (str) => str.replace(/^\s+|\s+$/g, '');
|
|
// In case of repeated headers, as per standards, first value need to be used
|
|
for (const header of rawFrame.headers.reverse()) {
|
|
const idx = header.indexOf(':');
|
|
const key = trim(header[0]);
|
|
let value = trim(header[1]);
|
|
if (escapeHeaderValues &&
|
|
rawFrame.command !== 'CONNECT' &&
|
|
rawFrame.command !== 'CONNECTED') {
|
|
value = FrameImpl.hdrValueUnEscape(value);
|
|
}
|
|
headers[key] = value;
|
|
}
|
|
return new FrameImpl({
|
|
command: rawFrame.command,
|
|
headers,
|
|
binaryBody: rawFrame.binaryBody,
|
|
escapeHeaderValues,
|
|
});
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
toString() {
|
|
return this.serializeCmdAndHeaders();
|
|
}
|
|
/**
|
|
* serialize this Frame in a format suitable to be passed to WebSocket.
|
|
* If the body is string the output will be string.
|
|
* If the body is binary (i.e. of type Unit8Array) it will be serialized to ArrayBuffer.
|
|
*
|
|
* @internal
|
|
*/
|
|
serialize() {
|
|
const cmdAndHeaders = this.serializeCmdAndHeaders();
|
|
if (this.isBinaryBody) {
|
|
return FrameImpl.toUnit8Array(cmdAndHeaders, this._binaryBody).buffer;
|
|
}
|
|
else {
|
|
return cmdAndHeaders + this._body + BYTE.NULL;
|
|
}
|
|
}
|
|
serializeCmdAndHeaders() {
|
|
const lines = [this.command];
|
|
if (this.skipContentLengthHeader) {
|
|
delete this.headers['content-length'];
|
|
}
|
|
for (const name of Object.keys(this.headers || {})) {
|
|
const value = this.headers[name];
|
|
if (this.escapeHeaderValues &&
|
|
this.command !== 'CONNECT' &&
|
|
this.command !== 'CONNECTED') {
|
|
lines.push(`${name}:${FrameImpl.hdrValueEscape(`${value}`)}`);
|
|
}
|
|
else {
|
|
lines.push(`${name}:${value}`);
|
|
}
|
|
}
|
|
if (this.isBinaryBody ||
|
|
(!this.isBodyEmpty() && !this.skipContentLengthHeader)) {
|
|
lines.push(`content-length:${this.bodyLength()}`);
|
|
}
|
|
return lines.join(BYTE.LF) + BYTE.LF + BYTE.LF;
|
|
}
|
|
isBodyEmpty() {
|
|
return this.bodyLength() === 0;
|
|
}
|
|
bodyLength() {
|
|
const binaryBody = this.binaryBody;
|
|
return binaryBody ? binaryBody.length : 0;
|
|
}
|
|
/**
|
|
* Compute the size of a UTF-8 string by counting its number of bytes
|
|
* (and not the number of characters composing the string)
|
|
*/
|
|
static sizeOfUTF8(s) {
|
|
return s ? new TextEncoder().encode(s).length : 0;
|
|
}
|
|
static toUnit8Array(cmdAndHeaders, binaryBody) {
|
|
const uint8CmdAndHeaders = new TextEncoder().encode(cmdAndHeaders);
|
|
const nullTerminator = new Uint8Array([0]);
|
|
const uint8Frame = new Uint8Array(uint8CmdAndHeaders.length + binaryBody.length + nullTerminator.length);
|
|
uint8Frame.set(uint8CmdAndHeaders);
|
|
uint8Frame.set(binaryBody, uint8CmdAndHeaders.length);
|
|
uint8Frame.set(nullTerminator, uint8CmdAndHeaders.length + binaryBody.length);
|
|
return uint8Frame;
|
|
}
|
|
/**
|
|
* Serialize a STOMP frame as per STOMP standards, suitable to be sent to the STOMP broker.
|
|
*
|
|
* @internal
|
|
*/
|
|
static marshall(params) {
|
|
const frame = new FrameImpl(params);
|
|
return frame.serialize();
|
|
}
|
|
/**
|
|
* Escape header values
|
|
*/
|
|
static hdrValueEscape(str) {
|
|
return str
|
|
.replace(/\\/g, '\\\\')
|
|
.replace(/\r/g, '\\r')
|
|
.replace(/\n/g, '\\n')
|
|
.replace(/:/g, '\\c');
|
|
}
|
|
/**
|
|
* UnEscape header values
|
|
*/
|
|
static hdrValueUnEscape(str) {
|
|
return str
|
|
.replace(/\\r/g, '\r')
|
|
.replace(/\\n/g, '\n')
|
|
.replace(/\\c/g, ':')
|
|
.replace(/\\\\/g, '\\');
|
|
}
|
|
}
|
|
//# sourceMappingURL=frame-impl.js.map
|