UNPKG

webstomp-obs

Version:

Stomp over websocket using observable

129 lines (102 loc) 4.41 kB
import { Observable } from 'rxjs/Observable'; import { Observer } from 'rxjs/Observer'; import { Subject } from 'rxjs/Subject'; import { AnonymousSubscription } from 'rxjs/Subscription' import { IEvent, IWebSocketObservable, WsOptions, IWebSocket } from './types'; import Frame from './frame'; import { ConnectionHeaders, DisconnectHeaders } from './headers'; import { unicodeStringToTypedArray, logger } from './utils'; export interface Subscription { id: string; unsubscribe: () => void; } // STOMP Client Class // // All STOMP protocol is exposed as methods of this class ('connect()', // 'send()', etc.) class WebSocketHandler { private ws: IWebSocket private isBinary: boolean private connected: boolean private maxWebSocketFrameSize: number private createWS: Function constructor(createWsConnection: () => IWebSocket, options: WsOptions) { // cannot have default options object + destructuring in the same time in method signature let {binary = false} = options; this.createWS = createWsConnection; this.isBinary = !!binary; // maximum *IWebSocket* frame size sent by the client. If the STOMP frame // is bigger than this value, the STOMP frame will be sent using multiple // IWebSocket frames (default is 16KiB) this.maxWebSocketFrameSize = 16 * 1024; } public initConnection = (headers: ConnectionHeaders): Observable<IWebSocketObservable> => { return Observable.create((webSocketObserver: Observer<IWebSocketObservable>) => { if (this.ws) { throw 'Error, the connection has already been created !' } this.ws = this.createWS(); if (!this.ws) { throw 'Error, createWsConnection function returned null !' } this.ws.binaryType = 'arraybuffer'; // Creating the Observables const messageReceived = new Subject() const messageReceipted = new Subject() const messageSender = new Subject() let inputSubscription: AnonymousSubscription logger.debug('Opening Web Socket...'); this.ws.onopen = () => { logger.debug('Web Socket Opened...'); inputSubscription = messageSender.subscribe(this._send) webSocketObserver.next({messageReceived: <Observable<IEvent>>messageReceived.asObservable(), messageSender}); }; this.ws.onmessage = (evt: IEvent) => { messageReceived.next(evt); }; this.ws.onclose = (ev: CloseEvent) => { logger.debug(`Whoops! Lost connection to ${this.ws.url}:`, ev); this.ws = null; webSocketObserver.error(ev); }; return () => { messageReceived.complete() messageReceipted.complete() inputSubscription && inputSubscription.unsubscribe() this.disconnect() } }) } public disconnect = (headers: DisconnectHeaders = {}) => { if (this.ws) { this.ws.onclose = null; this.ws.close(); this.ws = null; } } // Base method to transmit any stomp frame private _send = (data: any) => { if (!this.ws) { logger.debug('Error, this.ws is null ! Possibly initConnection has not been called or not subscribed !'); return; } logger.debug(`>>> ${data}`); this._wsSend(this.ws, data); } private _wsSend = (ws: IWebSocket, data: any) => { if (this.isBinary) data = unicodeStringToTypedArray(data); logger.debug(`>>> length ${data.length}`); // if necessary, split the *STOMP* frame to send it on many smaller // *IWebSocket* frames while (true) { if (data.length > this.maxWebSocketFrameSize) { ws.send(data.slice(0, this.maxWebSocketFrameSize)); data = data.slice(this.maxWebSocketFrameSize); logger.debug(`remaining = ${data.length}`); } else { return ws.send(data); } } } } export default WebSocketHandler;