UNPKG

5.55 kBJavaScriptView Raw
1import { BYTE } from './byte.js';
2/**
3 * Frame class represents a STOMP frame.
4 *
5 * @internal
6 */
7export class FrameImpl {
8 /**
9 * Frame constructor. `command`, `headers` and `body` are available as properties.
10 *
11 * @internal
12 */
13 constructor(params) {
14 const { command, headers, body, binaryBody, escapeHeaderValues, skipContentLengthHeader, } = params;
15 this.command = command;
16 this.headers = Object.assign({}, headers || {});
17 if (binaryBody) {
18 this._binaryBody = binaryBody;
19 this.isBinaryBody = true;
20 }
21 else {
22 this._body = body || '';
23 this.isBinaryBody = false;
24 }
25 this.escapeHeaderValues = escapeHeaderValues || false;
26 this.skipContentLengthHeader = skipContentLengthHeader || false;
27 }
28 /**
29 * body of the frame
30 */
31 get body() {
32 if (!this._body && this.isBinaryBody) {
33 this._body = new TextDecoder().decode(this._binaryBody);
34 }
35 return this._body || '';
36 }
37 /**
38 * body as Uint8Array
39 */
40 get binaryBody() {
41 if (!this._binaryBody && !this.isBinaryBody) {
42 this._binaryBody = new TextEncoder().encode(this._body);
43 }
44 // At this stage it will definitely have a valid value
45 return this._binaryBody;
46 }
47 /**
48 * deserialize a STOMP Frame from raw data.
49 *
50 * @internal
51 */
52 static fromRawFrame(rawFrame, escapeHeaderValues) {
53 const headers = {};
54 const trim = (str) => str.replace(/^\s+|\s+$/g, '');
55 // In case of repeated headers, as per standards, first value need to be used
56 for (const header of rawFrame.headers.reverse()) {
57 const idx = header.indexOf(':');
58 const key = trim(header[0]);
59 let value = trim(header[1]);
60 if (escapeHeaderValues &&
61 rawFrame.command !== 'CONNECT' &&
62 rawFrame.command !== 'CONNECTED') {
63 value = FrameImpl.hdrValueUnEscape(value);
64 }
65 headers[key] = value;
66 }
67 return new FrameImpl({
68 command: rawFrame.command,
69 headers,
70 binaryBody: rawFrame.binaryBody,
71 escapeHeaderValues,
72 });
73 }
74 /**
75 * @internal
76 */
77 toString() {
78 return this.serializeCmdAndHeaders();
79 }
80 /**
81 * serialize this Frame in a format suitable to be passed to WebSocket.
82 * If the body is string the output will be string.
83 * If the body is binary (i.e. of type Unit8Array) it will be serialized to ArrayBuffer.
84 *
85 * @internal
86 */
87 serialize() {
88 const cmdAndHeaders = this.serializeCmdAndHeaders();
89 if (this.isBinaryBody) {
90 return FrameImpl.toUnit8Array(cmdAndHeaders, this._binaryBody).buffer;
91 }
92 else {
93 return cmdAndHeaders + this._body + BYTE.NULL;
94 }
95 }
96 serializeCmdAndHeaders() {
97 const lines = [this.command];
98 if (this.skipContentLengthHeader) {
99 delete this.headers['content-length'];
100 }
101 for (const name of Object.keys(this.headers || {})) {
102 const value = this.headers[name];
103 if (this.escapeHeaderValues &&
104 this.command !== 'CONNECT' &&
105 this.command !== 'CONNECTED') {
106 lines.push(`${name}:${FrameImpl.hdrValueEscape(`${value}`)}`);
107 }
108 else {
109 lines.push(`${name}:${value}`);
110 }
111 }
112 if (this.isBinaryBody ||
113 (!this.isBodyEmpty() && !this.skipContentLengthHeader)) {
114 lines.push(`content-length:${this.bodyLength()}`);
115 }
116 return lines.join(BYTE.LF) + BYTE.LF + BYTE.LF;
117 }
118 isBodyEmpty() {
119 return this.bodyLength() === 0;
120 }
121 bodyLength() {
122 const binaryBody = this.binaryBody;
123 return binaryBody ? binaryBody.length : 0;
124 }
125 /**
126 * Compute the size of a UTF-8 string by counting its number of bytes
127 * (and not the number of characters composing the string)
128 */
129 static sizeOfUTF8(s) {
130 return s ? new TextEncoder().encode(s).length : 0;
131 }
132 static toUnit8Array(cmdAndHeaders, binaryBody) {
133 const uint8CmdAndHeaders = new TextEncoder().encode(cmdAndHeaders);
134 const nullTerminator = new Uint8Array([0]);
135 const uint8Frame = new Uint8Array(uint8CmdAndHeaders.length + binaryBody.length + nullTerminator.length);
136 uint8Frame.set(uint8CmdAndHeaders);
137 uint8Frame.set(binaryBody, uint8CmdAndHeaders.length);
138 uint8Frame.set(nullTerminator, uint8CmdAndHeaders.length + binaryBody.length);
139 return uint8Frame;
140 }
141 /**
142 * Serialize a STOMP frame as per STOMP standards, suitable to be sent to the STOMP broker.
143 *
144 * @internal
145 */
146 static marshall(params) {
147 const frame = new FrameImpl(params);
148 return frame.serialize();
149 }
150 /**
151 * Escape header values
152 */
153 static hdrValueEscape(str) {
154 return str
155 .replace(/\\/g, '\\\\')
156 .replace(/\r/g, '\\r')
157 .replace(/\n/g, '\\n')
158 .replace(/:/g, '\\c');
159 }
160 /**
161 * UnEscape header values
162 */
163 static hdrValueUnEscape(str) {
164 return str
165 .replace(/\\r/g, '\r')
166 .replace(/\\n/g, '\n')
167 .replace(/\\c/g, ':')
168 .replace(/\\\\/g, '\\');
169 }
170}
171//# sourceMappingURL=frame-impl.js.map
\No newline at end of file