1 | import { ITransaction } from './i-transaction';
|
2 | import { StompConfig } from './stomp-config';
|
3 | import { StompHandler } from './stomp-handler';
|
4 | import { StompHeaders } from './stomp-headers';
|
5 | import { StompSubscription } from './stomp-subscription';
|
6 | import {
|
7 | ActivationState,
|
8 | closeEventCallbackType,
|
9 | debugFnType,
|
10 | frameCallbackType,
|
11 | IPublishParams,
|
12 | IStompSocket,
|
13 | messageCallbackType,
|
14 | StompSocketState,
|
15 | wsErrorCallbackType,
|
16 | } from './types';
|
17 | import { Versions } from './versions';
|
18 |
|
19 | /**
|
20 | * @internal
|
21 | */
|
22 | declare const WebSocket: {
|
23 | prototype: IStompSocket;
|
24 | new (url: string, protocols?: string | string[]): IStompSocket;
|
25 | };
|
26 |
|
27 | /**
|
28 | * STOMP Client Class.
|
29 | *
|
30 | * Part of `@stomp/stompjs`.
|
31 | */
|
32 | export class Client {
|
33 | /**
|
34 | * The URL for the STOMP broker to connect to.
|
35 | * Typically like `"ws://broker.329broker.com:15674/ws"` or `"wss://broker.329broker.com:15674/ws"`.
|
36 | *
|
37 | * Only one of this or [Client#webSocketFactory]{@link Client#webSocketFactory} need to be set.
|
38 | * If both are set, [Client#webSocketFactory]{@link Client#webSocketFactory} will be used.
|
39 | *
|
40 | * If your environment does not support WebSockets natively, please refer to
|
41 | * [Polyfills]{@link https://stomp-js.github.io/guide/stompjs/rx-stomp/ng2-stompjs/pollyfils-for-stompjs-v5.html}.
|
42 | */
|
43 | public brokerURL: string;
|
44 |
|
45 | /**
|
46 | * STOMP versions to attempt during STOMP handshake. By default versions `1.0`, `1.1`, and `1.2` are attempted.
|
47 | *
|
48 | * Example:
|
49 | * ```javascript
|
50 | * // Try only versions 1.0 and 1.1
|
51 | * client.stompVersions = new Versions(['1.0', '1.1'])
|
52 | * ```
|
53 | */
|
54 | public stompVersions = Versions.default;
|
55 |
|
56 | /**
|
57 | * This function should return a WebSocket or a similar (e.g. SockJS) object.
|
58 | * If your environment does not support WebSockets natively, please refer to
|
59 | * [Polyfills]{@link https://stomp-js.github.io/guide/stompjs/rx-stomp/ng2-stompjs/pollyfils-for-stompjs-v5.html}.
|
60 | * If your STOMP Broker supports WebSockets, prefer setting [Client#brokerURL]{@link Client#brokerURL}.
|
61 | *
|
62 | * If both this and [Client#brokerURL]{@link Client#brokerURL} are set, this will be used.
|
63 | *
|
64 | * Example:
|
65 | * ```javascript
|
66 | * // use a WebSocket
|
67 | * client.webSocketFactory= function () {
|
68 | * return new WebSocket("wss://broker.329broker.com:15674/ws");
|
69 | * };
|
70 | *
|
71 | * // Typical usage with SockJS
|
72 | * client.webSocketFactory= function () {
|
73 | * return new SockJS("http://broker.329broker.com/stomp");
|
74 | * };
|
75 | * ```
|
76 | */
|
77 | public webSocketFactory: () => IStompSocket;
|
78 |
|
79 | /**
|
80 | * Will retry if Stomp connection is not established in specified milliseconds.
|
81 | * Default 0, which implies wait for ever.
|
82 | */
|
83 | public connectionTimeout: number = 0;
|
84 |
|
85 | private _connectionWatcher: number; // Timer
|
86 |
|
87 | /**
|
88 | * automatically reconnect with delay in milliseconds, set to 0 to disable.
|
89 | */
|
90 | public reconnectDelay: number = 5000;
|
91 |
|
92 | /**
|
93 | * Incoming heartbeat interval in milliseconds. Set to 0 to disable.
|
94 | */
|
95 | public heartbeatIncoming: number = 10000;
|
96 |
|
97 | /**
|
98 | * Outgoing heartbeat interval in milliseconds. Set to 0 to disable.
|
99 | */
|
100 | public heartbeatOutgoing: number = 10000;
|
101 |
|
102 | /**
|
103 | * This switches on a non standard behavior while sending WebSocket packets.
|
104 | * It splits larger (text) packets into chunks of [maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}.
|
105 | * Only Java Spring brokers seems to use this mode.
|
106 | *
|
107 | * WebSockets, by itself, split large (text) packets,
|
108 | * so it is not needed with a truly compliant STOMP/WebSocket broker.
|
109 | * Actually setting it for such broker will cause large messages to fail.
|
110 | *
|
111 | * `false` by default.
|
112 | *
|
113 | * Binary frames are never split.
|
114 | */
|
115 | public splitLargeFrames: boolean = false;
|
116 |
|
117 | /**
|
118 | * See [splitLargeFrames]{@link Client#splitLargeFrames}.
|
119 | * This has no effect if [splitLargeFrames]{@link Client#splitLargeFrames} is `false`.
|
120 | */
|
121 | public maxWebSocketChunkSize: number = 8 * 1024;
|
122 |
|
123 | /**
|
124 | * Usually the
|
125 | * [type of WebSocket frame]{@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send#Parameters}
|
126 | * is automatically decided by type of the payload.
|
127 | * Default is `false`, which should work with all compliant brokers.
|
128 | *
|
129 | * Set this flag to force binary frames.
|
130 | */
|
131 | public forceBinaryWSFrames: boolean = false;
|
132 |
|
133 | /**
|
134 | * A bug in ReactNative chops a string on occurrence of a NULL.
|
135 | * See issue [https://github.com/stomp-js/stompjs/issues/89]{@link https://github.com/stomp-js/stompjs/issues/89}.
|
136 | * This makes incoming WebSocket messages invalid STOMP packets.
|
137 | * Setting this flag attempts to reverse the damage by appending a NULL.
|
138 | * If the broker splits a large message into multiple WebSocket messages,
|
139 | * this flag will cause data loss and abnormal termination of connection.
|
140 | *
|
141 | * This is not an ideal solution, but a stop gap until the underlying issue is fixed at ReactNative library.
|
142 | */
|
143 | public appendMissingNULLonIncoming: boolean = false;
|
144 |
|
145 | /**
|
146 | * Underlying WebSocket instance, READONLY.
|
147 | */
|
148 | get webSocket(): IStompSocket {
|
149 | return this._stompHandler ? this._stompHandler._webSocket : undefined;
|
150 | }
|
151 |
|
152 | /**
|
153 | * Connection headers, important keys - `login`, `passcode`, `host`.
|
154 | * Though STOMP 1.2 standard marks these keys to be present, check your broker documentation for
|
155 | * details specific to your broker.
|
156 | */
|
157 | public connectHeaders: StompHeaders;
|
158 |
|
159 | /**
|
160 | * Disconnection headers.
|
161 | */
|
162 | get disconnectHeaders(): StompHeaders {
|
163 | return this._disconnectHeaders;
|
164 | }
|
165 |
|
166 | set disconnectHeaders(value: StompHeaders) {
|
167 | this._disconnectHeaders = value;
|
168 | if (this._stompHandler) {
|
169 | this._stompHandler.disconnectHeaders = this._disconnectHeaders;
|
170 | }
|
171 | }
|
172 | private _disconnectHeaders: StompHeaders;
|
173 |
|
174 | /**
|
175 | * This function will be called for any unhandled messages.
|
176 | * It is useful for receiving messages sent to RabbitMQ temporary queues.
|
177 | *
|
178 | * It can also get invoked with stray messages while the server is processing
|
179 | * a request to [Client#unsubscribe]{@link Client#unsubscribe}
|
180 | * from an endpoint.
|
181 | *
|
182 | * The actual {@link IMessage} will be passed as parameter to the callback.
|
183 | */
|
184 | public onUnhandledMessage: messageCallbackType;
|
185 |
|
186 | /**
|
187 | * STOMP brokers can be requested to notify when an operation is actually completed.
|
188 | * Prefer using [Client#watchForReceipt]{@link Client#watchForReceipt}. See
|
189 | * [Client#watchForReceipt]{@link Client#watchForReceipt} for examples.
|
190 | *
|
191 | * The actual {@link FrameImpl} will be passed as parameter to the callback.
|
192 | */
|
193 | public onUnhandledReceipt: frameCallbackType;
|
194 |
|
195 | /**
|
196 | * Will be invoked if {@link FrameImpl} of unknown type is received from the STOMP broker.
|
197 | *
|
198 | * The actual {@link IFrame} will be passed as parameter to the callback.
|
199 | */
|
200 | public onUnhandledFrame: frameCallbackType;
|
201 |
|
202 | /**
|
203 | * `true` if there is a active connection with STOMP Broker
|
204 | */
|
205 | get connected(): boolean {
|
206 | return !!this._stompHandler && this._stompHandler.connected;
|
207 | }
|
208 |
|
209 | /**
|
210 | * Callback, invoked on before a connection connection to the STOMP broker.
|
211 | *
|
212 | * You can change options on the client, which will impact the immediate connect.
|
213 | * It is valid to call [Client#decativate]{@link Client#deactivate} in this callback.
|
214 | *
|
215 | * As of version 5.1, this callback can be
|
216 | * [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
|
217 | * (i.e., it can return a
|
218 | * [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)).
|
219 | * In that case connect will be called only after the Promise is resolved.
|
220 | * This can be used to reliably fetch credentials, access token etc. from some other service
|
221 | * in an asynchronous way.
|
222 | */
|
223 | public beforeConnect: () => void | Promise<void>;
|
224 |
|
225 | /**
|
226 | * Callback, invoked on every successful connection to the STOMP broker.
|
227 | *
|
228 | * The actual {@link FrameImpl} will be passed as parameter to the callback.
|
229 | * Sometimes clients will like to use headers from this frame.
|
230 | */
|
231 | public onConnect: frameCallbackType;
|
232 |
|
233 | /**
|
234 | * Callback, invoked on every successful disconnection from the STOMP broker. It will not be invoked if
|
235 | * the STOMP broker disconnected due to an error.
|
236 | *
|
237 | * The actual Receipt {@link FrameImpl} acknowledging the DISCONNECT will be passed as parameter to the callback.
|
238 | *
|
239 | * The way STOMP protocol is designed, the connection may close/terminate without the client
|
240 | * receiving the Receipt {@link FrameImpl} acknowledging the DISCONNECT.
|
241 | * You might find [Client#onWebSocketClose]{@link Client#onWebSocketClose} more appropriate to watch
|
242 | * STOMP broker disconnects.
|
243 | */
|
244 | public onDisconnect: frameCallbackType;
|
245 |
|
246 | /**
|
247 | * Callback, invoked on an ERROR frame received from the STOMP Broker.
|
248 | * A compliant STOMP Broker will close the connection after this type of frame.
|
249 | * Please check broker specific documentation for exact behavior.
|
250 | *
|
251 | * The actual {@link IFrame} will be passed as parameter to the callback.
|
252 | */
|
253 | public onStompError: frameCallbackType;
|
254 |
|
255 | /**
|
256 | * Callback, invoked when underlying WebSocket is closed.
|
257 | *
|
258 | * Actual [CloseEvent]{@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}
|
259 | * is passed as parameter to the callback.
|
260 | */
|
261 | public onWebSocketClose: closeEventCallbackType;
|
262 |
|
263 | /**
|
264 | * Callback, invoked when underlying WebSocket raises an error.
|
265 | *
|
266 | * Actual [Event]{@link https://developer.mozilla.org/en-US/docs/Web/API/Event}
|
267 | * is passed as parameter to the callback.
|
268 | */
|
269 | public onWebSocketError: wsErrorCallbackType;
|
270 |
|
271 | /**
|
272 | * Set it to log the actual raw communication with the broker.
|
273 | * When unset, it logs headers of the parsed frames.
|
274 | *
|
275 | * Change in this effects from next broker reconnect.
|
276 | *
|
277 | * **Caution: this assumes that frames only have valid UTF8 strings.**
|
278 | */
|
279 | public logRawCommunication: boolean;
|
280 |
|
281 | /**
|
282 | * By default, debug messages are discarded. To log to `console` following can be used:
|
283 | *
|
284 | * ```javascript
|
285 | * client.debug = function(str) {
|
286 | * console.log(str);
|
287 | * };
|
288 | * ```
|
289 | *
|
290 | * Currently this method does not support levels of log. Be aware that the output can be quite verbose
|
291 | * and may contain sensitive information (like passwords, tokens etc.).
|
292 | */
|
293 | public debug: debugFnType;
|
294 |
|
295 | /**
|
296 | * Browsers do not immediately close WebSockets when `.close` is issued.
|
297 | * This may cause reconnection to take a longer on certain type of failures.
|
298 | * In case of incoming heartbeat failure, this experimental flag instructs the library
|
299 | * to discard the socket immediately (even before it is actually closed).
|
300 | */
|
301 | public discardWebsocketOnCommFailure: boolean;
|
302 |
|
303 | /**
|
304 | * version of STOMP protocol negotiated with the server, READONLY
|
305 | */
|
306 | get connectedVersion(): string {
|
307 | return this._stompHandler ? this._stompHandler.connectedVersion : undefined;
|
308 | }
|
309 |
|
310 | private _stompHandler: StompHandler;
|
311 |
|
312 | /**
|
313 | * if the client is active (connected or going to reconnect)
|
314 | */
|
315 | get active(): boolean {
|
316 | return this.state === ActivationState.ACTIVE;
|
317 | }
|
318 |
|
319 | /**
|
320 | * It will be called on state change.
|
321 | *
|
322 | * When deactivating it may go from ACTIVE to INACTIVE without entering DEACTIVATING.
|
323 | */
|
324 | public onChangeState: (state: ActivationState) => void;
|
325 |
|
326 | private _changeState(state: ActivationState) {
|
327 | this.state = state;
|
328 | this.onChangeState(state);
|
329 | }
|
330 |
|
331 | // This will mark deactivate to complete, to be called after Websocket is closed
|
332 | private _resolveSocketClose: (value?: PromiseLike<void> | void) => void;
|
333 |
|
334 | /**
|
335 | * Activation state.
|
336 | *
|
337 | * It will usually be ACTIVE or INACTIVE.
|
338 | * When deactivating it may go from ACTIVE to INACTIVE without entering DEACTIVATING.
|
339 | */
|
340 | public state: ActivationState = ActivationState.INACTIVE;
|
341 |
|
342 | private _reconnector: any;
|
343 |
|
344 | /**
|
345 | * Create an instance.
|
346 | */
|
347 | constructor(conf: StompConfig = {}) {
|
348 | // Dummy callbacks
|
349 | const noOp = () => {};
|
350 | this.debug = noOp;
|
351 | this.beforeConnect = noOp;
|
352 | this.onConnect = noOp;
|
353 | this.onDisconnect = noOp;
|
354 | this.onUnhandledMessage = noOp;
|
355 | this.onUnhandledReceipt = noOp;
|
356 | this.onUnhandledFrame = noOp;
|
357 | this.onStompError = noOp;
|
358 | this.onWebSocketClose = noOp;
|
359 | this.onWebSocketError = noOp;
|
360 | this.logRawCommunication = false;
|
361 | this.onChangeState = noOp;
|
362 |
|
363 | // These parameters would typically get proper values before connect is called
|
364 | this.connectHeaders = {};
|
365 | this._disconnectHeaders = {};
|
366 |
|
367 | // Apply configuration
|
368 | this.configure(conf);
|
369 | }
|
370 |
|
371 | /**
|
372 | * Update configuration.
|
373 | */
|
374 | public configure(conf: StompConfig): void {
|
375 | // bulk assign all properties to this
|
376 | (Object as any).assign(this, conf);
|
377 | }
|
378 |
|
379 | /**
|
380 | * Initiate the connection with the broker.
|
381 | * If the connection breaks, as per [Client#reconnectDelay]{@link Client#reconnectDelay},
|
382 | * it will keep trying to reconnect.
|
383 | *
|
384 | * Call [Client#deactivate]{@link Client#deactivate} to disconnect and stop reconnection attempts.
|
385 | */
|
386 | public activate(): void {
|
387 | if (this.state === ActivationState.DEACTIVATING) {
|
388 | this.debug(
|
389 | 'Still DEACTIVATING, please await call to deactivate before trying to re-activate'
|
390 | );
|
391 | throw new Error('Still DEACTIVATING, can not activate now');
|
392 | }
|
393 |
|
394 | if (this.active) {
|
395 | this.debug('Already ACTIVE, ignoring request to activate');
|
396 | return;
|
397 | }
|
398 |
|
399 | this._changeState(ActivationState.ACTIVE);
|
400 |
|
401 | this._connect();
|
402 | }
|
403 |
|
404 | private async _connect(): Promise<void> {
|
405 | if (this.connected) {
|
406 | this.debug('STOMP: already connected, nothing to do');
|
407 | return;
|
408 | }
|
409 |
|
410 | await this.beforeConnect();
|
411 |
|
412 | if (!this.active) {
|
413 | this.debug(
|
414 | 'Client has been marked inactive, will not attempt to connect'
|
415 | );
|
416 | return;
|
417 | }
|
418 |
|
419 | // setup connection watcher
|
420 | if (this.connectionTimeout > 0) {
|
421 | // clear first
|
422 | if (this._connectionWatcher) {
|
423 | clearTimeout(this._connectionWatcher);
|
424 | }
|
425 | this._connectionWatcher = setTimeout(() => {
|
426 | if (this.connected) {
|
427 | return;
|
428 | }
|
429 | // Connection not established, close the underlying socket
|
430 | // a reconnection will be attempted
|
431 | this.debug(
|
432 | `Connection not established in ${this.connectionTimeout}ms, closing socket`
|
433 | );
|
434 | this.forceDisconnect();
|
435 | }, this.connectionTimeout);
|
436 | }
|
437 |
|
438 | this.debug('Opening Web Socket...');
|
439 |
|
440 | // Get the actual WebSocket (or a similar object)
|
441 | const webSocket = this._createWebSocket();
|
442 |
|
443 | this._stompHandler = new StompHandler(this, webSocket, {
|
444 | debug: this.debug,
|
445 | stompVersions: this.stompVersions,
|
446 | connectHeaders: this.connectHeaders,
|
447 | disconnectHeaders: this._disconnectHeaders,
|
448 | heartbeatIncoming: this.heartbeatIncoming,
|
449 | heartbeatOutgoing: this.heartbeatOutgoing,
|
450 | splitLargeFrames: this.splitLargeFrames,
|
451 | maxWebSocketChunkSize: this.maxWebSocketChunkSize,
|
452 | forceBinaryWSFrames: this.forceBinaryWSFrames,
|
453 | logRawCommunication: this.logRawCommunication,
|
454 | appendMissingNULLonIncoming: this.appendMissingNULLonIncoming,
|
455 | discardWebsocketOnCommFailure: this.discardWebsocketOnCommFailure,
|
456 |
|
457 | onConnect: frame => {
|
458 | // Successfully connected, stop the connection watcher
|
459 | if (this._connectionWatcher) {
|
460 | clearTimeout(this._connectionWatcher);
|
461 | this._connectionWatcher = undefined;
|
462 | }
|
463 |
|
464 | if (!this.active) {
|
465 | this.debug(
|
466 | 'STOMP got connected while deactivate was issued, will disconnect now'
|
467 | );
|
468 | this._disposeStompHandler();
|
469 | return;
|
470 | }
|
471 | this.onConnect(frame);
|
472 | },
|
473 | onDisconnect: frame => {
|
474 | this.onDisconnect(frame);
|
475 | },
|
476 | onStompError: frame => {
|
477 | this.onStompError(frame);
|
478 | },
|
479 | onWebSocketClose: evt => {
|
480 | this._stompHandler = undefined; // a new one will be created in case of a reconnect
|
481 |
|
482 | if (this.state === ActivationState.DEACTIVATING) {
|
483 | // Mark deactivation complete
|
484 | this._resolveSocketClose();
|
485 | this._resolveSocketClose = undefined;
|
486 | this._changeState(ActivationState.INACTIVE);
|
487 | }
|
488 |
|
489 | this.onWebSocketClose(evt);
|
490 | // The callback is called before attempting to reconnect, this would allow the client
|
491 | // to be `deactivated` in the callback.
|
492 | if (this.active) {
|
493 | this._schedule_reconnect();
|
494 | }
|
495 | },
|
496 | onWebSocketError: evt => {
|
497 | this.onWebSocketError(evt);
|
498 | },
|
499 | onUnhandledMessage: message => {
|
500 | this.onUnhandledMessage(message);
|
501 | },
|
502 | onUnhandledReceipt: frame => {
|
503 | this.onUnhandledReceipt(frame);
|
504 | },
|
505 | onUnhandledFrame: frame => {
|
506 | this.onUnhandledFrame(frame);
|
507 | },
|
508 | });
|
509 |
|
510 | this._stompHandler.start();
|
511 | }
|
512 |
|
513 | private _createWebSocket(): IStompSocket {
|
514 | let webSocket: IStompSocket;
|
515 |
|
516 | if (this.webSocketFactory) {
|
517 | webSocket = this.webSocketFactory();
|
518 | } else {
|
519 | webSocket = new WebSocket(
|
520 | this.brokerURL,
|
521 | this.stompVersions.protocolVersions()
|
522 | );
|
523 | }
|
524 | webSocket.binaryType = 'arraybuffer';
|
525 | return webSocket;
|
526 | }
|
527 |
|
528 | private _schedule_reconnect(): void {
|
529 | if (this.reconnectDelay > 0) {
|
530 | this.debug(`STOMP: scheduling reconnection in ${this.reconnectDelay}ms`);
|
531 |
|
532 | this._reconnector = setTimeout(() => {
|
533 | this._connect();
|
534 | }, this.reconnectDelay);
|
535 | }
|
536 | }
|
537 |
|
538 | /**
|
539 | * Disconnect if connected and stop auto reconnect loop.
|
540 | * Appropriate callbacks will be invoked if underlying STOMP connection was connected.
|
541 | *
|
542 | * This call is async, it will resolve immediately if there is no underlying active websocket,
|
543 | * otherwise, it will resolve after underlying websocket is properly disposed.
|
544 | *
|
545 | * To reactivate you can call [Client#activate]{@link Client#activate}.
|
546 | */
|
547 | public async deactivate(): Promise<void> {
|
548 | let retPromise: Promise<void>;
|
549 |
|
550 | if (this.state !== ActivationState.ACTIVE) {
|
551 | this.debug(
|
552 | `Already ${ActivationState[this.state]}, ignoring call to deactivate`
|
553 | );
|
554 | return Promise.resolve();
|
555 | }
|
556 |
|
557 | this._changeState(ActivationState.DEACTIVATING);
|
558 |
|
559 | // Clear if a reconnection was scheduled
|
560 | if (this._reconnector) {
|
561 | clearTimeout(this._reconnector);
|
562 | }
|
563 |
|
564 | if (
|
565 | this._stompHandler &&
|
566 | this.webSocket.readyState !== StompSocketState.CLOSED
|
567 | ) {
|
568 | // we need to wait for underlying websocket to close
|
569 | retPromise = new Promise<void>((resolve, reject) => {
|
570 | this._resolveSocketClose = resolve;
|
571 | });
|
572 | } else {
|
573 | // indicate that auto reconnect loop should terminate
|
574 | this._changeState(ActivationState.INACTIVE);
|
575 | return Promise.resolve();
|
576 | }
|
577 |
|
578 | this._disposeStompHandler();
|
579 |
|
580 | return retPromise;
|
581 | }
|
582 |
|
583 | /**
|
584 | * Force disconnect if there is an active connection by directly closing the underlying WebSocket.
|
585 | * This is different than a normal disconnect where a DISCONNECT sequence is carried out with the broker.
|
586 | * After forcing disconnect, automatic reconnect will be attempted.
|
587 | * To stop further reconnects call [Client#deactivate]{@link Client#deactivate} as well.
|
588 | */
|
589 | public forceDisconnect() {
|
590 | if (this._stompHandler) {
|
591 | this._stompHandler.forceDisconnect();
|
592 | }
|
593 | }
|
594 |
|
595 | private _disposeStompHandler() {
|
596 | // Dispose STOMP Handler
|
597 | if (this._stompHandler) {
|
598 | this._stompHandler.dispose();
|
599 | this._stompHandler = null;
|
600 | }
|
601 | }
|
602 |
|
603 | /**
|
604 | * Send a message to a named destination. Refer to your STOMP broker documentation for types
|
605 | * and naming of destinations.
|
606 | *
|
607 | * STOMP protocol specifies and suggests some headers and also allows broker specific headers.
|
608 | *
|
609 | * `body` must be String.
|
610 | * You will need to covert the payload to string in case it is not string (e.g. JSON).
|
611 | *
|
612 | * To send a binary message body use binaryBody parameter. It should be a
|
613 | * [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).
|
614 | * Sometimes brokers may not support binary frames out of the box.
|
615 | * Please check your broker documentation.
|
616 | *
|
617 | * `content-length` header is automatically added to the STOMP Frame sent to the broker.
|
618 | * Set `skipContentLengthHeader` to indicate that `content-length` header should not be added.
|
619 | * For binary messages `content-length` header is always added.
|
620 | *
|
621 | * Caution: The broker will, most likely, report an error and disconnect if message body has NULL octet(s)
|
622 | * and `content-length` header is missing.
|
623 | *
|
624 | * ```javascript
|
625 | * client.publish({destination: "/queue/test", headers: {priority: 9}, body: "Hello, STOMP"});
|
626 | *
|
627 | * // Only destination is mandatory parameter
|
628 | * client.publish({destination: "/queue/test", body: "Hello, STOMP"});
|
629 | *
|
630 | * // Skip content-length header in the frame to the broker
|
631 | * client.publish({"/queue/test", body: "Hello, STOMP", skipContentLengthHeader: true});
|
632 | *
|
633 | * var binaryData = generateBinaryData(); // This need to be of type Uint8Array
|
634 | * // setting content-type header is not mandatory, however a good practice
|
635 | * client.publish({destination: '/topic/special', binaryBody: binaryData,
|
636 | * headers: {'content-type': 'application/octet-stream'}});
|
637 | * ```
|
638 | */
|
639 | public publish(params: IPublishParams) {
|
640 | this._stompHandler.publish(params);
|
641 | }
|
642 |
|
643 | /**
|
644 | * STOMP brokers may carry out operation asynchronously and allow requesting for acknowledgement.
|
645 | * To request an acknowledgement, a `receipt` header needs to be sent with the actual request.
|
646 | * The value (say receipt-id) for this header needs to be unique for each use. Typically a sequence, a UUID, a
|
647 | * random number or a combination may be used.
|
648 | *
|
649 | * A complaint broker will send a RECEIPT frame when an operation has actually been completed.
|
650 | * The operation needs to be matched based in the value of the receipt-id.
|
651 | *
|
652 | * This method allow watching for a receipt and invoke the callback
|
653 | * when corresponding receipt has been received.
|
654 | *
|
655 | * The actual {@link FrameImpl} will be passed as parameter to the callback.
|
656 | *
|
657 | * Example:
|
658 | * ```javascript
|
659 | * // Subscribing with acknowledgement
|
660 | * let receiptId = randomText();
|
661 | *
|
662 | * client.watchForReceipt(receiptId, function() {
|
663 | * // Will be called after server acknowledges
|
664 | * });
|
665 | *
|
666 | * client.subscribe(TEST.destination, onMessage, {receipt: receiptId});
|
667 | *
|
668 | *
|
669 | * // Publishing with acknowledgement
|
670 | * receiptId = randomText();
|
671 | *
|
672 | * client.watchForReceipt(receiptId, function() {
|
673 | * // Will be called after server acknowledges
|
674 | * });
|
675 | * client.publish({destination: TEST.destination, headers: {receipt: receiptId}, body: msg});
|
676 | * ```
|
677 | */
|
678 | public watchForReceipt(receiptId: string, callback: frameCallbackType): void {
|
679 | this._stompHandler.watchForReceipt(receiptId, callback);
|
680 | }
|
681 |
|
682 | /**
|
683 | * Subscribe to a STOMP Broker location. The callback will be invoked for each received message with
|
684 | * the {@link IMessage} as argument.
|
685 | *
|
686 | * Note: The library will generate an unique ID if there is none provided in the headers.
|
687 | * To use your own ID, pass it using the headers argument.
|
688 | *
|
689 | * ```javascript
|
690 | * callback = function(message) {
|
691 | * // called when the client receives a STOMP message from the server
|
692 | * if (message.body) {
|
693 | * alert("got message with body " + message.body)
|
694 | * } else {
|
695 | * alert("got empty message");
|
696 | * }
|
697 | * });
|
698 | *
|
699 | * var subscription = client.subscribe("/queue/test", callback);
|
700 | *
|
701 | * // Explicit subscription id
|
702 | * var mySubId = 'my-subscription-id-001';
|
703 | * var subscription = client.subscribe(destination, callback, { id: mySubId });
|
704 | * ```
|
705 | */
|
706 | public subscribe(
|
707 | destination: string,
|
708 | callback: messageCallbackType,
|
709 | headers: StompHeaders = {}
|
710 | ): StompSubscription {
|
711 | return this._stompHandler.subscribe(destination, callback, headers);
|
712 | }
|
713 |
|
714 | /**
|
715 | * It is preferable to unsubscribe from a subscription by calling
|
716 | * `unsubscribe()` directly on {@link StompSubscription} returned by `client.subscribe()`:
|
717 | *
|
718 | * ```javascript
|
719 | * var subscription = client.subscribe(destination, onmessage);
|
720 | * // ...
|
721 | * subscription.unsubscribe();
|
722 | * ```
|
723 | *
|
724 | * See: http://stomp.github.com/stomp-specification-1.2.html#UNSUBSCRIBE UNSUBSCRIBE Frame
|
725 | */
|
726 | public unsubscribe(id: string, headers: StompHeaders = {}): void {
|
727 | this._stompHandler.unsubscribe(id, headers);
|
728 | }
|
729 |
|
730 | /**
|
731 | * Start a transaction, the returned {@link ITransaction} has methods - [commit]{@link ITransaction#commit}
|
732 | * and [abort]{@link ITransaction#abort}.
|
733 | *
|
734 | * `transactionId` is optional, if not passed the library will generate it internally.
|
735 | */
|
736 | public begin(transactionId?: string): ITransaction {
|
737 | return this._stompHandler.begin(transactionId);
|
738 | }
|
739 |
|
740 | /**
|
741 | * Commit a transaction.
|
742 | *
|
743 | * It is preferable to commit a transaction by calling [commit]{@link ITransaction#commit} directly on
|
744 | * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
|
745 | *
|
746 | * ```javascript
|
747 | * var tx = client.begin(txId);
|
748 | * //...
|
749 | * tx.commit();
|
750 | * ```
|
751 | */
|
752 | public commit(transactionId: string): void {
|
753 | this._stompHandler.commit(transactionId);
|
754 | }
|
755 |
|
756 | /**
|
757 | * Abort a transaction.
|
758 | * It is preferable to abort a transaction by calling [abort]{@link ITransaction#abort} directly on
|
759 | * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
|
760 | *
|
761 | * ```javascript
|
762 | * var tx = client.begin(txId);
|
763 | * //...
|
764 | * tx.abort();
|
765 | * ```
|
766 | */
|
767 | public abort(transactionId: string): void {
|
768 | this._stompHandler.abort(transactionId);
|
769 | }
|
770 |
|
771 | /**
|
772 | * ACK a message. It is preferable to acknowledge a message by calling [ack]{@link IMessage#ack} directly
|
773 | * on the {@link IMessage} handled by a subscription callback:
|
774 | *
|
775 | * ```javascript
|
776 | * var callback = function (message) {
|
777 | * // process the message
|
778 | * // acknowledge it
|
779 | * message.ack();
|
780 | * };
|
781 | * client.subscribe(destination, callback, {'ack': 'client'});
|
782 | * ```
|
783 | */
|
784 | public ack(
|
785 | messageId: string,
|
786 | subscriptionId: string,
|
787 | headers: StompHeaders = {}
|
788 | ): void {
|
789 | this._stompHandler.ack(messageId, subscriptionId, headers);
|
790 | }
|
791 |
|
792 | /**
|
793 | * NACK a message. It is preferable to acknowledge a message by calling [nack]{@link IMessage#nack} directly
|
794 | * on the {@link IMessage} handled by a subscription callback:
|
795 | *
|
796 | * ```javascript
|
797 | * var callback = function (message) {
|
798 | * // process the message
|
799 | * // an error occurs, nack it
|
800 | * message.nack();
|
801 | * };
|
802 | * client.subscribe(destination, callback, {'ack': 'client'});
|
803 | * ```
|
804 | */
|
805 | public nack(
|
806 | messageId: string,
|
807 | subscriptionId: string,
|
808 | headers: StompHeaders = {}
|
809 | ): void {
|
810 | this._stompHandler.nack(messageId, subscriptionId, headers);
|
811 | }
|
812 | }
|
813 |
|
\ | No newline at end of file |