webstomp-obs
Version:
Stomp over websocket using observable
97 lines (80 loc) • 4.01 kB
text/typescript
import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
import { ConnectableObservable } from 'rxjs/Observable/ConnectableObservable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import 'rxjs/add/observable/timer';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/multicast';
import 'rxjs/add/operator/retryWhen';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import { ConnectedClient } from './connectedClient';
import { ConnectionHeaders } from './headers';
import WebSocketHandler from './webSocketHandler';
import stompWebSocketHandler from './protocol/stomp/stompWebSocketHandler';
import { IWebSocketHandler, IConnectedObservable, WsOptions, IWebSocket } from './types';
export interface ClientOptions extends WsOptions {
maxConnectAttempt: number,
ttlConnectAttempt: number
}
const DEFAULT_MAX_CONNECT_ATTEMPT: number = 10
const DEFAULT_TTL_CONNECT_ATTEMPT: number = 1000
// STOMP Client Class
//
// All STOMP protocol is exposed as methods of this class ('connect()',
// 'send()', etc.)
class Client {
private stompWebSockerHandler: IWebSocketHandler<IConnectedObservable>
private observableConnection: Observable<ConnectedClient>
private maxConnectAttempt: number
private ttlConnectAttempt: number
private isConnected: boolean
constructor (createWsConnection: () => IWebSocket, options: ClientOptions) {
this.stompWebSockerHandler = stompWebSocketHandler(createWsConnection, options);
this.maxConnectAttempt = options.maxConnectAttempt || DEFAULT_MAX_CONNECT_ATTEMPT;
this.ttlConnectAttempt = options.ttlConnectAttempt || DEFAULT_TTL_CONNECT_ATTEMPT;
this.isConnected = false;
}
// [CONNECT Frame](http://stomp.github.com/stomp-specification-1.1.html#CONNECT_or_STOMP_Frame)
//
// Return an Observable containing the connectedClient
public connect = (headers: ConnectionHeaders): Observable<ConnectedClient> => {
// create one and only one dedicated observable per destination
if (!this.observableConnection) {
const multicastConnection: ConnectableObservable<ConnectedClient> = this.__connect(headers)
.multicast(() => new ReplaySubject(1));
multicastConnection.connect();
this.observableConnection = multicastConnection.refCount();
}
return this.observableConnection;
}
private __connect = (headers: ConnectionHeaders): Observable<ConnectedClient> => {
return this.__initConnectedClient(headers).switchMap((connection: IConnectedObservable) => {
this.isConnected = true;
return Observable.of(new ConnectedClient(connection));
})
.catch(error => {
return Observable.throw(error);
});
}
private __initConnectedClient = (headers: ConnectionHeaders) => {
// we initialize the connection
return this.stompWebSockerHandler.initConnection(headers)
.retryWhen(attemps => attemps.scan( (errorCount, err) => {
// we reinitialize the error count if we were previously connected
if (this.isConnected) {
this.isConnected = false;
errorCount = 0;
}
if (this.maxConnectAttempt !== -1 && errorCount >= this.maxConnectAttempt) {
throw 'Attempted to connect ' + errorCount + ' failed.';
}
return errorCount + 1;
}, 1)
.do( errorCount => Observable.timer(errorCount * this.ttlConnectAttempt))
);
}
}
export default Client;