@stomp/stompjs 6.1.2 → 7.0.0-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/bundles/stomp.umd.js +1724 -2023
  2. package/bundles/stomp.umd.js.map +1 -1
  3. package/bundles/stomp.umd.min.js +1 -2
  4. package/esm6/augment-websocket.d.ts +1 -1
  5. package/esm6/augment-websocket.js +4 -3
  6. package/esm6/augment-websocket.js.map +1 -1
  7. package/esm6/client.d.ts +35 -19
  8. package/esm6/client.js +176 -132
  9. package/esm6/client.js.map +1 -1
  10. package/esm6/compatibility/compat-client.d.ts +29 -23
  11. package/esm6/compatibility/compat-client.js +2 -2
  12. package/esm6/compatibility/compat-client.js.map +1 -1
  13. package/esm6/compatibility/heartbeat-info.d.ts +5 -3
  14. package/esm6/compatibility/stomp.d.ts +1 -1
  15. package/esm6/compatibility/stomp.js +2 -2
  16. package/esm6/compatibility/stomp.js.map +1 -1
  17. package/esm6/frame-impl.d.ts +5 -5
  18. package/esm6/frame-impl.js +3 -2
  19. package/esm6/frame-impl.js.map +1 -1
  20. package/esm6/i-frame.d.ts +1 -1
  21. package/esm6/i-frame.js +1 -0
  22. package/esm6/i-message.d.ts +2 -2
  23. package/esm6/i-message.js +1 -0
  24. package/esm6/i-transaction.js +1 -0
  25. package/esm6/index.d.ts +13 -13
  26. package/esm6/index.js +13 -10
  27. package/esm6/index.js.map +1 -1
  28. package/esm6/parser.d.ts +1 -1
  29. package/esm6/parser.js +10 -2
  30. package/esm6/parser.js.map +1 -1
  31. package/esm6/stomp-config.d.ts +3 -3
  32. package/esm6/stomp-handler.d.ts +10 -13
  33. package/esm6/stomp-handler.js +35 -17
  34. package/esm6/stomp-handler.js.map +1 -1
  35. package/esm6/stomp-subscription.d.ts +2 -2
  36. package/esm6/stomp-subscription.js +1 -7
  37. package/esm6/stomp-subscription.js.map +1 -1
  38. package/esm6/types.d.ts +31 -5
  39. package/esm6/types.js +2 -2
  40. package/esm6/types.js.map +1 -1
  41. package/esm6/versions.js +2 -2
  42. package/index.d.ts +1 -1
  43. package/package.json +28 -25
  44. package/src/augment-websocket.ts +5 -4
  45. package/src/client.ts +81 -35
  46. package/src/compatibility/compat-client.ts +4 -4
  47. package/src/compatibility/heartbeat-info.ts +1 -1
  48. package/src/compatibility/stomp.ts +3 -3
  49. package/src/frame-impl.ts +14 -10
  50. package/src/i-frame.ts +1 -1
  51. package/src/i-message.ts +2 -2
  52. package/src/index.ts +13 -13
  53. package/src/parser.ts +18 -6
  54. package/src/stomp-config.ts +3 -3
  55. package/src/stomp-handler.ts +47 -31
  56. package/src/stomp-subscription.ts +4 -4
  57. package/src/types.ts +32 -5
  58. package/src/versions.ts +2 -2
  59. package/bundles/stomp.umd.min.js.map +0 -1
@@ -1,2170 +1,1871 @@
1
- (function webpackUniversalModuleDefinition(root, factory) {
2
- if(typeof exports === 'object' && typeof module === 'object')
3
- module.exports = factory();
4
- else if(typeof define === 'function' && define.amd)
5
- define("StompJs", [], factory);
6
- else if(typeof exports === 'object')
7
- exports["StompJs"] = factory();
8
- else
9
- root["StompJs"] = factory();
10
- })(typeof self !== 'undefined' ? self : this, function() {
11
- return /******/ (function(modules) { // webpackBootstrap
12
- /******/ // The module cache
13
- /******/ var installedModules = {};
14
- /******/
15
- /******/ // The require function
16
- /******/ function __webpack_require__(moduleId) {
17
- /******/
18
- /******/ // Check if module is in cache
19
- /******/ if(installedModules[moduleId]) {
20
- /******/ return installedModules[moduleId].exports;
21
- /******/ }
22
- /******/ // Create a new module (and put it into the cache)
23
- /******/ var module = installedModules[moduleId] = {
24
- /******/ i: moduleId,
25
- /******/ l: false,
26
- /******/ exports: {}
27
- /******/ };
28
- /******/
29
- /******/ // Execute the module function
30
- /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31
- /******/
32
- /******/ // Flag the module as loaded
33
- /******/ module.l = true;
34
- /******/
35
- /******/ // Return the exports of the module
36
- /******/ return module.exports;
37
- /******/ }
38
- /******/
39
- /******/
40
- /******/ // expose the modules object (__webpack_modules__)
41
- /******/ __webpack_require__.m = modules;
42
- /******/
43
- /******/ // expose the module cache
44
- /******/ __webpack_require__.c = installedModules;
45
- /******/
46
- /******/ // define getter function for harmony exports
47
- /******/ __webpack_require__.d = function(exports, name, getter) {
48
- /******/ if(!__webpack_require__.o(exports, name)) {
49
- /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
50
- /******/ }
51
- /******/ };
52
- /******/
53
- /******/ // define __esModule on exports
54
- /******/ __webpack_require__.r = function(exports) {
55
- /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
56
- /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
57
- /******/ }
58
- /******/ Object.defineProperty(exports, '__esModule', { value: true });
59
- /******/ };
60
- /******/
61
- /******/ // create a fake namespace object
62
- /******/ // mode & 1: value is a module id, require it
63
- /******/ // mode & 2: merge all properties of value into the ns
64
- /******/ // mode & 4: return value when already ns object
65
- /******/ // mode & 8|1: behave like require
66
- /******/ __webpack_require__.t = function(value, mode) {
67
- /******/ if(mode & 1) value = __webpack_require__(value);
68
- /******/ if(mode & 8) return value;
69
- /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
70
- /******/ var ns = Object.create(null);
71
- /******/ __webpack_require__.r(ns);
72
- /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
73
- /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
74
- /******/ return ns;
75
- /******/ };
76
- /******/
77
- /******/ // getDefaultExport function for compatibility with non-harmony modules
78
- /******/ __webpack_require__.n = function(module) {
79
- /******/ var getter = module && module.__esModule ?
80
- /******/ function getDefault() { return module['default']; } :
81
- /******/ function getModuleExports() { return module; };
82
- /******/ __webpack_require__.d(getter, 'a', getter);
83
- /******/ return getter;
84
- /******/ };
85
- /******/
86
- /******/ // Object.prototype.hasOwnProperty.call
87
- /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
88
- /******/
89
- /******/ // __webpack_public_path__
90
- /******/ __webpack_require__.p = "";
91
- /******/
92
- /******/
93
- /******/ // Load entry module and return exports
94
- /******/ return __webpack_require__(__webpack_require__.s = 0);
95
- /******/ })
96
- /************************************************************************/
97
- /******/ ({
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.StompJs = {}));
5
+ })(this, (function (exports) { 'use strict';
98
6
 
99
- /***/ "./src/augment-websocket.ts":
100
- /*!**********************************!*\
101
- !*** ./src/augment-websocket.ts ***!
102
- \**********************************/
103
- /*! exports provided: augmentWebsocket */
104
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
105
-
106
- "use strict";
107
- __webpack_require__.r(__webpack_exports__);
108
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "augmentWebsocket", function() { return augmentWebsocket; });
109
- /**
110
- * @internal
111
- */
112
- function augmentWebsocket(webSocket, debug) {
113
- webSocket.terminate = function () {
114
- const noOp = () => { };
115
- // set all callbacks to no op
116
- this.onerror = noOp;
117
- this.onmessage = noOp;
118
- this.onopen = noOp;
119
- const ts = new Date();
120
- const origOnClose = this.onclose;
121
- // Track delay in actual closure of the socket
122
- this.onclose = closeEvent => {
123
- const delay = new Date().getTime() - ts.getTime();
124
- debug(`Discarded socket closed after ${delay}ms, with code/reason: ${closeEvent.code}/${closeEvent.reason}`);
125
- };
126
- this.close();
127
- origOnClose.call(this, {
128
- code: 4001,
129
- reason: 'Heartbeat failure, discarding the socket',
130
- wasClean: false,
131
- });
7
+ /**
8
+ * Some byte values, used as per STOMP specifications.
9
+ *
10
+ * Part of `@stomp/stompjs`.
11
+ *
12
+ * @internal
13
+ */
14
+ const BYTE = {
15
+ // LINEFEED byte (octet 10)
16
+ LF: '\x0A',
17
+ // NULL byte (octet 0)
18
+ NULL: '\x00',
132
19
  };
133
- }
134
-
135
-
136
- /***/ }),
137
-
138
- /***/ "./src/byte.ts":
139
- /*!*********************!*\
140
- !*** ./src/byte.ts ***!
141
- \*********************/
142
- /*! exports provided: BYTE */
143
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
144
-
145
- "use strict";
146
- __webpack_require__.r(__webpack_exports__);
147
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BYTE", function() { return BYTE; });
148
- /**
149
- * Some byte values, used as per STOMP specifications.
150
- *
151
- * Part of `@stomp/stompjs`.
152
- *
153
- * @internal
154
- */
155
- const BYTE = {
156
- // LINEFEED byte (octet 10)
157
- LF: '\x0A',
158
- // NULL byte (octet 0)
159
- NULL: '\x00',
160
- };
161
-
162
-
163
- /***/ }),
164
-
165
- /***/ "./src/client.ts":
166
- /*!***********************!*\
167
- !*** ./src/client.ts ***!
168
- \***********************/
169
- /*! exports provided: Client */
170
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
171
-
172
- "use strict";
173
- __webpack_require__.r(__webpack_exports__);
174
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Client", function() { return Client; });
175
- /* harmony import */ var _stomp_handler__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./stomp-handler */ "./src/stomp-handler.ts");
176
- /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./types */ "./src/types.ts");
177
- /* harmony import */ var _versions__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./versions */ "./src/versions.ts");
178
- var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
179
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
180
- return new (P || (P = Promise))(function (resolve, reject) {
181
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
182
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
183
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
184
- step((generator = generator.apply(thisArg, _arguments || [])).next());
185
- });
186
- };
187
-
188
20
 
189
-
190
- /**
191
- * STOMP Client Class.
192
- *
193
- * Part of `@stomp/stompjs`.
194
- */
195
- class Client {
196
21
  /**
197
- * Create an instance.
22
+ * Frame class represents a STOMP frame.
23
+ *
24
+ * @internal
198
25
  */
199
- constructor(conf = {}) {
26
+ class FrameImpl {
200
27
  /**
201
- * STOMP versions to attempt during STOMP handshake. By default versions `1.0`, `1.1`, and `1.2` are attempted.
28
+ * Frame constructor. `command`, `headers` and `body` are available as properties.
202
29
  *
203
- * Example:
204
- * ```javascript
205
- * // Try only versions 1.0 and 1.1
206
- * client.stompVersions = new Versions(['1.0', '1.1'])
207
- * ```
30
+ * @internal
208
31
  */
209
- this.stompVersions = _versions__WEBPACK_IMPORTED_MODULE_2__["Versions"].default;
32
+ constructor(params) {
33
+ const { command, headers, body, binaryBody, escapeHeaderValues, skipContentLengthHeader, } = params;
34
+ this.command = command;
35
+ this.headers = Object.assign({}, headers || {});
36
+ if (binaryBody) {
37
+ this._binaryBody = binaryBody;
38
+ this.isBinaryBody = true;
39
+ }
40
+ else {
41
+ this._body = body || '';
42
+ this.isBinaryBody = false;
43
+ }
44
+ this.escapeHeaderValues = escapeHeaderValues || false;
45
+ this.skipContentLengthHeader = skipContentLengthHeader || false;
46
+ }
210
47
  /**
211
- * Will retry if Stomp connection is not established in specified milliseconds.
212
- * Default 0, which implies wait for ever.
48
+ * body of the frame
213
49
  */
214
- this.connectionTimeout = 0;
50
+ get body() {
51
+ if (!this._body && this.isBinaryBody) {
52
+ this._body = new TextDecoder().decode(this._binaryBody);
53
+ }
54
+ return this._body || '';
55
+ }
215
56
  /**
216
- * automatically reconnect with delay in milliseconds, set to 0 to disable.
57
+ * body as Uint8Array
217
58
  */
218
- this.reconnectDelay = 5000;
59
+ get binaryBody() {
60
+ if (!this._binaryBody && !this.isBinaryBody) {
61
+ this._binaryBody = new TextEncoder().encode(this._body);
62
+ }
63
+ // At this stage it will definitely have a valid value
64
+ return this._binaryBody;
65
+ }
219
66
  /**
220
- * Incoming heartbeat interval in milliseconds. Set to 0 to disable.
67
+ * deserialize a STOMP Frame from raw data.
68
+ *
69
+ * @internal
221
70
  */
222
- this.heartbeatIncoming = 10000;
71
+ static fromRawFrame(rawFrame, escapeHeaderValues) {
72
+ const headers = {};
73
+ const trim = (str) => str.replace(/^\s+|\s+$/g, '');
74
+ // In case of repeated headers, as per standards, first value need to be used
75
+ for (const header of rawFrame.headers.reverse()) {
76
+ header.indexOf(':');
77
+ const key = trim(header[0]);
78
+ let value = trim(header[1]);
79
+ if (escapeHeaderValues &&
80
+ rawFrame.command !== 'CONNECT' &&
81
+ rawFrame.command !== 'CONNECTED') {
82
+ value = FrameImpl.hdrValueUnEscape(value);
83
+ }
84
+ headers[key] = value;
85
+ }
86
+ return new FrameImpl({
87
+ command: rawFrame.command,
88
+ headers,
89
+ binaryBody: rawFrame.binaryBody,
90
+ escapeHeaderValues,
91
+ });
92
+ }
223
93
  /**
224
- * Outgoing heartbeat interval in milliseconds. Set to 0 to disable.
94
+ * @internal
225
95
  */
226
- this.heartbeatOutgoing = 10000;
96
+ toString() {
97
+ return this.serializeCmdAndHeaders();
98
+ }
227
99
  /**
228
- * This switches on a non standard behavior while sending WebSocket packets.
229
- * It splits larger (text) packets into chunks of [maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}.
230
- * Only Java Spring brokers seems to use this mode.
100
+ * serialize this Frame in a format suitable to be passed to WebSocket.
101
+ * If the body is string the output will be string.
102
+ * If the body is binary (i.e. of type Unit8Array) it will be serialized to ArrayBuffer.
231
103
  *
232
- * WebSockets, by itself, split large (text) packets,
233
- * so it is not needed with a truly compliant STOMP/WebSocket broker.
234
- * Actually setting it for such broker will cause large messages to fail.
235
- *
236
- * `false` by default.
237
- *
238
- * Binary frames are never split.
104
+ * @internal
239
105
  */
240
- this.splitLargeFrames = false;
106
+ serialize() {
107
+ const cmdAndHeaders = this.serializeCmdAndHeaders();
108
+ if (this.isBinaryBody) {
109
+ return FrameImpl.toUnit8Array(cmdAndHeaders, this._binaryBody).buffer;
110
+ }
111
+ else {
112
+ return cmdAndHeaders + this._body + BYTE.NULL;
113
+ }
114
+ }
115
+ serializeCmdAndHeaders() {
116
+ const lines = [this.command];
117
+ if (this.skipContentLengthHeader) {
118
+ delete this.headers['content-length'];
119
+ }
120
+ for (const name of Object.keys(this.headers || {})) {
121
+ const value = this.headers[name];
122
+ if (this.escapeHeaderValues &&
123
+ this.command !== 'CONNECT' &&
124
+ this.command !== 'CONNECTED') {
125
+ lines.push(`${name}:${FrameImpl.hdrValueEscape(`${value}`)}`);
126
+ }
127
+ else {
128
+ lines.push(`${name}:${value}`);
129
+ }
130
+ }
131
+ if (this.isBinaryBody ||
132
+ (!this.isBodyEmpty() && !this.skipContentLengthHeader)) {
133
+ lines.push(`content-length:${this.bodyLength()}`);
134
+ }
135
+ return lines.join(BYTE.LF) + BYTE.LF + BYTE.LF;
136
+ }
137
+ isBodyEmpty() {
138
+ return this.bodyLength() === 0;
139
+ }
140
+ bodyLength() {
141
+ const binaryBody = this.binaryBody;
142
+ return binaryBody ? binaryBody.length : 0;
143
+ }
241
144
  /**
242
- * See [splitLargeFrames]{@link Client#splitLargeFrames}.
243
- * This has no effect if [splitLargeFrames]{@link Client#splitLargeFrames} is `false`.
145
+ * Compute the size of a UTF-8 string by counting its number of bytes
146
+ * (and not the number of characters composing the string)
244
147
  */
245
- this.maxWebSocketChunkSize = 8 * 1024;
148
+ static sizeOfUTF8(s) {
149
+ return s ? new TextEncoder().encode(s).length : 0;
150
+ }
151
+ static toUnit8Array(cmdAndHeaders, binaryBody) {
152
+ const uint8CmdAndHeaders = new TextEncoder().encode(cmdAndHeaders);
153
+ const nullTerminator = new Uint8Array([0]);
154
+ const uint8Frame = new Uint8Array(uint8CmdAndHeaders.length + binaryBody.length + nullTerminator.length);
155
+ uint8Frame.set(uint8CmdAndHeaders);
156
+ uint8Frame.set(binaryBody, uint8CmdAndHeaders.length);
157
+ uint8Frame.set(nullTerminator, uint8CmdAndHeaders.length + binaryBody.length);
158
+ return uint8Frame;
159
+ }
246
160
  /**
247
- * Usually the
248
- * [type of WebSocket frame]{@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send#Parameters}
249
- * is automatically decided by type of the payload.
250
- * Default is `false`, which should work with all compliant brokers.
161
+ * Serialize a STOMP frame as per STOMP standards, suitable to be sent to the STOMP broker.
251
162
  *
252
- * Set this flag to force binary frames.
163
+ * @internal
253
164
  */
254
- this.forceBinaryWSFrames = false;
165
+ static marshall(params) {
166
+ const frame = new FrameImpl(params);
167
+ return frame.serialize();
168
+ }
255
169
  /**
256
- * A bug in ReactNative chops a string on occurrence of a NULL.
257
- * See issue [https://github.com/stomp-js/stompjs/issues/89]{@link https://github.com/stomp-js/stompjs/issues/89}.
258
- * This makes incoming WebSocket messages invalid STOMP packets.
259
- * Setting this flag attempts to reverse the damage by appending a NULL.
260
- * If the broker splits a large message into multiple WebSocket messages,
261
- * this flag will cause data loss and abnormal termination of connection.
262
- *
263
- * This is not an ideal solution, but a stop gap until the underlying issue is fixed at ReactNative library.
170
+ * Escape header values
264
171
  */
265
- this.appendMissingNULLonIncoming = false;
172
+ static hdrValueEscape(str) {
173
+ return str
174
+ .replace(/\\/g, '\\\\')
175
+ .replace(/\r/g, '\\r')
176
+ .replace(/\n/g, '\\n')
177
+ .replace(/:/g, '\\c');
178
+ }
266
179
  /**
267
- * Activation state.
268
- *
269
- * It will usually be ACTIVE or INACTIVE.
270
- * When deactivating it may go from ACTIVE to INACTIVE without entering DEACTIVATING.
180
+ * UnEscape header values
271
181
  */
272
- this.state = _types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].INACTIVE;
273
- // Dummy callbacks
274
- const noOp = () => { };
275
- this.debug = noOp;
276
- this.beforeConnect = noOp;
277
- this.onConnect = noOp;
278
- this.onDisconnect = noOp;
279
- this.onUnhandledMessage = noOp;
280
- this.onUnhandledReceipt = noOp;
281
- this.onUnhandledFrame = noOp;
282
- this.onStompError = noOp;
283
- this.onWebSocketClose = noOp;
284
- this.onWebSocketError = noOp;
285
- this.logRawCommunication = false;
286
- this.onChangeState = noOp;
287
- // These parameters would typically get proper values before connect is called
288
- this.connectHeaders = {};
289
- this._disconnectHeaders = {};
290
- // Apply configuration
291
- this.configure(conf);
292
- }
293
- /**
294
- * Underlying WebSocket instance, READONLY.
295
- */
296
- get webSocket() {
297
- return this._stompHandler ? this._stompHandler._webSocket : undefined;
298
- }
299
- /**
300
- * Disconnection headers.
301
- */
302
- get disconnectHeaders() {
303
- return this._disconnectHeaders;
304
- }
305
- set disconnectHeaders(value) {
306
- this._disconnectHeaders = value;
307
- if (this._stompHandler) {
308
- this._stompHandler.disconnectHeaders = this._disconnectHeaders;
182
+ static hdrValueUnEscape(str) {
183
+ return str
184
+ .replace(/\\r/g, '\r')
185
+ .replace(/\\n/g, '\n')
186
+ .replace(/\\c/g, ':')
187
+ .replace(/\\\\/g, '\\');
309
188
  }
310
189
  }
190
+
311
191
  /**
312
- * `true` if there is a active connection with STOMP Broker
192
+ * @internal
313
193
  */
314
- get connected() {
315
- return !!this._stompHandler && this._stompHandler.connected;
316
- }
194
+ const NULL = 0;
317
195
  /**
318
- * version of STOMP protocol negotiated with the server, READONLY
196
+ * @internal
319
197
  */
320
- get connectedVersion() {
321
- return this._stompHandler ? this._stompHandler.connectedVersion : undefined;
322
- }
198
+ const LF = 10;
323
199
  /**
324
- * if the client is active (connected or going to reconnect)
200
+ * @internal
325
201
  */
326
- get active() {
327
- return this.state === _types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].ACTIVE;
328
- }
329
- _changeState(state) {
330
- this.state = state;
331
- this.onChangeState(state);
332
- }
202
+ const CR = 13;
333
203
  /**
334
- * Update configuration.
204
+ * @internal
335
205
  */
336
- configure(conf) {
337
- // bulk assign all properties to this
338
- Object.assign(this, conf);
339
- }
206
+ const COLON = 58;
340
207
  /**
341
- * Initiate the connection with the broker.
342
- * If the connection breaks, as per [Client#reconnectDelay]{@link Client#reconnectDelay},
343
- * it will keep trying to reconnect.
208
+ * This is an evented, rec descent parser.
209
+ * A stream of Octets can be passed and whenever it recognizes
210
+ * a complete Frame or an incoming ping it will invoke the registered callbacks.
211
+ *
212
+ * All incoming Octets are fed into _onByte function.
213
+ * Depending on current state the _onByte function keeps changing.
214
+ * Depending on the state it keeps accumulating into _token and _results.
215
+ * State is indicated by current value of _onByte, all states are named as _collect.
216
+ *
217
+ * STOMP standards https://stomp.github.io/stomp-specification-1.2.html
218
+ * imply that all lengths are considered in bytes (instead of string lengths).
219
+ * So, before actual parsing, if the incoming data is String it is converted to Octets.
220
+ * This allows faithful implementation of the protocol and allows NULL Octets to be present in the body.
221
+ *
222
+ * There is no peek function on the incoming data.
223
+ * When a state change occurs based on an Octet without consuming the Octet,
224
+ * the Octet, after state change, is fed again (_reinjectByte).
225
+ * This became possible as the state change can be determined by inspecting just one Octet.
226
+ *
227
+ * There are two modes to collect the body, if content-length header is there then it by counting Octets
228
+ * otherwise it is determined by NULL terminator.
229
+ *
230
+ * Following the standards, the command and headers are converted to Strings
231
+ * and the body is returned as Octets.
232
+ * Headers are returned as an array and not as Hash - to allow multiple occurrence of an header.
233
+ *
234
+ * This parser does not use Regular Expressions as that can only operate on Strings.
235
+ *
236
+ * It handles if multiple STOMP frames are given as one chunk, a frame is split into multiple chunks, or
237
+ * any combination there of. The parser remembers its state (any partial frame) and continues when a new chunk
238
+ * is pushed.
239
+ *
240
+ * Typically the higher level function will convert headers to Hash, handle unescaping of header values
241
+ * (which is protocol version specific), and convert body to text.
344
242
  *
345
- * Call [Client#deactivate]{@link Client#deactivate} to disconnect and stop reconnection attempts.
243
+ * Check the parser.spec.js to understand cases that this parser is supposed to handle.
244
+ *
245
+ * Part of `@stomp/stompjs`.
246
+ *
247
+ * @internal
346
248
  */
347
- activate() {
348
- if (this.state === _types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].DEACTIVATING) {
349
- this.debug('Still DEACTIVATING, please await call to deactivate before trying to re-activate');
350
- throw new Error('Still DEACTIVATING, can not activate now');
249
+ class Parser {
250
+ constructor(onFrame, onIncomingPing) {
251
+ this.onFrame = onFrame;
252
+ this.onIncomingPing = onIncomingPing;
253
+ this._encoder = new TextEncoder();
254
+ this._decoder = new TextDecoder();
255
+ this._token = [];
256
+ this._initState();
257
+ }
258
+ parseChunk(segment, appendMissingNULLonIncoming = false) {
259
+ let chunk;
260
+ if (segment instanceof ArrayBuffer) {
261
+ chunk = new Uint8Array(segment);
262
+ }
263
+ else {
264
+ chunk = this._encoder.encode(segment);
265
+ }
266
+ // See https://github.com/stomp-js/stompjs/issues/89
267
+ // Remove when underlying issue is fixed.
268
+ //
269
+ // Send a NULL byte, if the last byte of a Text frame was not NULL.F
270
+ if (appendMissingNULLonIncoming && chunk[chunk.length - 1] !== 0) {
271
+ const chunkWithNull = new Uint8Array(chunk.length + 1);
272
+ chunkWithNull.set(chunk, 0);
273
+ chunkWithNull[chunk.length] = 0;
274
+ chunk = chunkWithNull;
275
+ }
276
+ // tslint:disable-next-line:prefer-for-of
277
+ for (let i = 0; i < chunk.length; i++) {
278
+ const byte = chunk[i];
279
+ this._onByte(byte);
280
+ }
351
281
  }
352
- if (this.active) {
353
- this.debug('Already ACTIVE, ignoring request to activate');
354
- return;
282
+ // The following implements a simple Rec Descent Parser.
283
+ // The grammar is simple and just one byte tells what should be the next state
284
+ _collectFrame(byte) {
285
+ if (byte === NULL) {
286
+ // Ignore
287
+ return;
288
+ }
289
+ if (byte === CR) {
290
+ // Ignore CR
291
+ return;
292
+ }
293
+ if (byte === LF) {
294
+ // Incoming Ping
295
+ this.onIncomingPing();
296
+ return;
297
+ }
298
+ this._onByte = this._collectCommand;
299
+ this._reinjectByte(byte);
355
300
  }
356
- this._changeState(_types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].ACTIVE);
357
- this._connect();
358
- }
359
- _connect() {
360
- return __awaiter(this, void 0, void 0, function* () {
361
- if (this.connected) {
362
- this.debug('STOMP: already connected, nothing to do');
301
+ _collectCommand(byte) {
302
+ if (byte === CR) {
303
+ // Ignore CR
363
304
  return;
364
305
  }
365
- yield this.beforeConnect();
366
- if (!this.active) {
367
- this.debug('Client has been marked inactive, will not attempt to connect');
306
+ if (byte === LF) {
307
+ this._results.command = this._consumeTokenAsUTF8();
308
+ this._onByte = this._collectHeaders;
368
309
  return;
369
310
  }
370
- // setup connection watcher
371
- if (this.connectionTimeout > 0) {
372
- // clear first
373
- if (this._connectionWatcher) {
374
- clearTimeout(this._connectionWatcher);
375
- }
376
- this._connectionWatcher = setTimeout(() => {
377
- if (this.connected) {
378
- return;
379
- }
380
- // Connection not established, close the underlying socket
381
- // a reconnection will be attempted
382
- this.debug(`Connection not established in ${this.connectionTimeout}ms, closing socket`);
383
- this.forceDisconnect();
384
- }, this.connectionTimeout);
311
+ this._consumeByte(byte);
312
+ }
313
+ _collectHeaders(byte) {
314
+ if (byte === CR) {
315
+ // Ignore CR
316
+ return;
385
317
  }
386
- this.debug('Opening Web Socket...');
387
- // Get the actual WebSocket (or a similar object)
388
- const webSocket = this._createWebSocket();
389
- this._stompHandler = new _stomp_handler__WEBPACK_IMPORTED_MODULE_0__["StompHandler"](this, webSocket, {
390
- debug: this.debug,
391
- stompVersions: this.stompVersions,
392
- connectHeaders: this.connectHeaders,
393
- disconnectHeaders: this._disconnectHeaders,
394
- heartbeatIncoming: this.heartbeatIncoming,
395
- heartbeatOutgoing: this.heartbeatOutgoing,
396
- splitLargeFrames: this.splitLargeFrames,
397
- maxWebSocketChunkSize: this.maxWebSocketChunkSize,
398
- forceBinaryWSFrames: this.forceBinaryWSFrames,
399
- logRawCommunication: this.logRawCommunication,
400
- appendMissingNULLonIncoming: this.appendMissingNULLonIncoming,
401
- discardWebsocketOnCommFailure: this.discardWebsocketOnCommFailure,
402
- onConnect: frame => {
403
- // Successfully connected, stop the connection watcher
404
- if (this._connectionWatcher) {
405
- clearTimeout(this._connectionWatcher);
406
- this._connectionWatcher = undefined;
407
- }
408
- if (!this.active) {
409
- this.debug('STOMP got connected while deactivate was issued, will disconnect now');
410
- this._disposeStompHandler();
411
- return;
412
- }
413
- this.onConnect(frame);
414
- },
415
- onDisconnect: frame => {
416
- this.onDisconnect(frame);
417
- },
418
- onStompError: frame => {
419
- this.onStompError(frame);
420
- },
421
- onWebSocketClose: evt => {
422
- this._stompHandler = undefined; // a new one will be created in case of a reconnect
423
- if (this.state === _types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].DEACTIVATING) {
424
- // Mark deactivation complete
425
- this._resolveSocketClose();
426
- this._resolveSocketClose = undefined;
427
- this._changeState(_types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].INACTIVE);
428
- }
429
- this.onWebSocketClose(evt);
430
- // The callback is called before attempting to reconnect, this would allow the client
431
- // to be `deactivated` in the callback.
432
- if (this.active) {
433
- this._schedule_reconnect();
434
- }
435
- },
436
- onWebSocketError: evt => {
437
- this.onWebSocketError(evt);
438
- },
439
- onUnhandledMessage: message => {
440
- this.onUnhandledMessage(message);
441
- },
442
- onUnhandledReceipt: frame => {
443
- this.onUnhandledReceipt(frame);
444
- },
445
- onUnhandledFrame: frame => {
446
- this.onUnhandledFrame(frame);
447
- },
448
- });
449
- this._stompHandler.start();
450
- });
451
- }
452
- _createWebSocket() {
453
- let webSocket;
454
- if (this.webSocketFactory) {
455
- webSocket = this.webSocketFactory();
318
+ if (byte === LF) {
319
+ this._setupCollectBody();
320
+ return;
321
+ }
322
+ this._onByte = this._collectHeaderKey;
323
+ this._reinjectByte(byte);
456
324
  }
457
- else {
458
- webSocket = new WebSocket(this.brokerURL, this.stompVersions.protocolVersions());
325
+ _reinjectByte(byte) {
326
+ this._onByte(byte);
459
327
  }
460
- webSocket.binaryType = 'arraybuffer';
461
- return webSocket;
462
- }
463
- _schedule_reconnect() {
464
- if (this.reconnectDelay > 0) {
465
- this.debug(`STOMP: scheduling reconnection in ${this.reconnectDelay}ms`);
466
- this._reconnector = setTimeout(() => {
467
- this._connect();
468
- }, this.reconnectDelay);
328
+ _collectHeaderKey(byte) {
329
+ if (byte === COLON) {
330
+ this._headerKey = this._consumeTokenAsUTF8();
331
+ this._onByte = this._collectHeaderValue;
332
+ return;
333
+ }
334
+ this._consumeByte(byte);
469
335
  }
470
- }
471
- /**
472
- * Disconnect if connected and stop auto reconnect loop.
473
- * Appropriate callbacks will be invoked if underlying STOMP connection was connected.
474
- *
475
- * This call is async, it will resolve immediately if there is no underlying active websocket,
476
- * otherwise, it will resolve after underlying websocket is properly disposed.
477
- *
478
- * To reactivate you can call [Client#activate]{@link Client#activate}.
479
- */
480
- deactivate() {
481
- return __awaiter(this, void 0, void 0, function* () {
482
- let retPromise;
483
- if (this.state !== _types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].ACTIVE) {
484
- this.debug(`Already ${_types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"][this.state]}, ignoring call to deactivate`);
485
- return Promise.resolve();
336
+ _collectHeaderValue(byte) {
337
+ if (byte === CR) {
338
+ // Ignore CR
339
+ return;
486
340
  }
487
- this._changeState(_types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].DEACTIVATING);
488
- // Clear if a reconnection was scheduled
489
- if (this._reconnector) {
490
- clearTimeout(this._reconnector);
341
+ if (byte === LF) {
342
+ this._results.headers.push([
343
+ this._headerKey,
344
+ this._consumeTokenAsUTF8(),
345
+ ]);
346
+ this._headerKey = undefined;
347
+ this._onByte = this._collectHeaders;
348
+ return;
491
349
  }
492
- if (this._stompHandler &&
493
- this.webSocket.readyState !== _types__WEBPACK_IMPORTED_MODULE_1__["StompSocketState"].CLOSED) {
494
- // we need to wait for underlying websocket to close
495
- retPromise = new Promise((resolve, reject) => {
496
- this._resolveSocketClose = resolve;
497
- });
350
+ this._consumeByte(byte);
351
+ }
352
+ _setupCollectBody() {
353
+ const contentLengthHeader = this._results.headers.filter((header) => {
354
+ return header[0] === 'content-length';
355
+ })[0];
356
+ if (contentLengthHeader) {
357
+ this._bodyBytesRemaining = parseInt(contentLengthHeader[1], 10);
358
+ this._onByte = this._collectBodyFixedSize;
498
359
  }
499
360
  else {
500
- // indicate that auto reconnect loop should terminate
501
- this._changeState(_types__WEBPACK_IMPORTED_MODULE_1__["ActivationState"].INACTIVE);
502
- return Promise.resolve();
361
+ this._onByte = this._collectBodyNullTerminated;
503
362
  }
504
- this._disposeStompHandler();
505
- return retPromise;
506
- });
507
- }
508
- /**
509
- * Force disconnect if there is an active connection by directly closing the underlying WebSocket.
510
- * This is different than a normal disconnect where a DISCONNECT sequence is carried out with the broker.
511
- * After forcing disconnect, automatic reconnect will be attempted.
512
- * To stop further reconnects call [Client#deactivate]{@link Client#deactivate} as well.
513
- */
514
- forceDisconnect() {
515
- if (this._stompHandler) {
516
- this._stompHandler.forceDisconnect();
517
363
  }
518
- }
519
- _disposeStompHandler() {
520
- // Dispose STOMP Handler
521
- if (this._stompHandler) {
522
- this._stompHandler.dispose();
523
- this._stompHandler = null;
364
+ _collectBodyNullTerminated(byte) {
365
+ if (byte === NULL) {
366
+ this._retrievedBody();
367
+ return;
368
+ }
369
+ this._consumeByte(byte);
370
+ }
371
+ _collectBodyFixedSize(byte) {
372
+ // It is post decrement, so that we discard the trailing NULL octet
373
+ if (this._bodyBytesRemaining-- === 0) {
374
+ this._retrievedBody();
375
+ return;
376
+ }
377
+ this._consumeByte(byte);
378
+ }
379
+ _retrievedBody() {
380
+ this._results.binaryBody = this._consumeTokenAsRaw();
381
+ try {
382
+ this.onFrame(this._results);
383
+ }
384
+ catch (e) {
385
+ console.log(`Ignoring an exception thrown by a frame handler. Original exception: `, e);
386
+ }
387
+ this._initState();
388
+ }
389
+ // Rec Descent Parser helpers
390
+ _consumeByte(byte) {
391
+ this._token.push(byte);
392
+ }
393
+ _consumeTokenAsUTF8() {
394
+ return this._decoder.decode(this._consumeTokenAsRaw());
395
+ }
396
+ _consumeTokenAsRaw() {
397
+ const rawResult = new Uint8Array(this._token);
398
+ this._token = [];
399
+ return rawResult;
400
+ }
401
+ _initState() {
402
+ this._results = {
403
+ command: undefined,
404
+ headers: [],
405
+ binaryBody: undefined,
406
+ };
407
+ this._token = [];
408
+ this._headerKey = undefined;
409
+ this._onByte = this._collectFrame;
524
410
  }
525
411
  }
412
+
526
413
  /**
527
- * Send a message to a named destination. Refer to your STOMP broker documentation for types
528
- * and naming of destinations.
529
- *
530
- * STOMP protocol specifies and suggests some headers and also allows broker specific headers.
531
- *
532
- * `body` must be String.
533
- * You will need to covert the payload to string in case it is not string (e.g. JSON).
534
- *
535
- * To send a binary message body use binaryBody parameter. It should be a
536
- * [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).
537
- * Sometimes brokers may not support binary frames out of the box.
538
- * Please check your broker documentation.
539
- *
540
- * `content-length` header is automatically added to the STOMP Frame sent to the broker.
541
- * Set `skipContentLengthHeader` to indicate that `content-length` header should not be added.
542
- * For binary messages `content-length` header is always added.
543
- *
544
- * Caution: The broker will, most likely, report an error and disconnect if message body has NULL octet(s)
545
- * and `content-length` header is missing.
546
- *
547
- * ```javascript
548
- * client.publish({destination: "/queue/test", headers: {priority: 9}, body: "Hello, STOMP"});
549
- *
550
- * // Only destination is mandatory parameter
551
- * client.publish({destination: "/queue/test", body: "Hello, STOMP"});
552
- *
553
- * // Skip content-length header in the frame to the broker
554
- * client.publish({"/queue/test", body: "Hello, STOMP", skipContentLengthHeader: true});
555
- *
556
- * var binaryData = generateBinaryData(); // This need to be of type Uint8Array
557
- * // setting content-type header is not mandatory, however a good practice
558
- * client.publish({destination: '/topic/special', binaryBody: binaryData,
559
- * headers: {'content-type': 'application/octet-stream'}});
560
- * ```
414
+ * Possible states for the IStompSocket
561
415
  */
562
- publish(params) {
563
- this._stompHandler.publish(params);
564
- }
416
+ exports.StompSocketState = void 0;
417
+ (function (StompSocketState) {
418
+ StompSocketState[StompSocketState["CONNECTING"] = 0] = "CONNECTING";
419
+ StompSocketState[StompSocketState["OPEN"] = 1] = "OPEN";
420
+ StompSocketState[StompSocketState["CLOSING"] = 2] = "CLOSING";
421
+ StompSocketState[StompSocketState["CLOSED"] = 3] = "CLOSED";
422
+ })(exports.StompSocketState = exports.StompSocketState || (exports.StompSocketState = {}));
565
423
  /**
566
- * STOMP brokers may carry out operation asynchronously and allow requesting for acknowledgement.
567
- * To request an acknowledgement, a `receipt` header needs to be sent with the actual request.
568
- * The value (say receipt-id) for this header needs to be unique for each use. Typically a sequence, a UUID, a
569
- * random number or a combination may be used.
570
- *
571
- * A complaint broker will send a RECEIPT frame when an operation has actually been completed.
572
- * The operation needs to be matched based in the value of the receipt-id.
573
- *
574
- * This method allow watching for a receipt and invoke the callback
575
- * when corresponding receipt has been received.
576
- *
577
- * The actual {@link FrameImpl} will be passed as parameter to the callback.
578
- *
579
- * Example:
580
- * ```javascript
581
- * // Subscribing with acknowledgement
582
- * let receiptId = randomText();
583
- *
584
- * client.watchForReceipt(receiptId, function() {
585
- * // Will be called after server acknowledges
586
- * });
587
- *
588
- * client.subscribe(TEST.destination, onMessage, {receipt: receiptId});
589
- *
590
- *
591
- * // Publishing with acknowledgement
592
- * receiptId = randomText();
593
- *
594
- * client.watchForReceipt(receiptId, function() {
595
- * // Will be called after server acknowledges
596
- * });
597
- * client.publish({destination: TEST.destination, headers: {receipt: receiptId}, body: msg});
598
- * ```
424
+ * Possible activation state
599
425
  */
600
- watchForReceipt(receiptId, callback) {
601
- this._stompHandler.watchForReceipt(receiptId, callback);
602
- }
426
+ exports.ActivationState = void 0;
427
+ (function (ActivationState) {
428
+ ActivationState[ActivationState["ACTIVE"] = 0] = "ACTIVE";
429
+ ActivationState[ActivationState["DEACTIVATING"] = 1] = "DEACTIVATING";
430
+ ActivationState[ActivationState["INACTIVE"] = 2] = "INACTIVE";
431
+ })(exports.ActivationState = exports.ActivationState || (exports.ActivationState = {}));
432
+
603
433
  /**
604
- * Subscribe to a STOMP Broker location. The callback will be invoked for each received message with
605
- * the {@link IMessage} as argument.
434
+ * Supported STOMP versions
606
435
  *
607
- * Note: The library will generate an unique ID if there is none provided in the headers.
608
- * To use your own ID, pass it using the headers argument.
609
- *
610
- * ```javascript
611
- * callback = function(message) {
612
- * // called when the client receives a STOMP message from the server
613
- * if (message.body) {
614
- * alert("got message with body " + message.body)
615
- * } else {
616
- * alert("got empty message");
617
- * }
618
- * });
619
- *
620
- * var subscription = client.subscribe("/queue/test", callback);
621
- *
622
- * // Explicit subscription id
623
- * var mySubId = 'my-subscription-id-001';
624
- * var subscription = client.subscribe(destination, callback, { id: mySubId });
625
- * ```
436
+ * Part of `@stomp/stompjs`.
626
437
  */
627
- subscribe(destination, callback, headers = {}) {
628
- return this._stompHandler.subscribe(destination, callback, headers);
438
+ class Versions {
439
+ /**
440
+ * Takes an array of string of versions, typical elements '1.0', '1.1', or '1.2'
441
+ *
442
+ * You will an instance if this class if you want to override supported versions to be declared during
443
+ * STOMP handshake.
444
+ */
445
+ constructor(versions) {
446
+ this.versions = versions;
447
+ }
448
+ /**
449
+ * Used as part of CONNECT STOMP Frame
450
+ */
451
+ supportedVersions() {
452
+ return this.versions.join(',');
453
+ }
454
+ /**
455
+ * Used while creating a WebSocket
456
+ */
457
+ protocolVersions() {
458
+ return this.versions.map(x => `v${x.replace('.', '')}.stomp`);
459
+ }
629
460
  }
630
461
  /**
631
- * It is preferable to unsubscribe from a subscription by calling
632
- * `unsubscribe()` directly on {@link StompSubscription} returned by `client.subscribe()`:
633
- *
634
- * ```javascript
635
- * var subscription = client.subscribe(destination, onmessage);
636
- * // ...
637
- * subscription.unsubscribe();
638
- * ```
639
- *
640
- * See: http://stomp.github.com/stomp-specification-1.2.html#UNSUBSCRIBE UNSUBSCRIBE Frame
641
- */
642
- unsubscribe(id, headers = {}) {
643
- this._stompHandler.unsubscribe(id, headers);
644
- }
645
- /**
646
- * Start a transaction, the returned {@link ITransaction} has methods - [commit]{@link ITransaction#commit}
647
- * and [abort]{@link ITransaction#abort}.
648
- *
649
- * `transactionId` is optional, if not passed the library will generate it internally.
462
+ * Indicates protocol version 1.0
650
463
  */
651
- begin(transactionId) {
652
- return this._stompHandler.begin(transactionId);
653
- }
654
- /**
655
- * Commit a transaction.
656
- *
657
- * It is preferable to commit a transaction by calling [commit]{@link ITransaction#commit} directly on
658
- * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
659
- *
660
- * ```javascript
661
- * var tx = client.begin(txId);
662
- * //...
663
- * tx.commit();
664
- * ```
665
- */
666
- commit(transactionId) {
667
- this._stompHandler.commit(transactionId);
668
- }
464
+ Versions.V1_0 = '1.0';
669
465
  /**
670
- * Abort a transaction.
671
- * It is preferable to abort a transaction by calling [abort]{@link ITransaction#abort} directly on
672
- * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
673
- *
674
- * ```javascript
675
- * var tx = client.begin(txId);
676
- * //...
677
- * tx.abort();
678
- * ```
466
+ * Indicates protocol version 1.1
679
467
  */
680
- abort(transactionId) {
681
- this._stompHandler.abort(transactionId);
682
- }
468
+ Versions.V1_1 = '1.1';
683
469
  /**
684
- * ACK a message. It is preferable to acknowledge a message by calling [ack]{@link IMessage#ack} directly
685
- * on the {@link IMessage} handled by a subscription callback:
686
- *
687
- * ```javascript
688
- * var callback = function (message) {
689
- * // process the message
690
- * // acknowledge it
691
- * message.ack();
692
- * };
693
- * client.subscribe(destination, callback, {'ack': 'client'});
694
- * ```
470
+ * Indicates protocol version 1.2
695
471
  */
696
- ack(messageId, subscriptionId, headers = {}) {
697
- this._stompHandler.ack(messageId, subscriptionId, headers);
698
- }
472
+ Versions.V1_2 = '1.2';
699
473
  /**
700
- * NACK a message. It is preferable to acknowledge a message by calling [nack]{@link IMessage#nack} directly
701
- * on the {@link IMessage} handled by a subscription callback:
702
- *
703
- * ```javascript
704
- * var callback = function (message) {
705
- * // process the message
706
- * // an error occurs, nack it
707
- * message.nack();
708
- * };
709
- * client.subscribe(destination, callback, {'ack': 'client'});
710
- * ```
474
+ * @internal
711
475
  */
712
- nack(messageId, subscriptionId, headers = {}) {
713
- this._stompHandler.nack(messageId, subscriptionId, headers);
714
- }
715
- }
716
-
717
-
718
- /***/ }),
476
+ Versions.default = new Versions([
477
+ Versions.V1_2,
478
+ Versions.V1_1,
479
+ Versions.V1_0,
480
+ ]);
719
481
 
720
- /***/ "./src/compatibility/compat-client.ts":
721
- /*!********************************************!*\
722
- !*** ./src/compatibility/compat-client.ts ***!
723
- \********************************************/
724
- /*! exports provided: CompatClient */
725
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
726
-
727
- "use strict";
728
- __webpack_require__.r(__webpack_exports__);
729
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CompatClient", function() { return CompatClient; });
730
- /* harmony import */ var _client__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../client */ "./src/client.ts");
731
- /* harmony import */ var _heartbeat_info__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./heartbeat-info */ "./src/compatibility/heartbeat-info.ts");
732
-
733
-
734
- /**
735
- * Available for backward compatibility, please shift to using {@link Client}.
736
- *
737
- * **Deprecated**
738
- *
739
- * Part of `@stomp/stompjs`.
740
- *
741
- * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html)
742
- */
743
- class CompatClient extends _client__WEBPACK_IMPORTED_MODULE_0__["Client"] {
744
482
  /**
745
- * Available for backward compatibility, please shift to using {@link Client}
746
- * and [Client#webSocketFactory]{@link Client#webSocketFactory}.
747
- *
748
- * **Deprecated**
749
- *
750
483
  * @internal
751
484
  */
752
- constructor(webSocketFactory) {
753
- super();
754
- /**
755
- * It is no op now. No longer needed. Large packets work out of the box.
756
- */
757
- this.maxWebSocketFrameSize = 16 * 1024;
758
- this._heartbeatInfo = new _heartbeat_info__WEBPACK_IMPORTED_MODULE_1__["HeartbeatInfo"](this);
759
- this.reconnect_delay = 0;
760
- this.webSocketFactory = webSocketFactory;
761
- // Default from previous version
762
- this.debug = (...message) => {
763
- console.log(...message);
485
+ function augmentWebsocket(webSocket, debug) {
486
+ webSocket.terminate = function () {
487
+ const noOp = () => { };
488
+ // set all callbacks to no op
489
+ this.onerror = noOp;
490
+ this.onmessage = noOp;
491
+ this.onopen = noOp;
492
+ const ts = new Date();
493
+ const id = Math.random().toString().substring(2, 8); // A simulated id
494
+ const origOnClose = this.onclose;
495
+ // Track delay in actual closure of the socket
496
+ this.onclose = closeEvent => {
497
+ const delay = new Date().getTime() - ts.getTime();
498
+ debug(`Discarded socket (#${id}) closed after ${delay}ms, with code/reason: ${closeEvent.code}/${closeEvent.reason}`);
499
+ };
500
+ this.close();
501
+ origOnClose?.call(webSocket, {
502
+ code: 4001,
503
+ reason: `Quick discarding socket (#${id}) without waiting for the shutdown sequence.`,
504
+ wasClean: false,
505
+ });
764
506
  };
765
507
  }
766
- _parseConnect(...args) {
767
- let closeEventCallback;
768
- let connectCallback;
769
- let errorCallback;
770
- let headers = {};
771
- if (args.length < 2) {
772
- throw new Error('Connect requires at least 2 arguments');
773
- }
774
- if (typeof args[1] === 'function') {
775
- [headers, connectCallback, errorCallback, closeEventCallback] = args;
776
- }
777
- else {
778
- switch (args.length) {
779
- case 6:
780
- [
781
- headers.login,
782
- headers.passcode,
783
- connectCallback,
784
- errorCallback,
785
- closeEventCallback,
786
- headers.host,
787
- ] = args;
788
- break;
789
- default:
790
- [
791
- headers.login,
792
- headers.passcode,
793
- connectCallback,
794
- errorCallback,
795
- closeEventCallback,
796
- ] = args;
797
- }
798
- }
799
- return [headers, connectCallback, errorCallback, closeEventCallback];
800
- }
508
+
801
509
  /**
802
- * Available for backward compatibility, please shift to using [Client#activate]{@link Client#activate}.
803
- *
804
- * **Deprecated**
805
- *
806
- * The `connect` method accepts different number of arguments and types. See the Overloads list. Use the
807
- * version with headers to pass your broker specific options.
808
- *
809
- * overloads:
810
- * - connect(headers, connectCallback)
811
- * - connect(headers, connectCallback, errorCallback)
812
- * - connect(login, passcode, connectCallback)
813
- * - connect(login, passcode, connectCallback, errorCallback)
814
- * - connect(login, passcode, connectCallback, errorCallback, closeEventCallback)
815
- * - connect(login, passcode, connectCallback, errorCallback, closeEventCallback, host)
510
+ * The STOMP protocol handler
816
511
  *
817
- * params:
818
- * - headers, see [Client#connectHeaders]{@link Client#connectHeaders}
819
- * - connectCallback, see [Client#onConnect]{@link Client#onConnect}
820
- * - errorCallback, see [Client#onStompError]{@link Client#onStompError}
821
- * - closeEventCallback, see [Client#onWebSocketClose]{@link Client#onWebSocketClose}
822
- * - login [String], see [Client#connectHeaders](../classes/Client.html#connectHeaders)
823
- * - passcode [String], [Client#connectHeaders](../classes/Client.html#connectHeaders)
824
- * - host [String], see [Client#connectHeaders](../classes/Client.html#connectHeaders)
512
+ * Part of `@stomp/stompjs`.
825
513
  *
826
- * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html)
514
+ * @internal
827
515
  */
828
- connect(...args) {
829
- const out = this._parseConnect(...args);
830
- if (out[0]) {
831
- this.connectHeaders = out[0];
516
+ class StompHandler {
517
+ constructor(_client, _webSocket, config) {
518
+ this._client = _client;
519
+ this._webSocket = _webSocket;
520
+ this._connected = false;
521
+ this._serverFrameHandlers = {
522
+ // [CONNECTED Frame](http://stomp.github.com/stomp-specification-1.2.html#CONNECTED_Frame)
523
+ CONNECTED: frame => {
524
+ this.debug(`connected to server ${frame.headers.server}`);
525
+ this._connected = true;
526
+ this._connectedVersion = frame.headers.version;
527
+ // STOMP version 1.2 needs header values to be escaped
528
+ if (this._connectedVersion === Versions.V1_2) {
529
+ this._escapeHeaderValues = true;
530
+ }
531
+ this._setupHeartbeat(frame.headers);
532
+ this.onConnect(frame);
533
+ },
534
+ // [MESSAGE Frame](http://stomp.github.com/stomp-specification-1.2.html#MESSAGE)
535
+ MESSAGE: frame => {
536
+ // the callback is registered when the client calls
537
+ // `subscribe()`.
538
+ // If there is no registered subscription for the received message,
539
+ // the default `onUnhandledMessage` callback is used that the client can set.
540
+ // This is useful for subscriptions that are automatically created
541
+ // on the browser side (e.g. [RabbitMQ's temporary
542
+ // queues](http://www.rabbitmq.com/stomp.html)).
543
+ const subscription = frame.headers.subscription;
544
+ const onReceive = this._subscriptions[subscription] || this.onUnhandledMessage;
545
+ // bless the frame to be a Message
546
+ const message = frame;
547
+ const client = this;
548
+ const messageId = this._connectedVersion === Versions.V1_2
549
+ ? message.headers.ack
550
+ : message.headers['message-id'];
551
+ // add `ack()` and `nack()` methods directly to the returned frame
552
+ // so that a simple call to `message.ack()` can acknowledge the message.
553
+ message.ack = (headers = {}) => {
554
+ return client.ack(messageId, subscription, headers);
555
+ };
556
+ message.nack = (headers = {}) => {
557
+ return client.nack(messageId, subscription, headers);
558
+ };
559
+ onReceive(message);
560
+ },
561
+ // [RECEIPT Frame](http://stomp.github.com/stomp-specification-1.2.html#RECEIPT)
562
+ RECEIPT: frame => {
563
+ const callback = this._receiptWatchers[frame.headers['receipt-id']];
564
+ if (callback) {
565
+ callback(frame);
566
+ // Server will acknowledge only once, remove the callback
567
+ delete this._receiptWatchers[frame.headers['receipt-id']];
568
+ }
569
+ else {
570
+ this.onUnhandledReceipt(frame);
571
+ }
572
+ },
573
+ // [ERROR Frame](http://stomp.github.com/stomp-specification-1.2.html#ERROR)
574
+ ERROR: frame => {
575
+ this.onStompError(frame);
576
+ },
577
+ };
578
+ // used to index subscribers
579
+ this._counter = 0;
580
+ // subscription callbacks indexed by subscriber's ID
581
+ this._subscriptions = {};
582
+ // receipt-watchers indexed by receipts-ids
583
+ this._receiptWatchers = {};
584
+ this._partialData = '';
585
+ this._escapeHeaderValues = false;
586
+ this._lastServerActivityTS = Date.now();
587
+ this.debug = config.debug;
588
+ this.stompVersions = config.stompVersions;
589
+ this.connectHeaders = config.connectHeaders;
590
+ this.disconnectHeaders = config.disconnectHeaders;
591
+ this.heartbeatIncoming = config.heartbeatIncoming;
592
+ this.heartbeatOutgoing = config.heartbeatOutgoing;
593
+ this.splitLargeFrames = config.splitLargeFrames;
594
+ this.maxWebSocketChunkSize = config.maxWebSocketChunkSize;
595
+ this.forceBinaryWSFrames = config.forceBinaryWSFrames;
596
+ this.logRawCommunication = config.logRawCommunication;
597
+ this.appendMissingNULLonIncoming = config.appendMissingNULLonIncoming;
598
+ this.discardWebsocketOnCommFailure = config.discardWebsocketOnCommFailure;
599
+ this.onConnect = config.onConnect;
600
+ this.onDisconnect = config.onDisconnect;
601
+ this.onStompError = config.onStompError;
602
+ this.onWebSocketClose = config.onWebSocketClose;
603
+ this.onWebSocketError = config.onWebSocketError;
604
+ this.onUnhandledMessage = config.onUnhandledMessage;
605
+ this.onUnhandledReceipt = config.onUnhandledReceipt;
606
+ this.onUnhandledFrame = config.onUnhandledFrame;
607
+ }
608
+ get connectedVersion() {
609
+ return this._connectedVersion;
610
+ }
611
+ get connected() {
612
+ return this._connected;
613
+ }
614
+ start() {
615
+ const parser = new Parser(
616
+ // On Frame
617
+ rawFrame => {
618
+ const frame = FrameImpl.fromRawFrame(rawFrame, this._escapeHeaderValues);
619
+ // if this.logRawCommunication is set, the rawChunk is logged at this._webSocket.onmessage
620
+ if (!this.logRawCommunication) {
621
+ this.debug(`<<< ${frame}`);
622
+ }
623
+ const serverFrameHandler = this._serverFrameHandlers[frame.command] || this.onUnhandledFrame;
624
+ serverFrameHandler(frame);
625
+ },
626
+ // On Incoming Ping
627
+ () => {
628
+ this.debug('<<< PONG');
629
+ });
630
+ this._webSocket.onmessage = (evt) => {
631
+ this.debug('Received data');
632
+ this._lastServerActivityTS = Date.now();
633
+ if (this.logRawCommunication) {
634
+ const rawChunkAsString = evt.data instanceof ArrayBuffer
635
+ ? new TextDecoder().decode(evt.data)
636
+ : evt.data;
637
+ this.debug(`<<< ${rawChunkAsString}`);
638
+ }
639
+ parser.parseChunk(evt.data, this.appendMissingNULLonIncoming);
640
+ };
641
+ this._webSocket.onclose = (closeEvent) => {
642
+ this.debug(`Connection closed to ${this._client.brokerURL}`);
643
+ this._cleanUp();
644
+ this.onWebSocketClose(closeEvent);
645
+ };
646
+ this._webSocket.onerror = (errorEvent) => {
647
+ this.onWebSocketError(errorEvent);
648
+ };
649
+ this._webSocket.onopen = () => {
650
+ // Clone before updating
651
+ const connectHeaders = Object.assign({}, this.connectHeaders);
652
+ this.debug('Web Socket Opened...');
653
+ connectHeaders['accept-version'] = this.stompVersions.supportedVersions();
654
+ connectHeaders['heart-beat'] = [
655
+ this.heartbeatOutgoing,
656
+ this.heartbeatIncoming,
657
+ ].join(',');
658
+ this._transmit({ command: 'CONNECT', headers: connectHeaders });
659
+ };
660
+ }
661
+ _setupHeartbeat(headers) {
662
+ if (headers.version !== Versions.V1_1 &&
663
+ headers.version !== Versions.V1_2) {
664
+ return;
665
+ }
666
+ // It is valid for the server to not send this header
667
+ // https://stomp.github.io/stomp-specification-1.2.html#Heart-beating
668
+ if (!headers['heart-beat']) {
669
+ return;
670
+ }
671
+ // heart-beat header received from the server looks like:
672
+ //
673
+ // heart-beat: sx, sy
674
+ const [serverOutgoing, serverIncoming] = headers['heart-beat']
675
+ .split(',')
676
+ .map((v) => parseInt(v, 10));
677
+ if (this.heartbeatOutgoing !== 0 && serverIncoming !== 0) {
678
+ const ttl = Math.max(this.heartbeatOutgoing, serverIncoming);
679
+ this.debug(`send PING every ${ttl}ms`);
680
+ this._pinger = setInterval(() => {
681
+ if (this._webSocket.readyState === exports.StompSocketState.OPEN) {
682
+ this._webSocket.send(BYTE.LF);
683
+ this.debug('>>> PING');
684
+ }
685
+ }, ttl);
686
+ }
687
+ if (this.heartbeatIncoming !== 0 && serverOutgoing !== 0) {
688
+ const ttl = Math.max(this.heartbeatIncoming, serverOutgoing);
689
+ this.debug(`check PONG every ${ttl}ms`);
690
+ this._ponger = setInterval(() => {
691
+ const delta = Date.now() - this._lastServerActivityTS;
692
+ // We wait twice the TTL to be flexible on window's setInterval calls
693
+ if (delta > ttl * 2) {
694
+ this.debug(`did not receive server activity for the last ${delta}ms`);
695
+ this._closeOrDiscardWebsocket();
696
+ }
697
+ }, ttl);
698
+ }
832
699
  }
833
- if (out[1]) {
834
- this.onConnect = out[1];
700
+ _closeOrDiscardWebsocket() {
701
+ if (this.discardWebsocketOnCommFailure) {
702
+ this.debug('Discarding websocket, the underlying socket may linger for a while');
703
+ this.discardWebsocket();
704
+ }
705
+ else {
706
+ this.debug('Issuing close on the websocket');
707
+ this._closeWebsocket();
708
+ }
835
709
  }
836
- if (out[2]) {
837
- this.onStompError = out[2];
710
+ forceDisconnect() {
711
+ if (this._webSocket) {
712
+ if (this._webSocket.readyState === exports.StompSocketState.CONNECTING ||
713
+ this._webSocket.readyState === exports.StompSocketState.OPEN) {
714
+ this._closeOrDiscardWebsocket();
715
+ }
716
+ }
838
717
  }
839
- if (out[3]) {
840
- this.onWebSocketClose = out[3];
718
+ _closeWebsocket() {
719
+ this._webSocket.onmessage = () => { }; // ignore messages
720
+ this._webSocket.close();
841
721
  }
842
- super.activate();
843
- }
844
- /**
845
- * Available for backward compatibility, please shift to using [Client#deactivate]{@link Client#deactivate}.
846
- *
847
- * **Deprecated**
848
- *
849
- * See:
850
- * [Client#onDisconnect]{@link Client#onDisconnect}, and
851
- * [Client#disconnectHeaders]{@link Client#disconnectHeaders}
852
- *
853
- * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html)
854
- */
855
- disconnect(disconnectCallback, headers = {}) {
856
- if (disconnectCallback) {
857
- this.onDisconnect = disconnectCallback;
722
+ discardWebsocket() {
723
+ if (typeof this._webSocket.terminate !== 'function') {
724
+ augmentWebsocket(this._webSocket, (msg) => this.debug(msg));
725
+ }
726
+ // @ts-ignore - this method will be there at this stage
727
+ this._webSocket.terminate();
728
+ }
729
+ _transmit(params) {
730
+ const { command, headers, body, binaryBody, skipContentLengthHeader } = params;
731
+ const frame = new FrameImpl({
732
+ command,
733
+ headers,
734
+ body,
735
+ binaryBody,
736
+ escapeHeaderValues: this._escapeHeaderValues,
737
+ skipContentLengthHeader,
738
+ });
739
+ let rawChunk = frame.serialize();
740
+ if (this.logRawCommunication) {
741
+ this.debug(`>>> ${rawChunk}`);
742
+ }
743
+ else {
744
+ this.debug(`>>> ${frame}`);
745
+ }
746
+ if (this.forceBinaryWSFrames && typeof rawChunk === 'string') {
747
+ rawChunk = new TextEncoder().encode(rawChunk);
748
+ }
749
+ if (typeof rawChunk !== 'string' || !this.splitLargeFrames) {
750
+ this._webSocket.send(rawChunk);
751
+ }
752
+ else {
753
+ let out = rawChunk;
754
+ while (out.length > 0) {
755
+ const chunk = out.substring(0, this.maxWebSocketChunkSize);
756
+ out = out.substring(this.maxWebSocketChunkSize);
757
+ this._webSocket.send(chunk);
758
+ this.debug(`chunk sent = ${chunk.length}, remaining = ${out.length}`);
759
+ }
760
+ }
858
761
  }
859
- this.disconnectHeaders = headers;
860
- super.deactivate();
861
- }
862
- /**
863
- * Available for backward compatibility, use [Client#publish]{@link Client#publish}.
864
- *
865
- * Send a message to a named destination. Refer to your STOMP broker documentation for types
866
- * and naming of destinations. The headers will, typically, be available to the subscriber.
867
- * However, there may be special purpose headers corresponding to your STOMP broker.
868
- *
869
- * **Deprecated**, use [Client#publish]{@link Client#publish}
870
- *
871
- * Note: Body must be String. You will need to covert the payload to string in case it is not string (e.g. JSON)
872
- *
873
- * ```javascript
874
- * client.send("/queue/test", {priority: 9}, "Hello, STOMP");
875
- *
876
- * // If you want to send a message with a body, you must also pass the headers argument.
877
- * client.send("/queue/test", {}, "Hello, STOMP");
878
- * ```
879
- *
880
- * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html)
881
- */
882
- send(destination, headers = {}, body = '') {
883
- headers = Object.assign({}, headers);
884
- const skipContentLengthHeader = headers['content-length'] === false;
885
- if (skipContentLengthHeader) {
886
- delete headers['content-length'];
887
- }
888
- this.publish({
889
- destination,
890
- headers: headers,
891
- body,
892
- skipContentLengthHeader,
893
- });
894
- }
895
- /**
896
- * Available for backward compatibility, renamed to [Client#reconnectDelay]{@link Client#reconnectDelay}.
897
- *
898
- * **Deprecated**
899
- */
900
- set reconnect_delay(value) {
901
- this.reconnectDelay = value;
902
- }
903
- /**
904
- * Available for backward compatibility, renamed to [Client#webSocket]{@link Client#webSocket}.
905
- *
906
- * **Deprecated**
907
- */
908
- get ws() {
909
- return this.webSocket;
910
- }
911
- /**
912
- * Available for backward compatibility, renamed to [Client#connectedVersion]{@link Client#connectedVersion}.
913
- *
914
- * **Deprecated**
915
- */
916
- get version() {
917
- return this.connectedVersion;
918
- }
919
- /**
920
- * Available for backward compatibility, renamed to [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}.
921
- *
922
- * **Deprecated**
923
- */
924
- get onreceive() {
925
- return this.onUnhandledMessage;
926
- }
927
- /**
928
- * Available for backward compatibility, renamed to [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}.
929
- *
930
- * **Deprecated**
931
- */
932
- set onreceive(value) {
933
- this.onUnhandledMessage = value;
934
- }
935
- /**
936
- * Available for backward compatibility, renamed to [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}.
937
- * Prefer using [Client#watchForReceipt]{@link Client#watchForReceipt}.
938
- *
939
- * **Deprecated**
940
- */
941
- get onreceipt() {
942
- return this.onUnhandledReceipt;
943
- }
944
- /**
945
- * Available for backward compatibility, renamed to [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}.
946
- *
947
- * **Deprecated**
948
- */
949
- set onreceipt(value) {
950
- this.onUnhandledReceipt = value;
951
- }
952
- /**
953
- * Available for backward compatibility, renamed to [Client#heartbeatIncoming]{@link Client#heartbeatIncoming}
954
- * [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}.
955
- *
956
- * **Deprecated**
957
- */
958
- get heartbeat() {
959
- return this._heartbeatInfo;
960
- }
961
- /**
962
- * Available for backward compatibility, renamed to [Client#heartbeatIncoming]{@link Client#heartbeatIncoming}
963
- * [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}.
964
- *
965
- * **Deprecated**
966
- */
967
- set heartbeat(value) {
968
- this.heartbeatIncoming = value.incoming;
969
- this.heartbeatOutgoing = value.outgoing;
970
- }
971
- }
972
-
973
-
974
- /***/ }),
975
-
976
- /***/ "./src/compatibility/heartbeat-info.ts":
977
- /*!*********************************************!*\
978
- !*** ./src/compatibility/heartbeat-info.ts ***!
979
- \*********************************************/
980
- /*! exports provided: HeartbeatInfo */
981
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
982
-
983
- "use strict";
984
- __webpack_require__.r(__webpack_exports__);
985
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HeartbeatInfo", function() { return HeartbeatInfo; });
986
- /**
987
- * Part of `@stomp/stompjs`.
988
- *
989
- * @internal
990
- */
991
- class HeartbeatInfo {
992
- constructor(client) {
993
- this.client = client;
994
- }
995
- get outgoing() {
996
- return this.client.heartbeatOutgoing;
997
- }
998
- set outgoing(value) {
999
- this.client.heartbeatOutgoing = value;
1000
- }
1001
- get incoming() {
1002
- return this.client.heartbeatIncoming;
1003
- }
1004
- set incoming(value) {
1005
- this.client.heartbeatIncoming = value;
1006
- }
1007
- }
1008
-
1009
-
1010
- /***/ }),
1011
-
1012
- /***/ "./src/compatibility/stomp.ts":
1013
- /*!************************************!*\
1014
- !*** ./src/compatibility/stomp.ts ***!
1015
- \************************************/
1016
- /*! exports provided: Stomp */
1017
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1018
-
1019
- "use strict";
1020
- __webpack_require__.r(__webpack_exports__);
1021
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Stomp", function() { return Stomp; });
1022
- /* harmony import */ var _versions__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../versions */ "./src/versions.ts");
1023
- /* harmony import */ var _compat_client__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./compat-client */ "./src/compatibility/compat-client.ts");
1024
-
1025
-
1026
- /**
1027
- * STOMP Class, acts like a factory to create {@link Client}.
1028
- *
1029
- * Part of `@stomp/stompjs`.
1030
- *
1031
- * **Deprecated**
1032
- *
1033
- * It will be removed in next major version. Please switch to {@link Client}.
1034
- */
1035
- class Stomp {
1036
- /**
1037
- * This method creates a WebSocket client that is connected to
1038
- * the STOMP server located at the url.
1039
- *
1040
- * ```javascript
1041
- * var url = "ws://localhost:61614/stomp";
1042
- * var client = Stomp.client(url);
1043
- * ```
1044
- *
1045
- * **Deprecated**
1046
- *
1047
- * It will be removed in next major version. Please switch to {@link Client}
1048
- * using [Client#brokerURL]{@link Client#brokerURL}.
1049
- */
1050
- static client(url, protocols) {
1051
- // This is a hack to allow another implementation than the standard
1052
- // HTML5 WebSocket class.
1053
- //
1054
- // It is possible to use another class by calling
1055
- //
1056
- // Stomp.WebSocketClass = MozWebSocket
1057
- //
1058
- // *prior* to call `Stomp.client()`.
1059
- //
1060
- // This hack is deprecated and `Stomp.over()` method should be used
1061
- // instead.
1062
- // See remarks on the function Stomp.over
1063
- if (protocols == null) {
1064
- protocols = _versions__WEBPACK_IMPORTED_MODULE_0__["Versions"].default.protocolVersions();
1065
- }
1066
- const wsFn = () => {
1067
- const klass = Stomp.WebSocketClass || WebSocket;
1068
- return new klass(url, protocols);
1069
- };
1070
- return new _compat_client__WEBPACK_IMPORTED_MODULE_1__["CompatClient"](wsFn);
1071
- }
1072
- /**
1073
- * This method is an alternative to [Stomp#client]{@link Stomp#client} to let the user
1074
- * specify the WebSocket to use (either a standard HTML5 WebSocket or
1075
- * a similar object).
1076
- *
1077
- * In order to support reconnection, the function Client._connect should be callable more than once.
1078
- * While reconnecting
1079
- * a new instance of underlying transport (TCP Socket, WebSocket or SockJS) will be needed. So, this function
1080
- * alternatively allows passing a function that should return a new instance of the underlying socket.
1081
- *
1082
- * ```javascript
1083
- * var client = Stomp.over(function(){
1084
- * return new WebSocket('ws://localhost:15674/ws')
1085
- * });
1086
- * ```
1087
- *
1088
- * **Deprecated**
1089
- *
1090
- * It will be removed in next major version. Please switch to {@link Client}
1091
- * using [Client#webSocketFactory]{@link Client#webSocketFactory}.
1092
- */
1093
- static over(ws) {
1094
- let wsFn;
1095
- if (typeof ws === 'function') {
1096
- wsFn = ws;
762
+ dispose() {
763
+ if (this.connected) {
764
+ try {
765
+ // clone before updating
766
+ const disconnectHeaders = Object.assign({}, this.disconnectHeaders);
767
+ if (!disconnectHeaders.receipt) {
768
+ disconnectHeaders.receipt = `close-${this._counter++}`;
769
+ }
770
+ this.watchForReceipt(disconnectHeaders.receipt, frame => {
771
+ this._closeWebsocket();
772
+ this._cleanUp();
773
+ this.onDisconnect(frame);
774
+ });
775
+ this._transmit({ command: 'DISCONNECT', headers: disconnectHeaders });
776
+ }
777
+ catch (error) {
778
+ this.debug(`Ignoring error during disconnect ${error}`);
779
+ }
780
+ }
781
+ else {
782
+ if (this._webSocket.readyState === exports.StompSocketState.CONNECTING ||
783
+ this._webSocket.readyState === exports.StompSocketState.OPEN) {
784
+ this._closeWebsocket();
785
+ }
786
+ }
1097
787
  }
1098
- else {
1099
- console.warn('Stomp.over did not receive a factory, auto reconnect will not work. ' +
1100
- 'Please see https://stomp-js.github.io/api-docs/latest/classes/Stomp.html#over');
1101
- wsFn = () => ws;
788
+ _cleanUp() {
789
+ this._connected = false;
790
+ if (this._pinger) {
791
+ clearInterval(this._pinger);
792
+ this._pinger = undefined;
793
+ }
794
+ if (this._ponger) {
795
+ clearInterval(this._ponger);
796
+ this._ponger = undefined;
797
+ }
798
+ }
799
+ publish(params) {
800
+ const { destination, headers, body, binaryBody, skipContentLengthHeader } = params;
801
+ const hdrs = Object.assign({ destination }, headers);
802
+ this._transmit({
803
+ command: 'SEND',
804
+ headers: hdrs,
805
+ body,
806
+ binaryBody,
807
+ skipContentLengthHeader,
808
+ });
809
+ }
810
+ watchForReceipt(receiptId, callback) {
811
+ this._receiptWatchers[receiptId] = callback;
812
+ }
813
+ subscribe(destination, callback, headers = {}) {
814
+ headers = Object.assign({}, headers);
815
+ if (!headers.id) {
816
+ headers.id = `sub-${this._counter++}`;
817
+ }
818
+ headers.destination = destination;
819
+ this._subscriptions[headers.id] = callback;
820
+ this._transmit({ command: 'SUBSCRIBE', headers });
821
+ const client = this;
822
+ return {
823
+ id: headers.id,
824
+ unsubscribe(hdrs) {
825
+ return client.unsubscribe(headers.id, hdrs);
826
+ },
827
+ };
828
+ }
829
+ unsubscribe(id, headers = {}) {
830
+ headers = Object.assign({}, headers);
831
+ delete this._subscriptions[id];
832
+ headers.id = id;
833
+ this._transmit({ command: 'UNSUBSCRIBE', headers });
834
+ }
835
+ begin(transactionId) {
836
+ const txId = transactionId || `tx-${this._counter++}`;
837
+ this._transmit({
838
+ command: 'BEGIN',
839
+ headers: {
840
+ transaction: txId,
841
+ },
842
+ });
843
+ const client = this;
844
+ return {
845
+ id: txId,
846
+ commit() {
847
+ client.commit(txId);
848
+ },
849
+ abort() {
850
+ client.abort(txId);
851
+ },
852
+ };
853
+ }
854
+ commit(transactionId) {
855
+ this._transmit({
856
+ command: 'COMMIT',
857
+ headers: {
858
+ transaction: transactionId,
859
+ },
860
+ });
861
+ }
862
+ abort(transactionId) {
863
+ this._transmit({
864
+ command: 'ABORT',
865
+ headers: {
866
+ transaction: transactionId,
867
+ },
868
+ });
869
+ }
870
+ ack(messageId, subscriptionId, headers = {}) {
871
+ headers = Object.assign({}, headers);
872
+ if (this._connectedVersion === Versions.V1_2) {
873
+ headers.id = messageId;
874
+ }
875
+ else {
876
+ headers['message-id'] = messageId;
877
+ }
878
+ headers.subscription = subscriptionId;
879
+ this._transmit({ command: 'ACK', headers });
880
+ }
881
+ nack(messageId, subscriptionId, headers = {}) {
882
+ headers = Object.assign({}, headers);
883
+ if (this._connectedVersion === Versions.V1_2) {
884
+ headers.id = messageId;
885
+ }
886
+ else {
887
+ headers['message-id'] = messageId;
888
+ }
889
+ headers.subscription = subscriptionId;
890
+ return this._transmit({ command: 'NACK', headers });
1102
891
  }
1103
- return new _compat_client__WEBPACK_IMPORTED_MODULE_1__["CompatClient"](wsFn);
1104
892
  }
1105
- }
1106
- /**
1107
- * In case you need to use a non standard class for WebSocket.
1108
- *
1109
- * For example when using within NodeJS environment:
1110
- *
1111
- * ```javascript
1112
- * StompJs = require('../../esm5/');
1113
- * Stomp = StompJs.Stomp;
1114
- * Stomp.WebSocketClass = require('websocket').w3cwebsocket;
1115
- * ```
1116
- *
1117
- * **Deprecated**
1118
- *
1119
- *
1120
- * It will be removed in next major version. Please switch to {@link Client}
1121
- * using [Client#webSocketFactory]{@link Client#webSocketFactory}.
1122
- */
1123
- // tslint:disable-next-line:variable-name
1124
- Stomp.WebSocketClass = null;
1125
-
1126
-
1127
- /***/ }),
1128
-
1129
- /***/ "./src/frame-impl.ts":
1130
- /*!***************************!*\
1131
- !*** ./src/frame-impl.ts ***!
1132
- \***************************/
1133
- /*! exports provided: FrameImpl */
1134
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1135
893
 
1136
- "use strict";
1137
- __webpack_require__.r(__webpack_exports__);
1138
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FrameImpl", function() { return FrameImpl; });
1139
- /* harmony import */ var _byte__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./byte */ "./src/byte.ts");
1140
-
1141
- /**
1142
- * Frame class represents a STOMP frame.
1143
- *
1144
- * @internal
1145
- */
1146
- class FrameImpl {
1147
894
  /**
1148
- * Frame constructor. `command`, `headers` and `body` are available as properties.
895
+ * STOMP Client Class.
1149
896
  *
1150
- * @internal
1151
- */
1152
- constructor(params) {
1153
- const { command, headers, body, binaryBody, escapeHeaderValues, skipContentLengthHeader, } = params;
1154
- this.command = command;
1155
- this.headers = Object.assign({}, headers || {});
1156
- if (binaryBody) {
1157
- this._binaryBody = binaryBody;
1158
- this.isBinaryBody = true;
1159
- }
1160
- else {
1161
- this._body = body || '';
1162
- this.isBinaryBody = false;
1163
- }
1164
- this.escapeHeaderValues = escapeHeaderValues || false;
1165
- this.skipContentLengthHeader = skipContentLengthHeader || false;
1166
- }
1167
- /**
1168
- * body of the frame
897
+ * Part of `@stomp/stompjs`.
1169
898
  */
1170
- get body() {
1171
- if (!this._body && this.isBinaryBody) {
1172
- this._body = new TextDecoder().decode(this._binaryBody);
899
+ class Client {
900
+ /**
901
+ * Create an instance.
902
+ */
903
+ constructor(conf = {}) {
904
+ /**
905
+ * STOMP versions to attempt during STOMP handshake. By default versions `1.0`, `1.1`, and `1.2` are attempted.
906
+ *
907
+ * Example:
908
+ * ```javascript
909
+ * // Try only versions 1.0 and 1.1
910
+ * client.stompVersions = new Versions(['1.0', '1.1'])
911
+ * ```
912
+ */
913
+ this.stompVersions = Versions.default;
914
+ /**
915
+ * Will retry if Stomp connection is not established in specified milliseconds.
916
+ * Default 0, which implies wait for ever.
917
+ */
918
+ this.connectionTimeout = 0;
919
+ /**
920
+ * automatically reconnect with delay in milliseconds, set to 0 to disable.
921
+ */
922
+ this.reconnectDelay = 5000;
923
+ /**
924
+ * Incoming heartbeat interval in milliseconds. Set to 0 to disable.
925
+ */
926
+ this.heartbeatIncoming = 10000;
927
+ /**
928
+ * Outgoing heartbeat interval in milliseconds. Set to 0 to disable.
929
+ */
930
+ this.heartbeatOutgoing = 10000;
931
+ /**
932
+ * This switches on a non standard behavior while sending WebSocket packets.
933
+ * It splits larger (text) packets into chunks of [maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}.
934
+ * Only Java Spring brokers seems to use this mode.
935
+ *
936
+ * WebSockets, by itself, split large (text) packets,
937
+ * so it is not needed with a truly compliant STOMP/WebSocket broker.
938
+ * Actually setting it for such broker will cause large messages to fail.
939
+ *
940
+ * `false` by default.
941
+ *
942
+ * Binary frames are never split.
943
+ */
944
+ this.splitLargeFrames = false;
945
+ /**
946
+ * See [splitLargeFrames]{@link Client#splitLargeFrames}.
947
+ * This has no effect if [splitLargeFrames]{@link Client#splitLargeFrames} is `false`.
948
+ */
949
+ this.maxWebSocketChunkSize = 8 * 1024;
950
+ /**
951
+ * Usually the
952
+ * [type of WebSocket frame]{@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send#Parameters}
953
+ * is automatically decided by type of the payload.
954
+ * Default is `false`, which should work with all compliant brokers.
955
+ *
956
+ * Set this flag to force binary frames.
957
+ */
958
+ this.forceBinaryWSFrames = false;
959
+ /**
960
+ * A bug in ReactNative chops a string on occurrence of a NULL.
961
+ * See issue [https://github.com/stomp-js/stompjs/issues/89]{@link https://github.com/stomp-js/stompjs/issues/89}.
962
+ * This makes incoming WebSocket messages invalid STOMP packets.
963
+ * Setting this flag attempts to reverse the damage by appending a NULL.
964
+ * If the broker splits a large message into multiple WebSocket messages,
965
+ * this flag will cause data loss and abnormal termination of connection.
966
+ *
967
+ * This is not an ideal solution, but a stop gap until the underlying issue is fixed at ReactNative library.
968
+ */
969
+ this.appendMissingNULLonIncoming = false;
970
+ /**
971
+ * Browsers do not immediately close WebSockets when `.close` is issued.
972
+ * This may cause reconnection to take a longer on certain type of failures.
973
+ * In case of incoming heartbeat failure, this experimental flag instructs the library
974
+ * to discard the socket immediately (even before it is actually closed).
975
+ */
976
+ this.discardWebsocketOnCommFailure = false;
977
+ /**
978
+ * Activation state.
979
+ *
980
+ * It will usually be ACTIVE or INACTIVE.
981
+ * When deactivating it may go from ACTIVE to INACTIVE without entering DEACTIVATING.
982
+ */
983
+ this.state = exports.ActivationState.INACTIVE;
984
+ // Dummy callbacks
985
+ const noOp = () => { };
986
+ this.debug = noOp;
987
+ this.beforeConnect = noOp;
988
+ this.onConnect = noOp;
989
+ this.onDisconnect = noOp;
990
+ this.onUnhandledMessage = noOp;
991
+ this.onUnhandledReceipt = noOp;
992
+ this.onUnhandledFrame = noOp;
993
+ this.onStompError = noOp;
994
+ this.onWebSocketClose = noOp;
995
+ this.onWebSocketError = noOp;
996
+ this.logRawCommunication = false;
997
+ this.onChangeState = noOp;
998
+ // These parameters would typically get proper values before connect is called
999
+ this.connectHeaders = {};
1000
+ this._disconnectHeaders = {};
1001
+ // Apply configuration
1002
+ this.configure(conf);
1173
1003
  }
1174
- return this._body;
1175
- }
1176
- /**
1177
- * body as Uint8Array
1178
- */
1179
- get binaryBody() {
1180
- if (!this._binaryBody && !this.isBinaryBody) {
1181
- this._binaryBody = new TextEncoder().encode(this._body);
1004
+ /**
1005
+ * Underlying WebSocket instance, READONLY.
1006
+ */
1007
+ get webSocket() {
1008
+ return this._stompHandler?._webSocket;
1182
1009
  }
1183
- return this._binaryBody;
1184
- }
1185
- /**
1186
- * deserialize a STOMP Frame from raw data.
1187
- *
1188
- * @internal
1189
- */
1190
- static fromRawFrame(rawFrame, escapeHeaderValues) {
1191
- const headers = {};
1192
- const trim = (str) => str.replace(/^\s+|\s+$/g, '');
1193
- // In case of repeated headers, as per standards, first value need to be used
1194
- for (const header of rawFrame.headers.reverse()) {
1195
- const idx = header.indexOf(':');
1196
- const key = trim(header[0]);
1197
- let value = trim(header[1]);
1198
- if (escapeHeaderValues &&
1199
- rawFrame.command !== 'CONNECT' &&
1200
- rawFrame.command !== 'CONNECTED') {
1201
- value = FrameImpl.hdrValueUnEscape(value);
1202
- }
1203
- headers[key] = value;
1204
- }
1205
- return new FrameImpl({
1206
- command: rawFrame.command,
1207
- headers,
1208
- binaryBody: rawFrame.binaryBody,
1209
- escapeHeaderValues,
1210
- });
1211
- }
1212
- /**
1213
- * @internal
1214
- */
1215
- toString() {
1216
- return this.serializeCmdAndHeaders();
1217
- }
1218
- /**
1219
- * serialize this Frame in a format suitable to be passed to WebSocket.
1220
- * If the body is string the output will be string.
1221
- * If the body is binary (i.e. of type Unit8Array) it will be serialized to ArrayBuffer.
1222
- *
1223
- * @internal
1224
- */
1225
- serialize() {
1226
- const cmdAndHeaders = this.serializeCmdAndHeaders();
1227
- if (this.isBinaryBody) {
1228
- return FrameImpl.toUnit8Array(cmdAndHeaders, this._binaryBody).buffer;
1010
+ /**
1011
+ * Disconnection headers.
1012
+ */
1013
+ get disconnectHeaders() {
1014
+ return this._disconnectHeaders;
1015
+ }
1016
+ set disconnectHeaders(value) {
1017
+ this._disconnectHeaders = value;
1018
+ if (this._stompHandler) {
1019
+ this._stompHandler.disconnectHeaders = this._disconnectHeaders;
1020
+ }
1229
1021
  }
1230
- else {
1231
- return cmdAndHeaders + this._body + _byte__WEBPACK_IMPORTED_MODULE_0__["BYTE"].NULL;
1022
+ /**
1023
+ * `true` if there is a active connection with STOMP Broker
1024
+ */
1025
+ get connected() {
1026
+ return !!this._stompHandler && this._stompHandler.connected;
1232
1027
  }
1233
- }
1234
- serializeCmdAndHeaders() {
1235
- const lines = [this.command];
1236
- if (this.skipContentLengthHeader) {
1237
- delete this.headers['content-length'];
1238
- }
1239
- for (const name of Object.keys(this.headers || {})) {
1240
- const value = this.headers[name];
1241
- if (this.escapeHeaderValues &&
1242
- this.command !== 'CONNECT' &&
1243
- this.command !== 'CONNECTED') {
1244
- lines.push(`${name}:${FrameImpl.hdrValueEscape(`${value}`)}`);
1028
+ /**
1029
+ * version of STOMP protocol negotiated with the server, READONLY
1030
+ */
1031
+ get connectedVersion() {
1032
+ return this._stompHandler ? this._stompHandler.connectedVersion : undefined;
1033
+ }
1034
+ /**
1035
+ * if the client is active (connected or going to reconnect)
1036
+ */
1037
+ get active() {
1038
+ return this.state === exports.ActivationState.ACTIVE;
1039
+ }
1040
+ _changeState(state) {
1041
+ this.state = state;
1042
+ this.onChangeState(state);
1043
+ }
1044
+ /**
1045
+ * Update configuration.
1046
+ */
1047
+ configure(conf) {
1048
+ // bulk assign all properties to this
1049
+ Object.assign(this, conf);
1050
+ }
1051
+ /**
1052
+ * Initiate the connection with the broker.
1053
+ * If the connection breaks, as per [Client#reconnectDelay]{@link Client#reconnectDelay},
1054
+ * it will keep trying to reconnect.
1055
+ *
1056
+ * Call [Client#deactivate]{@link Client#deactivate} to disconnect and stop reconnection attempts.
1057
+ */
1058
+ activate() {
1059
+ if (this.state === exports.ActivationState.DEACTIVATING) {
1060
+ this.debug('Still DEACTIVATING, please await call to deactivate before trying to re-activate');
1061
+ throw new Error('Still DEACTIVATING, can not activate now');
1062
+ }
1063
+ if (this.active) {
1064
+ this.debug('Already ACTIVE, ignoring request to activate');
1065
+ return;
1066
+ }
1067
+ this._changeState(exports.ActivationState.ACTIVE);
1068
+ this._connect();
1069
+ }
1070
+ async _connect() {
1071
+ if (this.connected) {
1072
+ this.debug('STOMP: already connected, nothing to do');
1073
+ return;
1074
+ }
1075
+ await this.beforeConnect();
1076
+ if (!this.active) {
1077
+ this.debug('Client has been marked inactive, will not attempt to connect');
1078
+ return;
1079
+ }
1080
+ // setup connection watcher
1081
+ if (this.connectionTimeout > 0) {
1082
+ // clear first
1083
+ if (this._connectionWatcher) {
1084
+ clearTimeout(this._connectionWatcher);
1085
+ }
1086
+ this._connectionWatcher = setTimeout(() => {
1087
+ if (this.connected) {
1088
+ return;
1089
+ }
1090
+ // Connection not established, close the underlying socket
1091
+ // a reconnection will be attempted
1092
+ this.debug(`Connection not established in ${this.connectionTimeout}ms, closing socket`);
1093
+ this.forceDisconnect();
1094
+ }, this.connectionTimeout);
1095
+ }
1096
+ this.debug('Opening Web Socket...');
1097
+ // Get the actual WebSocket (or a similar object)
1098
+ const webSocket = this._createWebSocket();
1099
+ this._stompHandler = new StompHandler(this, webSocket, {
1100
+ debug: this.debug,
1101
+ stompVersions: this.stompVersions,
1102
+ connectHeaders: this.connectHeaders,
1103
+ disconnectHeaders: this._disconnectHeaders,
1104
+ heartbeatIncoming: this.heartbeatIncoming,
1105
+ heartbeatOutgoing: this.heartbeatOutgoing,
1106
+ splitLargeFrames: this.splitLargeFrames,
1107
+ maxWebSocketChunkSize: this.maxWebSocketChunkSize,
1108
+ forceBinaryWSFrames: this.forceBinaryWSFrames,
1109
+ logRawCommunication: this.logRawCommunication,
1110
+ appendMissingNULLonIncoming: this.appendMissingNULLonIncoming,
1111
+ discardWebsocketOnCommFailure: this.discardWebsocketOnCommFailure,
1112
+ onConnect: frame => {
1113
+ // Successfully connected, stop the connection watcher
1114
+ if (this._connectionWatcher) {
1115
+ clearTimeout(this._connectionWatcher);
1116
+ this._connectionWatcher = undefined;
1117
+ }
1118
+ if (!this.active) {
1119
+ this.debug('STOMP got connected while deactivate was issued, will disconnect now');
1120
+ this._disposeStompHandler();
1121
+ return;
1122
+ }
1123
+ this.onConnect(frame);
1124
+ },
1125
+ onDisconnect: frame => {
1126
+ this.onDisconnect(frame);
1127
+ },
1128
+ onStompError: frame => {
1129
+ this.onStompError(frame);
1130
+ },
1131
+ onWebSocketClose: evt => {
1132
+ this._stompHandler = undefined; // a new one will be created in case of a reconnect
1133
+ if (this.state === exports.ActivationState.DEACTIVATING) {
1134
+ // Mark deactivation complete
1135
+ this._changeState(exports.ActivationState.INACTIVE);
1136
+ }
1137
+ // The callback is called before attempting to reconnect, this would allow the client
1138
+ // to be `deactivated` in the callback.
1139
+ this.onWebSocketClose(evt);
1140
+ if (this.active) {
1141
+ this._schedule_reconnect();
1142
+ }
1143
+ },
1144
+ onWebSocketError: evt => {
1145
+ this.onWebSocketError(evt);
1146
+ },
1147
+ onUnhandledMessage: message => {
1148
+ this.onUnhandledMessage(message);
1149
+ },
1150
+ onUnhandledReceipt: frame => {
1151
+ this.onUnhandledReceipt(frame);
1152
+ },
1153
+ onUnhandledFrame: frame => {
1154
+ this.onUnhandledFrame(frame);
1155
+ },
1156
+ });
1157
+ this._stompHandler.start();
1158
+ }
1159
+ _createWebSocket() {
1160
+ let webSocket;
1161
+ if (this.webSocketFactory) {
1162
+ webSocket = this.webSocketFactory();
1163
+ }
1164
+ else if (this.brokerURL) {
1165
+ webSocket = new WebSocket(this.brokerURL, this.stompVersions.protocolVersions());
1245
1166
  }
1246
1167
  else {
1247
- lines.push(`${name}:${value}`);
1168
+ throw new Error('Either brokerURL or webSocketFactory must be provided');
1169
+ }
1170
+ webSocket.binaryType = 'arraybuffer';
1171
+ return webSocket;
1172
+ }
1173
+ _schedule_reconnect() {
1174
+ if (this.reconnectDelay > 0) {
1175
+ this.debug(`STOMP: scheduling reconnection in ${this.reconnectDelay}ms`);
1176
+ this._reconnector = setTimeout(() => {
1177
+ this._connect();
1178
+ }, this.reconnectDelay);
1248
1179
  }
1249
1180
  }
1250
- if (this.isBinaryBody ||
1251
- (!this.isBodyEmpty() && !this.skipContentLengthHeader)) {
1252
- lines.push(`content-length:${this.bodyLength()}`);
1181
+ /**
1182
+ * Disconnect if connected and stop auto reconnect loop.
1183
+ * Appropriate callbacks will be invoked if there is an underlying STOMP connection.
1184
+ *
1185
+ * This call is async. It will resolve immediately if there is no underlying active websocket,
1186
+ * otherwise, it will resolve after the underlying websocket is properly disposed of.
1187
+ *
1188
+ * It is not an error to invoke this method more than once.
1189
+ * Each of those would resolve on completion of deactivation.
1190
+ *
1191
+ * To reactivate, you can call [Client#activate]{@link Client#activate}.
1192
+ *
1193
+ * Experimental: pass `force: true` to immediately discard the underlying connection.
1194
+ * This mode will skip both the STOMP and the Websocket shutdown sequences.
1195
+ * In some cases, browsers take a long time in the Websocket shutdown if the underlying connection had gone stale.
1196
+ * Using this mode can speed up.
1197
+ * When this mode is used, the actual Websocket may linger for a while
1198
+ * and the broker may not realize that the connection is no longer in use.
1199
+ *
1200
+ * It is possible to invoke this method initially without the `force` option
1201
+ * and subsequently, say after a wait, with the `force` option.
1202
+ */
1203
+ async deactivate(options = {}) {
1204
+ const force = options.force || false;
1205
+ const needToDispose = this.active;
1206
+ let retPromise;
1207
+ if (this.state === exports.ActivationState.INACTIVE) {
1208
+ this.debug(`Already INACTIVE, nothing more to do`);
1209
+ return Promise.resolve();
1210
+ }
1211
+ this._changeState(exports.ActivationState.DEACTIVATING);
1212
+ // Clear if a reconnection was scheduled
1213
+ if (this._reconnector) {
1214
+ clearTimeout(this._reconnector);
1215
+ this._reconnector = undefined;
1216
+ }
1217
+ if (this._stompHandler &&
1218
+ // @ts-ignore - if there is a _stompHandler, there is the webSocket
1219
+ this.webSocket.readyState !== exports.StompSocketState.CLOSED) {
1220
+ const origOnWebSocketClose = this._stompHandler.onWebSocketClose;
1221
+ // we need to wait for the underlying websocket to close
1222
+ retPromise = new Promise((resolve, reject) => {
1223
+ // @ts-ignore - there is a _stompHandler
1224
+ this._stompHandler.onWebSocketClose = evt => {
1225
+ origOnWebSocketClose(evt);
1226
+ resolve();
1227
+ };
1228
+ });
1229
+ }
1230
+ else {
1231
+ // indicate that auto reconnect loop should terminate
1232
+ this._changeState(exports.ActivationState.INACTIVE);
1233
+ return Promise.resolve();
1234
+ }
1235
+ if (force) {
1236
+ this._stompHandler?.discardWebsocket();
1237
+ }
1238
+ else if (needToDispose) {
1239
+ this._disposeStompHandler();
1240
+ }
1241
+ return retPromise;
1242
+ }
1243
+ /**
1244
+ * Force disconnect if there is an active connection by directly closing the underlying WebSocket.
1245
+ * This is different than a normal disconnect where a DISCONNECT sequence is carried out with the broker.
1246
+ * After forcing disconnect, automatic reconnect will be attempted.
1247
+ * To stop further reconnects call [Client#deactivate]{@link Client#deactivate} as well.
1248
+ */
1249
+ forceDisconnect() {
1250
+ if (this._stompHandler) {
1251
+ this._stompHandler.forceDisconnect();
1252
+ }
1253
+ }
1254
+ _disposeStompHandler() {
1255
+ // Dispose STOMP Handler
1256
+ if (this._stompHandler) {
1257
+ this._stompHandler.dispose();
1258
+ }
1259
+ }
1260
+ /**
1261
+ * Send a message to a named destination. Refer to your STOMP broker documentation for types
1262
+ * and naming of destinations.
1263
+ *
1264
+ * STOMP protocol specifies and suggests some headers and also allows broker specific headers.
1265
+ *
1266
+ * `body` must be String.
1267
+ * You will need to covert the payload to string in case it is not string (e.g. JSON).
1268
+ *
1269
+ * To send a binary message body use binaryBody parameter. It should be a
1270
+ * [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).
1271
+ * Sometimes brokers may not support binary frames out of the box.
1272
+ * Please check your broker documentation.
1273
+ *
1274
+ * `content-length` header is automatically added to the STOMP Frame sent to the broker.
1275
+ * Set `skipContentLengthHeader` to indicate that `content-length` header should not be added.
1276
+ * For binary messages `content-length` header is always added.
1277
+ *
1278
+ * Caution: The broker will, most likely, report an error and disconnect if message body has NULL octet(s)
1279
+ * and `content-length` header is missing.
1280
+ *
1281
+ * ```javascript
1282
+ * client.publish({destination: "/queue/test", headers: {priority: 9}, body: "Hello, STOMP"});
1283
+ *
1284
+ * // Only destination is mandatory parameter
1285
+ * client.publish({destination: "/queue/test", body: "Hello, STOMP"});
1286
+ *
1287
+ * // Skip content-length header in the frame to the broker
1288
+ * client.publish({"/queue/test", body: "Hello, STOMP", skipContentLengthHeader: true});
1289
+ *
1290
+ * var binaryData = generateBinaryData(); // This need to be of type Uint8Array
1291
+ * // setting content-type header is not mandatory, however a good practice
1292
+ * client.publish({destination: '/topic/special', binaryBody: binaryData,
1293
+ * headers: {'content-type': 'application/octet-stream'}});
1294
+ * ```
1295
+ */
1296
+ publish(params) {
1297
+ this._checkConnection();
1298
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1299
+ this._stompHandler.publish(params);
1300
+ }
1301
+ _checkConnection() {
1302
+ if (!this.connected) {
1303
+ throw new TypeError('There is no underlying STOMP connection');
1304
+ }
1305
+ }
1306
+ /**
1307
+ * STOMP brokers may carry out operation asynchronously and allow requesting for acknowledgement.
1308
+ * To request an acknowledgement, a `receipt` header needs to be sent with the actual request.
1309
+ * The value (say receipt-id) for this header needs to be unique for each use. Typically a sequence, a UUID, a
1310
+ * random number or a combination may be used.
1311
+ *
1312
+ * A complaint broker will send a RECEIPT frame when an operation has actually been completed.
1313
+ * The operation needs to be matched based in the value of the receipt-id.
1314
+ *
1315
+ * This method allow watching for a receipt and invoke the callback
1316
+ * when corresponding receipt has been received.
1317
+ *
1318
+ * The actual {@link FrameImpl} will be passed as parameter to the callback.
1319
+ *
1320
+ * Example:
1321
+ * ```javascript
1322
+ * // Subscribing with acknowledgement
1323
+ * let receiptId = randomText();
1324
+ *
1325
+ * client.watchForReceipt(receiptId, function() {
1326
+ * // Will be called after server acknowledges
1327
+ * });
1328
+ *
1329
+ * client.subscribe(TEST.destination, onMessage, {receipt: receiptId});
1330
+ *
1331
+ *
1332
+ * // Publishing with acknowledgement
1333
+ * receiptId = randomText();
1334
+ *
1335
+ * client.watchForReceipt(receiptId, function() {
1336
+ * // Will be called after server acknowledges
1337
+ * });
1338
+ * client.publish({destination: TEST.destination, headers: {receipt: receiptId}, body: msg});
1339
+ * ```
1340
+ */
1341
+ watchForReceipt(receiptId, callback) {
1342
+ this._checkConnection();
1343
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1344
+ this._stompHandler.watchForReceipt(receiptId, callback);
1345
+ }
1346
+ /**
1347
+ * Subscribe to a STOMP Broker location. The callback will be invoked for each received message with
1348
+ * the {@link IMessage} as argument.
1349
+ *
1350
+ * Note: The library will generate an unique ID if there is none provided in the headers.
1351
+ * To use your own ID, pass it using the headers argument.
1352
+ *
1353
+ * ```javascript
1354
+ * callback = function(message) {
1355
+ * // called when the client receives a STOMP message from the server
1356
+ * if (message.body) {
1357
+ * alert("got message with body " + message.body)
1358
+ * } else {
1359
+ * alert("got empty message");
1360
+ * }
1361
+ * });
1362
+ *
1363
+ * var subscription = client.subscribe("/queue/test", callback);
1364
+ *
1365
+ * // Explicit subscription id
1366
+ * var mySubId = 'my-subscription-id-001';
1367
+ * var subscription = client.subscribe(destination, callback, { id: mySubId });
1368
+ * ```
1369
+ */
1370
+ subscribe(destination, callback, headers = {}) {
1371
+ this._checkConnection();
1372
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1373
+ return this._stompHandler.subscribe(destination, callback, headers);
1374
+ }
1375
+ /**
1376
+ * It is preferable to unsubscribe from a subscription by calling
1377
+ * `unsubscribe()` directly on {@link StompSubscription} returned by `client.subscribe()`:
1378
+ *
1379
+ * ```javascript
1380
+ * var subscription = client.subscribe(destination, onmessage);
1381
+ * // ...
1382
+ * subscription.unsubscribe();
1383
+ * ```
1384
+ *
1385
+ * See: http://stomp.github.com/stomp-specification-1.2.html#UNSUBSCRIBE UNSUBSCRIBE Frame
1386
+ */
1387
+ unsubscribe(id, headers = {}) {
1388
+ this._checkConnection();
1389
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1390
+ this._stompHandler.unsubscribe(id, headers);
1391
+ }
1392
+ /**
1393
+ * Start a transaction, the returned {@link ITransaction} has methods - [commit]{@link ITransaction#commit}
1394
+ * and [abort]{@link ITransaction#abort}.
1395
+ *
1396
+ * `transactionId` is optional, if not passed the library will generate it internally.
1397
+ */
1398
+ begin(transactionId) {
1399
+ this._checkConnection();
1400
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1401
+ return this._stompHandler.begin(transactionId);
1402
+ }
1403
+ /**
1404
+ * Commit a transaction.
1405
+ *
1406
+ * It is preferable to commit a transaction by calling [commit]{@link ITransaction#commit} directly on
1407
+ * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
1408
+ *
1409
+ * ```javascript
1410
+ * var tx = client.begin(txId);
1411
+ * //...
1412
+ * tx.commit();
1413
+ * ```
1414
+ */
1415
+ commit(transactionId) {
1416
+ this._checkConnection();
1417
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1418
+ this._stompHandler.commit(transactionId);
1419
+ }
1420
+ /**
1421
+ * Abort a transaction.
1422
+ * It is preferable to abort a transaction by calling [abort]{@link ITransaction#abort} directly on
1423
+ * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
1424
+ *
1425
+ * ```javascript
1426
+ * var tx = client.begin(txId);
1427
+ * //...
1428
+ * tx.abort();
1429
+ * ```
1430
+ */
1431
+ abort(transactionId) {
1432
+ this._checkConnection();
1433
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1434
+ this._stompHandler.abort(transactionId);
1435
+ }
1436
+ /**
1437
+ * ACK a message. It is preferable to acknowledge a message by calling [ack]{@link IMessage#ack} directly
1438
+ * on the {@link IMessage} handled by a subscription callback:
1439
+ *
1440
+ * ```javascript
1441
+ * var callback = function (message) {
1442
+ * // process the message
1443
+ * // acknowledge it
1444
+ * message.ack();
1445
+ * };
1446
+ * client.subscribe(destination, callback, {'ack': 'client'});
1447
+ * ```
1448
+ */
1449
+ ack(messageId, subscriptionId, headers = {}) {
1450
+ this._checkConnection();
1451
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1452
+ this._stompHandler.ack(messageId, subscriptionId, headers);
1453
+ }
1454
+ /**
1455
+ * NACK a message. It is preferable to acknowledge a message by calling [nack]{@link IMessage#nack} directly
1456
+ * on the {@link IMessage} handled by a subscription callback:
1457
+ *
1458
+ * ```javascript
1459
+ * var callback = function (message) {
1460
+ * // process the message
1461
+ * // an error occurs, nack it
1462
+ * message.nack();
1463
+ * };
1464
+ * client.subscribe(destination, callback, {'ack': 'client'});
1465
+ * ```
1466
+ */
1467
+ nack(messageId, subscriptionId, headers = {}) {
1468
+ this._checkConnection();
1469
+ // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
1470
+ this._stompHandler.nack(messageId, subscriptionId, headers);
1253
1471
  }
1254
- return lines.join(_byte__WEBPACK_IMPORTED_MODULE_0__["BYTE"].LF) + _byte__WEBPACK_IMPORTED_MODULE_0__["BYTE"].LF + _byte__WEBPACK_IMPORTED_MODULE_0__["BYTE"].LF;
1255
- }
1256
- isBodyEmpty() {
1257
- return this.bodyLength() === 0;
1258
- }
1259
- bodyLength() {
1260
- const binaryBody = this.binaryBody;
1261
- return binaryBody ? binaryBody.length : 0;
1262
- }
1263
- /**
1264
- * Compute the size of a UTF-8 string by counting its number of bytes
1265
- * (and not the number of characters composing the string)
1266
- */
1267
- static sizeOfUTF8(s) {
1268
- return s ? new TextEncoder().encode(s).length : 0;
1269
- }
1270
- static toUnit8Array(cmdAndHeaders, binaryBody) {
1271
- const uint8CmdAndHeaders = new TextEncoder().encode(cmdAndHeaders);
1272
- const nullTerminator = new Uint8Array([0]);
1273
- const uint8Frame = new Uint8Array(uint8CmdAndHeaders.length + binaryBody.length + nullTerminator.length);
1274
- uint8Frame.set(uint8CmdAndHeaders);
1275
- uint8Frame.set(binaryBody, uint8CmdAndHeaders.length);
1276
- uint8Frame.set(nullTerminator, uint8CmdAndHeaders.length + binaryBody.length);
1277
- return uint8Frame;
1278
1472
  }
1473
+
1279
1474
  /**
1280
- * Serialize a STOMP frame as per STOMP standards, suitable to be sent to the STOMP broker.
1475
+ * Configuration options for STOMP Client, each key corresponds to
1476
+ * field by the same name in {@link Client}. This can be passed to
1477
+ * the constructor of {@link Client} or to [Client#configure]{@link Client#configure}.
1281
1478
  *
1282
- * @internal
1479
+ * There used to be a class with the same name in `@stomp/ng2-stompjs`, which has been replaced by
1480
+ * {@link RxStompConfig} and {@link InjectableRxStompConfig}.
1481
+ *
1482
+ * Part of `@stomp/stompjs`.
1283
1483
  */
1284
- static marshall(params) {
1285
- const frame = new FrameImpl(params);
1286
- return frame.serialize();
1484
+ class StompConfig {
1287
1485
  }
1486
+
1288
1487
  /**
1289
- * Escape header values
1488
+ * STOMP headers. Many functions calls will accept headers as parameters.
1489
+ * The headers sent by Broker will be available as [IFrame#headers]{@link IFrame#headers}.
1490
+ *
1491
+ * `key` and `value` must be valid strings.
1492
+ * In addition, `key` must not contain `CR`, `LF`, or `:`.
1493
+ *
1494
+ * Part of `@stomp/stompjs`.
1290
1495
  */
1291
- static hdrValueEscape(str) {
1292
- return str
1293
- .replace(/\\/g, '\\\\')
1294
- .replace(/\r/g, '\\r')
1295
- .replace(/\n/g, '\\n')
1296
- .replace(/:/g, '\\c');
1496
+ class StompHeaders {
1297
1497
  }
1498
+
1298
1499
  /**
1299
- * UnEscape header values
1500
+ * Part of `@stomp/stompjs`.
1501
+ *
1502
+ * @internal
1300
1503
  */
1301
- static hdrValueUnEscape(str) {
1302
- return str
1303
- .replace(/\\r/g, '\r')
1304
- .replace(/\\n/g, '\n')
1305
- .replace(/\\c/g, ':')
1306
- .replace(/\\\\/g, '\\');
1307
- }
1308
- }
1309
-
1310
-
1311
- /***/ }),
1312
-
1313
- /***/ "./src/index.ts":
1314
- /*!**********************!*\
1315
- !*** ./src/index.ts ***!
1316
- \**********************/
1317
- /*! exports provided: Client, FrameImpl, Parser, StompConfig, StompHeaders, StompSubscription, StompSocketState, ActivationState, Versions, CompatClient, Stomp */
1318
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1319
-
1320
- "use strict";
1321
- __webpack_require__.r(__webpack_exports__);
1322
- /* harmony import */ var _client__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./client */ "./src/client.ts");
1323
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Client", function() { return _client__WEBPACK_IMPORTED_MODULE_0__["Client"]; });
1324
-
1325
- /* harmony import */ var _frame_impl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./frame-impl */ "./src/frame-impl.ts");
1326
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "FrameImpl", function() { return _frame_impl__WEBPACK_IMPORTED_MODULE_1__["FrameImpl"]; });
1327
-
1328
- /* harmony import */ var _parser__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./parser */ "./src/parser.ts");
1329
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Parser", function() { return _parser__WEBPACK_IMPORTED_MODULE_2__["Parser"]; });
1330
-
1331
- /* harmony import */ var _stomp_config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stomp-config */ "./src/stomp-config.ts");
1332
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "StompConfig", function() { return _stomp_config__WEBPACK_IMPORTED_MODULE_3__["StompConfig"]; });
1333
-
1334
- /* harmony import */ var _stomp_headers__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./stomp-headers */ "./src/stomp-headers.ts");
1335
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "StompHeaders", function() { return _stomp_headers__WEBPACK_IMPORTED_MODULE_4__["StompHeaders"]; });
1336
-
1337
- /* harmony import */ var _stomp_subscription__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./stomp-subscription */ "./src/stomp-subscription.ts");
1338
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "StompSubscription", function() { return _stomp_subscription__WEBPACK_IMPORTED_MODULE_5__["StompSubscription"]; });
1339
-
1340
- /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./types */ "./src/types.ts");
1341
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "StompSocketState", function() { return _types__WEBPACK_IMPORTED_MODULE_6__["StompSocketState"]; });
1342
-
1343
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ActivationState", function() { return _types__WEBPACK_IMPORTED_MODULE_6__["ActivationState"]; });
1344
-
1345
- /* harmony import */ var _versions__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./versions */ "./src/versions.ts");
1346
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Versions", function() { return _versions__WEBPACK_IMPORTED_MODULE_7__["Versions"]; });
1347
-
1348
- /* harmony import */ var _compatibility_compat_client__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./compatibility/compat-client */ "./src/compatibility/compat-client.ts");
1349
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "CompatClient", function() { return _compatibility_compat_client__WEBPACK_IMPORTED_MODULE_8__["CompatClient"]; });
1350
-
1351
- /* harmony import */ var _compatibility_stomp__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./compatibility/stomp */ "./src/compatibility/stomp.ts");
1352
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Stomp", function() { return _compatibility_stomp__WEBPACK_IMPORTED_MODULE_9__["Stomp"]; });
1353
-
1354
-
1355
-
1356
-
1357
-
1358
-
1359
-
1360
-
1361
-
1362
- // Compatibility code
1363
-
1364
-
1365
-
1366
-
1367
- /***/ }),
1368
-
1369
- /***/ "./src/parser.ts":
1370
- /*!***********************!*\
1371
- !*** ./src/parser.ts ***!
1372
- \***********************/
1373
- /*! exports provided: Parser */
1374
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1375
-
1376
- "use strict";
1377
- __webpack_require__.r(__webpack_exports__);
1378
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Parser", function() { return Parser; });
1379
- /**
1380
- * @internal
1381
- */
1382
- const NULL = 0;
1383
- /**
1384
- * @internal
1385
- */
1386
- const LF = 10;
1387
- /**
1388
- * @internal
1389
- */
1390
- const CR = 13;
1391
- /**
1392
- * @internal
1393
- */
1394
- const COLON = 58;
1395
- /**
1396
- * This is an evented, rec descent parser.
1397
- * A stream of Octets can be passed and whenever it recognizes
1398
- * a complete Frame or an incoming ping it will invoke the registered callbacks.
1399
- *
1400
- * All incoming Octets are fed into _onByte function.
1401
- * Depending on current state the _onByte function keeps changing.
1402
- * Depending on the state it keeps accumulating into _token and _results.
1403
- * State is indicated by current value of _onByte, all states are named as _collect.
1404
- *
1405
- * STOMP standards https://stomp.github.io/stomp-specification-1.2.html
1406
- * imply that all lengths are considered in bytes (instead of string lengths).
1407
- * So, before actual parsing, if the incoming data is String it is converted to Octets.
1408
- * This allows faithful implementation of the protocol and allows NULL Octets to be present in the body.
1409
- *
1410
- * There is no peek function on the incoming data.
1411
- * When a state change occurs based on an Octet without consuming the Octet,
1412
- * the Octet, after state change, is fed again (_reinjectByte).
1413
- * This became possible as the state change can be determined by inspecting just one Octet.
1414
- *
1415
- * There are two modes to collect the body, if content-length header is there then it by counting Octets
1416
- * otherwise it is determined by NULL terminator.
1417
- *
1418
- * Following the standards, the command and headers are converted to Strings
1419
- * and the body is returned as Octets.
1420
- * Headers are returned as an array and not as Hash - to allow multiple occurrence of an header.
1421
- *
1422
- * This parser does not use Regular Expressions as that can only operate on Strings.
1423
- *
1424
- * It handles if multiple STOMP frames are given as one chunk, a frame is split into multiple chunks, or
1425
- * any combination there of. The parser remembers its state (any partial frame) and continues when a new chunk
1426
- * is pushed.
1427
- *
1428
- * Typically the higher level function will convert headers to Hash, handle unescaping of header values
1429
- * (which is protocol version specific), and convert body to text.
1430
- *
1431
- * Check the parser.spec.js to understand cases that this parser is supposed to handle.
1432
- *
1433
- * Part of `@stomp/stompjs`.
1434
- *
1435
- * @internal
1436
- */
1437
- class Parser {
1438
- constructor(onFrame, onIncomingPing) {
1439
- this.onFrame = onFrame;
1440
- this.onIncomingPing = onIncomingPing;
1441
- this._encoder = new TextEncoder();
1442
- this._decoder = new TextDecoder();
1443
- this._token = [];
1444
- this._initState();
1445
- }
1446
- parseChunk(segment, appendMissingNULLonIncoming = false) {
1447
- let chunk;
1448
- if (segment instanceof ArrayBuffer) {
1449
- chunk = new Uint8Array(segment);
1450
- }
1451
- else {
1452
- chunk = this._encoder.encode(segment);
1453
- }
1454
- // See https://github.com/stomp-js/stompjs/issues/89
1455
- // Remove when underlying issue is fixed.
1456
- //
1457
- // Send a NULL byte, if the last byte of a Text frame was not NULL.F
1458
- if (appendMissingNULLonIncoming && chunk[chunk.length - 1] !== 0) {
1459
- const chunkWithNull = new Uint8Array(chunk.length + 1);
1460
- chunkWithNull.set(chunk, 0);
1461
- chunkWithNull[chunk.length] = 0;
1462
- chunk = chunkWithNull;
1463
- }
1464
- // tslint:disable-next-line:prefer-for-of
1465
- for (let i = 0; i < chunk.length; i++) {
1466
- const byte = chunk[i];
1467
- this._onByte(byte);
1468
- }
1469
- }
1470
- // The following implements a simple Rec Descent Parser.
1471
- // The grammar is simple and just one byte tells what should be the next state
1472
- _collectFrame(byte) {
1473
- if (byte === NULL) {
1474
- // Ignore
1475
- return;
1476
- }
1477
- if (byte === CR) {
1478
- // Ignore CR
1479
- return;
1480
- }
1481
- if (byte === LF) {
1482
- // Incoming Ping
1483
- this.onIncomingPing();
1484
- return;
1485
- }
1486
- this._onByte = this._collectCommand;
1487
- this._reinjectByte(byte);
1488
- }
1489
- _collectCommand(byte) {
1490
- if (byte === CR) {
1491
- // Ignore CR
1492
- return;
1493
- }
1494
- if (byte === LF) {
1495
- this._results.command = this._consumeTokenAsUTF8();
1496
- this._onByte = this._collectHeaders;
1497
- return;
1498
- }
1499
- this._consumeByte(byte);
1500
- }
1501
- _collectHeaders(byte) {
1502
- if (byte === CR) {
1503
- // Ignore CR
1504
- return;
1505
- }
1506
- if (byte === LF) {
1507
- this._setupCollectBody();
1508
- return;
1509
- }
1510
- this._onByte = this._collectHeaderKey;
1511
- this._reinjectByte(byte);
1512
- }
1513
- _reinjectByte(byte) {
1514
- this._onByte(byte);
1515
- }
1516
- _collectHeaderKey(byte) {
1517
- if (byte === COLON) {
1518
- this._headerKey = this._consumeTokenAsUTF8();
1519
- this._onByte = this._collectHeaderValue;
1520
- return;
1521
- }
1522
- this._consumeByte(byte);
1523
- }
1524
- _collectHeaderValue(byte) {
1525
- if (byte === CR) {
1526
- // Ignore CR
1527
- return;
1528
- }
1529
- if (byte === LF) {
1530
- this._results.headers.push([this._headerKey, this._consumeTokenAsUTF8()]);
1531
- this._headerKey = undefined;
1532
- this._onByte = this._collectHeaders;
1533
- return;
1504
+ class HeartbeatInfo {
1505
+ constructor(client) {
1506
+ this.client = client;
1534
1507
  }
1535
- this._consumeByte(byte);
1536
- }
1537
- _setupCollectBody() {
1538
- const contentLengthHeader = this._results.headers.filter((header) => {
1539
- return header[0] === 'content-length';
1540
- })[0];
1541
- if (contentLengthHeader) {
1542
- this._bodyBytesRemaining = parseInt(contentLengthHeader[1], 10);
1543
- this._onByte = this._collectBodyFixedSize;
1508
+ get outgoing() {
1509
+ return this.client.heartbeatOutgoing;
1544
1510
  }
1545
- else {
1546
- this._onByte = this._collectBodyNullTerminated;
1511
+ set outgoing(value) {
1512
+ this.client.heartbeatOutgoing = value;
1547
1513
  }
1548
- }
1549
- _collectBodyNullTerminated(byte) {
1550
- if (byte === NULL) {
1551
- this._retrievedBody();
1552
- return;
1514
+ get incoming() {
1515
+ return this.client.heartbeatIncoming;
1553
1516
  }
1554
- this._consumeByte(byte);
1555
- }
1556
- _collectBodyFixedSize(byte) {
1557
- // It is post decrement, so that we discard the trailing NULL octet
1558
- if (this._bodyBytesRemaining-- === 0) {
1559
- this._retrievedBody();
1560
- return;
1517
+ set incoming(value) {
1518
+ this.client.heartbeatIncoming = value;
1561
1519
  }
1562
- this._consumeByte(byte);
1563
- }
1564
- _retrievedBody() {
1565
- this._results.binaryBody = this._consumeTokenAsRaw();
1566
- this.onFrame(this._results);
1567
- this._initState();
1568
- }
1569
- // Rec Descent Parser helpers
1570
- _consumeByte(byte) {
1571
- this._token.push(byte);
1572
- }
1573
- _consumeTokenAsUTF8() {
1574
- return this._decoder.decode(this._consumeTokenAsRaw());
1575
- }
1576
- _consumeTokenAsRaw() {
1577
- const rawResult = new Uint8Array(this._token);
1578
- this._token = [];
1579
- return rawResult;
1580
1520
  }
1581
- _initState() {
1582
- this._results = {
1583
- command: undefined,
1584
- headers: [],
1585
- binaryBody: undefined,
1586
- };
1587
- this._token = [];
1588
- this._headerKey = undefined;
1589
- this._onByte = this._collectFrame;
1590
- }
1591
- }
1592
-
1593
-
1594
- /***/ }),
1595
-
1596
- /***/ "./src/stomp-config.ts":
1597
- /*!*****************************!*\
1598
- !*** ./src/stomp-config.ts ***!
1599
- \*****************************/
1600
- /*! exports provided: StompConfig */
1601
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1602
-
1603
- "use strict";
1604
- __webpack_require__.r(__webpack_exports__);
1605
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StompConfig", function() { return StompConfig; });
1606
- /**
1607
- * Configuration options for STOMP Client, each key corresponds to
1608
- * field by the same name in {@link Client}. This can be passed to
1609
- * the constructor of {@link Client} or to [Client#configure]{@link Client#configure}.
1610
- *
1611
- * There used to be a class with the same name in `@stomp/ng2-stompjs`, which has been replaced by
1612
- * {@link RxStompConfig} and {@link InjectableRxStompConfig}.
1613
- *
1614
- * Part of `@stomp/stompjs`.
1615
- */
1616
- class StompConfig {
1617
- }
1618
-
1619
-
1620
- /***/ }),
1621
1521
 
1622
- /***/ "./src/stomp-handler.ts":
1623
- /*!******************************!*\
1624
- !*** ./src/stomp-handler.ts ***!
1625
- \******************************/
1626
- /*! exports provided: StompHandler */
1627
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1628
-
1629
- "use strict";
1630
- __webpack_require__.r(__webpack_exports__);
1631
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StompHandler", function() { return StompHandler; });
1632
- /* harmony import */ var _byte__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./byte */ "./src/byte.ts");
1633
- /* harmony import */ var _frame_impl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./frame-impl */ "./src/frame-impl.ts");
1634
- /* harmony import */ var _parser__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./parser */ "./src/parser.ts");
1635
- /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./types */ "./src/types.ts");
1636
- /* harmony import */ var _versions__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./versions */ "./src/versions.ts");
1637
- /* harmony import */ var _augment_websocket__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./augment-websocket */ "./src/augment-websocket.ts");
1638
-
1639
-
1640
-
1641
-
1642
-
1643
-
1644
- /**
1645
- * The STOMP protocol handler
1646
- *
1647
- * Part of `@stomp/stompjs`.
1648
- *
1649
- * @internal
1650
- */
1651
- class StompHandler {
1652
- constructor(_client, _webSocket, config = {}) {
1653
- this._client = _client;
1654
- this._webSocket = _webSocket;
1655
- this._serverFrameHandlers = {
1656
- // [CONNECTED Frame](http://stomp.github.com/stomp-specification-1.2.html#CONNECTED_Frame)
1657
- CONNECTED: frame => {
1658
- this.debug(`connected to server ${frame.headers.server}`);
1659
- this._connected = true;
1660
- this._connectedVersion = frame.headers.version;
1661
- // STOMP version 1.2 needs header values to be escaped
1662
- if (this._connectedVersion === _versions__WEBPACK_IMPORTED_MODULE_4__["Versions"].V1_2) {
1663
- this._escapeHeaderValues = true;
1664
- }
1665
- this._setupHeartbeat(frame.headers);
1666
- this.onConnect(frame);
1667
- },
1668
- // [MESSAGE Frame](http://stomp.github.com/stomp-specification-1.2.html#MESSAGE)
1669
- MESSAGE: frame => {
1670
- // the callback is registered when the client calls
1671
- // `subscribe()`.
1672
- // If there is no registered subscription for the received message,
1673
- // the default `onUnhandledMessage` callback is used that the client can set.
1674
- // This is useful for subscriptions that are automatically created
1675
- // on the browser side (e.g. [RabbitMQ's temporary
1676
- // queues](http://www.rabbitmq.com/stomp.html)).
1677
- const subscription = frame.headers.subscription;
1678
- const onReceive = this._subscriptions[subscription] || this.onUnhandledMessage;
1679
- // bless the frame to be a Message
1680
- const message = frame;
1681
- const client = this;
1682
- const messageId = this._connectedVersion === _versions__WEBPACK_IMPORTED_MODULE_4__["Versions"].V1_2
1683
- ? message.headers.ack
1684
- : message.headers['message-id'];
1685
- // add `ack()` and `nack()` methods directly to the returned frame
1686
- // so that a simple call to `message.ack()` can acknowledge the message.
1687
- message.ack = (headers = {}) => {
1688
- return client.ack(messageId, subscription, headers);
1689
- };
1690
- message.nack = (headers = {}) => {
1691
- return client.nack(messageId, subscription, headers);
1692
- };
1693
- onReceive(message);
1694
- },
1695
- // [RECEIPT Frame](http://stomp.github.com/stomp-specification-1.2.html#RECEIPT)
1696
- RECEIPT: frame => {
1697
- const callback = this._receiptWatchers[frame.headers['receipt-id']];
1698
- if (callback) {
1699
- callback(frame);
1700
- // Server will acknowledge only once, remove the callback
1701
- delete this._receiptWatchers[frame.headers['receipt-id']];
1702
- }
1703
- else {
1704
- this.onUnhandledReceipt(frame);
1705
- }
1706
- },
1707
- // [ERROR Frame](http://stomp.github.com/stomp-specification-1.2.html#ERROR)
1708
- ERROR: frame => {
1709
- this.onStompError(frame);
1710
- },
1711
- };
1712
- // used to index subscribers
1713
- this._counter = 0;
1714
- // subscription callbacks indexed by subscriber's ID
1715
- this._subscriptions = {};
1716
- // receipt-watchers indexed by receipts-ids
1717
- this._receiptWatchers = {};
1718
- this._partialData = '';
1719
- this._escapeHeaderValues = false;
1720
- this._lastServerActivityTS = Date.now();
1721
- this.configure(config);
1722
- }
1723
- get connectedVersion() {
1724
- return this._connectedVersion;
1725
- }
1726
- get connected() {
1727
- return this._connected;
1728
- }
1729
- configure(conf) {
1730
- // bulk assign all properties to this
1731
- Object.assign(this, conf);
1732
- }
1733
- start() {
1734
- const parser = new _parser__WEBPACK_IMPORTED_MODULE_2__["Parser"](
1735
- // On Frame
1736
- rawFrame => {
1737
- const frame = _frame_impl__WEBPACK_IMPORTED_MODULE_1__["FrameImpl"].fromRawFrame(rawFrame, this._escapeHeaderValues);
1738
- // if this.logRawCommunication is set, the rawChunk is logged at this._webSocket.onmessage
1739
- if (!this.logRawCommunication) {
1740
- this.debug(`<<< ${frame}`);
1741
- }
1742
- const serverFrameHandler = this._serverFrameHandlers[frame.command] || this.onUnhandledFrame;
1743
- serverFrameHandler(frame);
1744
- },
1745
- // On Incoming Ping
1746
- () => {
1747
- this.debug('<<< PONG');
1748
- });
1749
- this._webSocket.onmessage = (evt) => {
1750
- this.debug('Received data');
1751
- this._lastServerActivityTS = Date.now();
1752
- if (this.logRawCommunication) {
1753
- const rawChunkAsString = evt.data instanceof ArrayBuffer
1754
- ? new TextDecoder().decode(evt.data)
1755
- : evt.data;
1756
- this.debug(`<<< ${rawChunkAsString}`);
1522
+ /**
1523
+ * Available for backward compatibility, please shift to using {@link Client}.
1524
+ *
1525
+ * **Deprecated**
1526
+ *
1527
+ * Part of `@stomp/stompjs`.
1528
+ *
1529
+ * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html)
1530
+ */
1531
+ class CompatClient extends Client {
1532
+ /**
1533
+ * Available for backward compatibility, please shift to using {@link Client}
1534
+ * and [Client#webSocketFactory]{@link Client#webSocketFactory}.
1535
+ *
1536
+ * **Deprecated**
1537
+ *
1538
+ * @internal
1539
+ */
1540
+ constructor(webSocketFactory) {
1541
+ super();
1542
+ /**
1543
+ * It is no op now. No longer needed. Large packets work out of the box.
1544
+ */
1545
+ this.maxWebSocketFrameSize = 16 * 1024;
1546
+ this._heartbeatInfo = new HeartbeatInfo(this);
1547
+ this.reconnect_delay = 0;
1548
+ this.webSocketFactory = webSocketFactory;
1549
+ // Default from previous version
1550
+ this.debug = (...message) => {
1551
+ console.log(...message);
1552
+ };
1553
+ }
1554
+ _parseConnect(...args) {
1555
+ let closeEventCallback;
1556
+ let connectCallback;
1557
+ let errorCallback;
1558
+ let headers = {};
1559
+ if (args.length < 2) {
1560
+ throw new Error('Connect requires at least 2 arguments');
1757
1561
  }
1758
- parser.parseChunk(evt.data, this.appendMissingNULLonIncoming);
1759
- };
1760
- this._onclose = (closeEvent) => {
1761
- this.debug(`Connection closed to ${this._client.brokerURL}`);
1762
- this._cleanUp();
1763
- this.onWebSocketClose(closeEvent);
1764
- };
1765
- this._webSocket.onclose = this._onclose;
1766
- this._webSocket.onerror = (errorEvent) => {
1767
- this.onWebSocketError(errorEvent);
1768
- };
1769
- this._webSocket.onopen = () => {
1770
- // Clone before updating
1771
- const connectHeaders = Object.assign({}, this.connectHeaders);
1772
- this.debug('Web Socket Opened...');
1773
- connectHeaders['accept-version'] = this.stompVersions.supportedVersions();
1774
- connectHeaders['heart-beat'] = [
1775
- this.heartbeatOutgoing,
1776
- this.heartbeatIncoming,
1777
- ].join(',');
1778
- this._transmit({ command: 'CONNECT', headers: connectHeaders });
1779
- };
1780
- }
1781
- _setupHeartbeat(headers) {
1782
- if (headers.version !== _versions__WEBPACK_IMPORTED_MODULE_4__["Versions"].V1_1 &&
1783
- headers.version !== _versions__WEBPACK_IMPORTED_MODULE_4__["Versions"].V1_2) {
1784
- return;
1785
- }
1786
- // It is valid for the server to not send this header
1787
- // https://stomp.github.io/stomp-specification-1.2.html#Heart-beating
1788
- if (!headers['heart-beat']) {
1789
- return;
1790
- }
1791
- // heart-beat header received from the server looks like:
1792
- //
1793
- // heart-beat: sx, sy
1794
- const [serverOutgoing, serverIncoming] = headers['heart-beat']
1795
- .split(',')
1796
- .map((v) => parseInt(v, 10));
1797
- if (this.heartbeatOutgoing !== 0 && serverIncoming !== 0) {
1798
- const ttl = Math.max(this.heartbeatOutgoing, serverIncoming);
1799
- this.debug(`send PING every ${ttl}ms`);
1800
- this._pinger = setInterval(() => {
1801
- if (this._webSocket.readyState === _types__WEBPACK_IMPORTED_MODULE_3__["StompSocketState"].OPEN) {
1802
- this._webSocket.send(_byte__WEBPACK_IMPORTED_MODULE_0__["BYTE"].LF);
1803
- this.debug('>>> PING');
1804
- }
1805
- }, ttl);
1806
- }
1807
- if (this.heartbeatIncoming !== 0 && serverOutgoing !== 0) {
1808
- const ttl = Math.max(this.heartbeatIncoming, serverOutgoing);
1809
- this.debug(`check PONG every ${ttl}ms`);
1810
- this._ponger = setInterval(() => {
1811
- const delta = Date.now() - this._lastServerActivityTS;
1812
- // We wait twice the TTL to be flexible on window's setInterval calls
1813
- if (delta > ttl * 2) {
1814
- this.debug(`did not receive server activity for the last ${delta}ms`);
1815
- this._closeOrDiscardWebsocket();
1562
+ if (typeof args[1] === 'function') {
1563
+ [headers, connectCallback, errorCallback, closeEventCallback] = args;
1564
+ }
1565
+ else {
1566
+ switch (args.length) {
1567
+ case 6:
1568
+ [
1569
+ headers.login,
1570
+ headers.passcode,
1571
+ connectCallback,
1572
+ errorCallback,
1573
+ closeEventCallback,
1574
+ headers.host,
1575
+ ] = args;
1576
+ break;
1577
+ default:
1578
+ [
1579
+ headers.login,
1580
+ headers.passcode,
1581
+ connectCallback,
1582
+ errorCallback,
1583
+ closeEventCallback,
1584
+ ] = args;
1816
1585
  }
1817
- }, ttl);
1818
- }
1819
- }
1820
- _closeOrDiscardWebsocket() {
1821
- if (this.discardWebsocketOnCommFailure) {
1822
- this.debug('Discarding websocket, the underlying socket may linger for a while');
1823
- this._discardWebsocket();
1824
- }
1825
- else {
1826
- this.debug('Issuing close on the websocket');
1827
- this._closeWebsocket();
1828
- }
1829
- }
1830
- forceDisconnect() {
1831
- if (this._webSocket) {
1832
- if (this._webSocket.readyState === _types__WEBPACK_IMPORTED_MODULE_3__["StompSocketState"].CONNECTING ||
1833
- this._webSocket.readyState === _types__WEBPACK_IMPORTED_MODULE_3__["StompSocketState"].OPEN) {
1834
- this._closeOrDiscardWebsocket();
1835
1586
  }
1587
+ return [headers, connectCallback, errorCallback, closeEventCallback];
1836
1588
  }
1837
- }
1838
- _closeWebsocket() {
1839
- this._webSocket.onmessage = () => { }; // ignore messages
1840
- this._webSocket.close();
1841
- }
1842
- _discardWebsocket() {
1843
- if (!this._webSocket.terminate) {
1844
- Object(_augment_websocket__WEBPACK_IMPORTED_MODULE_5__["augmentWebsocket"])(this._webSocket, (msg) => this.debug(msg));
1845
- }
1846
- this._webSocket.terminate();
1847
- }
1848
- _transmit(params) {
1849
- const { command, headers, body, binaryBody, skipContentLengthHeader } = params;
1850
- const frame = new _frame_impl__WEBPACK_IMPORTED_MODULE_1__["FrameImpl"]({
1851
- command,
1852
- headers,
1853
- body,
1854
- binaryBody,
1855
- escapeHeaderValues: this._escapeHeaderValues,
1856
- skipContentLengthHeader,
1857
- });
1858
- let rawChunk = frame.serialize();
1859
- if (this.logRawCommunication) {
1860
- this.debug(`>>> ${rawChunk}`);
1861
- }
1862
- else {
1863
- this.debug(`>>> ${frame}`);
1864
- }
1865
- if (this.forceBinaryWSFrames && typeof rawChunk === 'string') {
1866
- rawChunk = new TextEncoder().encode(rawChunk);
1867
- }
1868
- if (typeof rawChunk !== 'string' || !this.splitLargeFrames) {
1869
- this._webSocket.send(rawChunk);
1870
- }
1871
- else {
1872
- let out = rawChunk;
1873
- while (out.length > 0) {
1874
- const chunk = out.substring(0, this.maxWebSocketChunkSize);
1875
- out = out.substring(this.maxWebSocketChunkSize);
1876
- this._webSocket.send(chunk);
1877
- this.debug(`chunk sent = ${chunk.length}, remaining = ${out.length}`);
1589
+ /**
1590
+ * Available for backward compatibility, please shift to using [Client#activate]{@link Client#activate}.
1591
+ *
1592
+ * **Deprecated**
1593
+ *
1594
+ * The `connect` method accepts different number of arguments and types. See the Overloads list. Use the
1595
+ * version with headers to pass your broker specific options.
1596
+ *
1597
+ * overloads:
1598
+ * - connect(headers, connectCallback)
1599
+ * - connect(headers, connectCallback, errorCallback)
1600
+ * - connect(login, passcode, connectCallback)
1601
+ * - connect(login, passcode, connectCallback, errorCallback)
1602
+ * - connect(login, passcode, connectCallback, errorCallback, closeEventCallback)
1603
+ * - connect(login, passcode, connectCallback, errorCallback, closeEventCallback, host)
1604
+ *
1605
+ * params:
1606
+ * - headers, see [Client#connectHeaders]{@link Client#connectHeaders}
1607
+ * - connectCallback, see [Client#onConnect]{@link Client#onConnect}
1608
+ * - errorCallback, see [Client#onStompError]{@link Client#onStompError}
1609
+ * - closeEventCallback, see [Client#onWebSocketClose]{@link Client#onWebSocketClose}
1610
+ * - login [String], see [Client#connectHeaders](../classes/Client.html#connectHeaders)
1611
+ * - passcode [String], [Client#connectHeaders](../classes/Client.html#connectHeaders)
1612
+ * - host [String], see [Client#connectHeaders](../classes/Client.html#connectHeaders)
1613
+ *
1614
+ * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html)
1615
+ */
1616
+ connect(...args) {
1617
+ const out = this._parseConnect(...args);
1618
+ if (out[0]) {
1619
+ this.connectHeaders = out[0];
1878
1620
  }
1879
- }
1880
- }
1881
- dispose() {
1882
- if (this.connected) {
1883
- try {
1884
- // clone before updating
1885
- const disconnectHeaders = Object.assign({}, this.disconnectHeaders);
1886
- if (!disconnectHeaders.receipt) {
1887
- disconnectHeaders.receipt = `close-${this._counter++}`;
1888
- }
1889
- this.watchForReceipt(disconnectHeaders.receipt, frame => {
1890
- this._closeWebsocket();
1891
- this._cleanUp();
1892
- this.onDisconnect(frame);
1893
- });
1894
- this._transmit({ command: 'DISCONNECT', headers: disconnectHeaders });
1621
+ if (out[1]) {
1622
+ this.onConnect = out[1];
1895
1623
  }
1896
- catch (error) {
1897
- this.debug(`Ignoring error during disconnect ${error}`);
1624
+ if (out[2]) {
1625
+ this.onStompError = out[2];
1898
1626
  }
1627
+ if (out[3]) {
1628
+ this.onWebSocketClose = out[3];
1629
+ }
1630
+ super.activate();
1899
1631
  }
1900
- else {
1901
- if (this._webSocket.readyState === _types__WEBPACK_IMPORTED_MODULE_3__["StompSocketState"].CONNECTING ||
1902
- this._webSocket.readyState === _types__WEBPACK_IMPORTED_MODULE_3__["StompSocketState"].OPEN) {
1903
- this._closeWebsocket();
1632
+ /**
1633
+ * Available for backward compatibility, please shift to using [Client#deactivate]{@link Client#deactivate}.
1634
+ *
1635
+ * **Deprecated**
1636
+ *
1637
+ * See:
1638
+ * [Client#onDisconnect]{@link Client#onDisconnect}, and
1639
+ * [Client#disconnectHeaders]{@link Client#disconnectHeaders}
1640
+ *
1641
+ * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html)
1642
+ */
1643
+ disconnect(disconnectCallback, headers = {}) {
1644
+ if (disconnectCallback) {
1645
+ this.onDisconnect = disconnectCallback;
1904
1646
  }
1647
+ this.disconnectHeaders = headers;
1648
+ super.deactivate();
1905
1649
  }
1906
- }
1907
- _cleanUp() {
1908
- this._connected = false;
1909
- if (this._pinger) {
1910
- clearInterval(this._pinger);
1650
+ /**
1651
+ * Available for backward compatibility, use [Client#publish]{@link Client#publish}.
1652
+ *
1653
+ * Send a message to a named destination. Refer to your STOMP broker documentation for types
1654
+ * and naming of destinations. The headers will, typically, be available to the subscriber.
1655
+ * However, there may be special purpose headers corresponding to your STOMP broker.
1656
+ *
1657
+ * **Deprecated**, use [Client#publish]{@link Client#publish}
1658
+ *
1659
+ * Note: Body must be String. You will need to covert the payload to string in case it is not string (e.g. JSON)
1660
+ *
1661
+ * ```javascript
1662
+ * client.send("/queue/test", {priority: 9}, "Hello, STOMP");
1663
+ *
1664
+ * // If you want to send a message with a body, you must also pass the headers argument.
1665
+ * client.send("/queue/test", {}, "Hello, STOMP");
1666
+ * ```
1667
+ *
1668
+ * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html)
1669
+ */
1670
+ send(destination, headers = {}, body = '') {
1671
+ headers = Object.assign({}, headers);
1672
+ const skipContentLengthHeader = headers['content-length'] === false;
1673
+ if (skipContentLengthHeader) {
1674
+ delete headers['content-length'];
1675
+ }
1676
+ this.publish({
1677
+ destination,
1678
+ headers: headers,
1679
+ body,
1680
+ skipContentLengthHeader,
1681
+ });
1911
1682
  }
1912
- if (this._ponger) {
1913
- clearInterval(this._ponger);
1683
+ /**
1684
+ * Available for backward compatibility, renamed to [Client#reconnectDelay]{@link Client#reconnectDelay}.
1685
+ *
1686
+ * **Deprecated**
1687
+ */
1688
+ set reconnect_delay(value) {
1689
+ this.reconnectDelay = value;
1914
1690
  }
1915
- }
1916
- publish(params) {
1917
- const { destination, headers, body, binaryBody, skipContentLengthHeader } = params;
1918
- const hdrs = Object.assign({ destination }, headers);
1919
- this._transmit({
1920
- command: 'SEND',
1921
- headers: hdrs,
1922
- body,
1923
- binaryBody,
1924
- skipContentLengthHeader,
1925
- });
1926
- }
1927
- watchForReceipt(receiptId, callback) {
1928
- this._receiptWatchers[receiptId] = callback;
1929
- }
1930
- subscribe(destination, callback, headers = {}) {
1931
- headers = Object.assign({}, headers);
1932
- if (!headers.id) {
1933
- headers.id = `sub-${this._counter++}`;
1934
- }
1935
- headers.destination = destination;
1936
- this._subscriptions[headers.id] = callback;
1937
- this._transmit({ command: 'SUBSCRIBE', headers });
1938
- const client = this;
1939
- return {
1940
- id: headers.id,
1941
- unsubscribe(hdrs) {
1942
- return client.unsubscribe(headers.id, hdrs);
1943
- },
1944
- };
1945
- }
1946
- unsubscribe(id, headers = {}) {
1947
- headers = Object.assign({}, headers);
1948
- delete this._subscriptions[id];
1949
- headers.id = id;
1950
- this._transmit({ command: 'UNSUBSCRIBE', headers });
1951
- }
1952
- begin(transactionId) {
1953
- const txId = transactionId || `tx-${this._counter++}`;
1954
- this._transmit({
1955
- command: 'BEGIN',
1956
- headers: {
1957
- transaction: txId,
1958
- },
1959
- });
1960
- const client = this;
1961
- return {
1962
- id: txId,
1963
- commit() {
1964
- client.commit(txId);
1965
- },
1966
- abort() {
1967
- client.abort(txId);
1968
- },
1969
- };
1970
- }
1971
- commit(transactionId) {
1972
- this._transmit({
1973
- command: 'COMMIT',
1974
- headers: {
1975
- transaction: transactionId,
1976
- },
1977
- });
1978
- }
1979
- abort(transactionId) {
1980
- this._transmit({
1981
- command: 'ABORT',
1982
- headers: {
1983
- transaction: transactionId,
1984
- },
1985
- });
1986
- }
1987
- ack(messageId, subscriptionId, headers = {}) {
1988
- headers = Object.assign({}, headers);
1989
- if (this._connectedVersion === _versions__WEBPACK_IMPORTED_MODULE_4__["Versions"].V1_2) {
1990
- headers.id = messageId;
1691
+ /**
1692
+ * Available for backward compatibility, renamed to [Client#webSocket]{@link Client#webSocket}.
1693
+ *
1694
+ * **Deprecated**
1695
+ */
1696
+ get ws() {
1697
+ return this.webSocket;
1991
1698
  }
1992
- else {
1993
- headers['message-id'] = messageId;
1699
+ /**
1700
+ * Available for backward compatibility, renamed to [Client#connectedVersion]{@link Client#connectedVersion}.
1701
+ *
1702
+ * **Deprecated**
1703
+ */
1704
+ get version() {
1705
+ return this.connectedVersion;
1994
1706
  }
1995
- headers.subscription = subscriptionId;
1996
- this._transmit({ command: 'ACK', headers });
1997
- }
1998
- nack(messageId, subscriptionId, headers = {}) {
1999
- headers = Object.assign({}, headers);
2000
- if (this._connectedVersion === _versions__WEBPACK_IMPORTED_MODULE_4__["Versions"].V1_2) {
2001
- headers.id = messageId;
1707
+ /**
1708
+ * Available for backward compatibility, renamed to [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}.
1709
+ *
1710
+ * **Deprecated**
1711
+ */
1712
+ get onreceive() {
1713
+ return this.onUnhandledMessage;
1714
+ }
1715
+ /**
1716
+ * Available for backward compatibility, renamed to [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}.
1717
+ *
1718
+ * **Deprecated**
1719
+ */
1720
+ set onreceive(value) {
1721
+ this.onUnhandledMessage = value;
1722
+ }
1723
+ /**
1724
+ * Available for backward compatibility, renamed to [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}.
1725
+ * Prefer using [Client#watchForReceipt]{@link Client#watchForReceipt}.
1726
+ *
1727
+ * **Deprecated**
1728
+ */
1729
+ get onreceipt() {
1730
+ return this.onUnhandledReceipt;
1731
+ }
1732
+ /**
1733
+ * Available for backward compatibility, renamed to [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}.
1734
+ *
1735
+ * **Deprecated**
1736
+ */
1737
+ set onreceipt(value) {
1738
+ this.onUnhandledReceipt = value;
1739
+ }
1740
+ /**
1741
+ * Available for backward compatibility, renamed to [Client#heartbeatIncoming]{@link Client#heartbeatIncoming}
1742
+ * [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}.
1743
+ *
1744
+ * **Deprecated**
1745
+ */
1746
+ get heartbeat() {
1747
+ return this._heartbeatInfo;
2002
1748
  }
2003
- else {
2004
- headers['message-id'] = messageId;
1749
+ /**
1750
+ * Available for backward compatibility, renamed to [Client#heartbeatIncoming]{@link Client#heartbeatIncoming}
1751
+ * [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}.
1752
+ *
1753
+ * **Deprecated**
1754
+ */
1755
+ set heartbeat(value) {
1756
+ this.heartbeatIncoming = value.incoming;
1757
+ this.heartbeatOutgoing = value.outgoing;
2005
1758
  }
2006
- headers.subscription = subscriptionId;
2007
- return this._transmit({ command: 'NACK', headers });
2008
1759
  }
2009
- }
2010
-
2011
-
2012
- /***/ }),
2013
-
2014
- /***/ "./src/stomp-headers.ts":
2015
- /*!******************************!*\
2016
- !*** ./src/stomp-headers.ts ***!
2017
- \******************************/
2018
- /*! exports provided: StompHeaders */
2019
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
2020
-
2021
- "use strict";
2022
- __webpack_require__.r(__webpack_exports__);
2023
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StompHeaders", function() { return StompHeaders; });
2024
- /**
2025
- * STOMP headers. Many functions calls will accept headers as parameters.
2026
- * The headers sent by Broker will be available as [IFrame#headers]{@link IFrame#headers}.
2027
- *
2028
- * `key` and `value` must be valid strings.
2029
- * In addition, `key` must not contain `CR`, `LF`, or `:`.
2030
- *
2031
- * Part of `@stomp/stompjs`.
2032
- */
2033
- class StompHeaders {
2034
- }
2035
-
2036
-
2037
- /***/ }),
2038
-
2039
- /***/ "./src/stomp-subscription.ts":
2040
- /*!***********************************!*\
2041
- !*** ./src/stomp-subscription.ts ***!
2042
- \***********************************/
2043
- /*! exports provided: StompSubscription */
2044
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
2045
-
2046
- "use strict";
2047
- __webpack_require__.r(__webpack_exports__);
2048
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StompSubscription", function() { return StompSubscription; });
2049
- /**
2050
- * Call [Client#subscribe]{@link Client#subscribe} to create a StompSubscription.
2051
- *
2052
- * Part of `@stomp/stompjs`.
2053
- */
2054
- class StompSubscription {
2055
- }
2056
-
2057
-
2058
- /***/ }),
2059
-
2060
- /***/ "./src/types.ts":
2061
- /*!**********************!*\
2062
- !*** ./src/types.ts ***!
2063
- \**********************/
2064
- /*! exports provided: StompSocketState, ActivationState */
2065
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
2066
-
2067
- "use strict";
2068
- __webpack_require__.r(__webpack_exports__);
2069
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StompSocketState", function() { return StompSocketState; });
2070
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ActivationState", function() { return ActivationState; });
2071
- /**
2072
- * Possible states for the IStompSocket
2073
- */
2074
- var StompSocketState;
2075
- (function (StompSocketState) {
2076
- StompSocketState[StompSocketState["CONNECTING"] = 0] = "CONNECTING";
2077
- StompSocketState[StompSocketState["OPEN"] = 1] = "OPEN";
2078
- StompSocketState[StompSocketState["CLOSING"] = 2] = "CLOSING";
2079
- StompSocketState[StompSocketState["CLOSED"] = 3] = "CLOSED";
2080
- })(StompSocketState || (StompSocketState = {}));
2081
- /**
2082
- * Possible activation state
2083
- */
2084
- var ActivationState;
2085
- (function (ActivationState) {
2086
- ActivationState[ActivationState["ACTIVE"] = 0] = "ACTIVE";
2087
- ActivationState[ActivationState["DEACTIVATING"] = 1] = "DEACTIVATING";
2088
- ActivationState[ActivationState["INACTIVE"] = 2] = "INACTIVE";
2089
- })(ActivationState || (ActivationState = {}));
2090
1760
 
2091
-
2092
- /***/ }),
2093
-
2094
- /***/ "./src/versions.ts":
2095
- /*!*************************!*\
2096
- !*** ./src/versions.ts ***!
2097
- \*************************/
2098
- /*! exports provided: Versions */
2099
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
2100
-
2101
- "use strict";
2102
- __webpack_require__.r(__webpack_exports__);
2103
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Versions", function() { return Versions; });
2104
- /**
2105
- * Supported STOMP versions
2106
- *
2107
- * Part of `@stomp/stompjs`.
2108
- */
2109
- class Versions {
2110
1761
  /**
2111
- * Takes an array of string of versions, typical elements '1.0', '1.1', or '1.2'
1762
+ * STOMP Class, acts like a factory to create {@link Client}.
2112
1763
  *
2113
- * You will an instance if this class if you want to override supported versions to be declared during
2114
- * STOMP handshake.
2115
- */
2116
- constructor(versions) {
2117
- this.versions = versions;
2118
- }
2119
- /**
2120
- * Used as part of CONNECT STOMP Frame
1764
+ * Part of `@stomp/stompjs`.
1765
+ *
1766
+ * **Deprecated**
1767
+ *
1768
+ * It will be removed in next major version. Please switch to {@link Client}.
2121
1769
  */
2122
- supportedVersions() {
2123
- return this.versions.join(',');
1770
+ class Stomp {
1771
+ /**
1772
+ * This method creates a WebSocket client that is connected to
1773
+ * the STOMP server located at the url.
1774
+ *
1775
+ * ```javascript
1776
+ * var url = "ws://localhost:61614/stomp";
1777
+ * var client = Stomp.client(url);
1778
+ * ```
1779
+ *
1780
+ * **Deprecated**
1781
+ *
1782
+ * It will be removed in next major version. Please switch to {@link Client}
1783
+ * using [Client#brokerURL]{@link Client#brokerURL}.
1784
+ */
1785
+ static client(url, protocols) {
1786
+ // This is a hack to allow another implementation than the standard
1787
+ // HTML5 WebSocket class.
1788
+ //
1789
+ // It is possible to use another class by calling
1790
+ //
1791
+ // Stomp.WebSocketClass = MozWebSocket
1792
+ //
1793
+ // *prior* to call `Stomp.client()`.
1794
+ //
1795
+ // This hack is deprecated and `Stomp.over()` method should be used
1796
+ // instead.
1797
+ // See remarks on the function Stomp.over
1798
+ if (protocols == null) {
1799
+ protocols = Versions.default.protocolVersions();
1800
+ }
1801
+ const wsFn = () => {
1802
+ const klass = Stomp.WebSocketClass || WebSocket;
1803
+ return new klass(url, protocols);
1804
+ };
1805
+ return new CompatClient(wsFn);
1806
+ }
1807
+ /**
1808
+ * This method is an alternative to [Stomp#client]{@link Stomp#client} to let the user
1809
+ * specify the WebSocket to use (either a standard HTML5 WebSocket or
1810
+ * a similar object).
1811
+ *
1812
+ * In order to support reconnection, the function Client._connect should be callable more than once.
1813
+ * While reconnecting
1814
+ * a new instance of underlying transport (TCP Socket, WebSocket or SockJS) will be needed. So, this function
1815
+ * alternatively allows passing a function that should return a new instance of the underlying socket.
1816
+ *
1817
+ * ```javascript
1818
+ * var client = Stomp.over(function(){
1819
+ * return new WebSocket('ws://localhost:15674/ws')
1820
+ * });
1821
+ * ```
1822
+ *
1823
+ * **Deprecated**
1824
+ *
1825
+ * It will be removed in next major version. Please switch to {@link Client}
1826
+ * using [Client#webSocketFactory]{@link Client#webSocketFactory}.
1827
+ */
1828
+ static over(ws) {
1829
+ let wsFn;
1830
+ if (typeof ws === 'function') {
1831
+ wsFn = ws;
1832
+ }
1833
+ else {
1834
+ console.warn('Stomp.over did not receive a factory, auto reconnect will not work. ' +
1835
+ 'Please see https://stomp-js.github.io/api-docs/latest/classes/Stomp.html#over');
1836
+ wsFn = () => ws;
1837
+ }
1838
+ return new CompatClient(wsFn);
1839
+ }
2124
1840
  }
2125
1841
  /**
2126
- * Used while creating a WebSocket
1842
+ * In case you need to use a non standard class for WebSocket.
1843
+ *
1844
+ * For example when using within NodeJS environment:
1845
+ *
1846
+ * ```javascript
1847
+ * StompJs = require('../../esm5/');
1848
+ * Stomp = StompJs.Stomp;
1849
+ * Stomp.WebSocketClass = require('websocket').w3cwebsocket;
1850
+ * ```
1851
+ *
1852
+ * **Deprecated**
1853
+ *
1854
+ *
1855
+ * It will be removed in next major version. Please switch to {@link Client}
1856
+ * using [Client#webSocketFactory]{@link Client#webSocketFactory}.
2127
1857
  */
2128
- protocolVersions() {
2129
- return this.versions.map(x => `v${x.replace('.', '')}.stomp`);
2130
- }
2131
- }
2132
- /**
2133
- * Indicates protocol version 1.0
2134
- */
2135
- Versions.V1_0 = '1.0';
2136
- /**
2137
- * Indicates protocol version 1.1
2138
- */
2139
- Versions.V1_1 = '1.1';
2140
- /**
2141
- * Indicates protocol version 1.2
2142
- */
2143
- Versions.V1_2 = '1.2';
2144
- /**
2145
- * @internal
2146
- */
2147
- Versions.default = new Versions([
2148
- Versions.V1_0,
2149
- Versions.V1_1,
2150
- Versions.V1_2,
2151
- ]);
2152
-
2153
-
2154
- /***/ }),
2155
-
2156
- /***/ 0:
2157
- /*!****************************!*\
2158
- !*** multi ./src/index.ts ***!
2159
- \****************************/
2160
- /*! no static exports found */
2161
- /***/ (function(module, exports, __webpack_require__) {
2162
-
2163
- module.exports = __webpack_require__(/*! /home/kdeepak/MyWork/Tech/stomp/stompjs/src/index.ts */"./src/index.ts");
2164
-
2165
-
2166
- /***/ })
2167
-
2168
- /******/ });
2169
- });
2170
- //# sourceMappingURL=stomp.umd.js.map
1858
+ // tslint:disable-next-line:variable-name
1859
+ Stomp.WebSocketClass = null;
1860
+
1861
+ exports.Client = Client;
1862
+ exports.CompatClient = CompatClient;
1863
+ exports.FrameImpl = FrameImpl;
1864
+ exports.Parser = Parser;
1865
+ exports.Stomp = Stomp;
1866
+ exports.StompConfig = StompConfig;
1867
+ exports.StompHeaders = StompHeaders;
1868
+ exports.Versions = Versions;
1869
+
1870
+ }));
1871
+ //# sourceMappingURL=stomp.umd.js.map