UNPKG

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