rsocket-rxjs
Version:
RSocket Protocol Client Implementation
127 lines (126 loc) • 4.92 kB
JavaScript
import { Observable } from 'rxjs';
import { delay, filter, retryWhen } from 'rxjs/operators';
import { Payload } from '../core/protocol/payload';
import { RSocketClient } from '../core/rsocket-client.impl';
import { WebsocketTransport } from '../core/transport/websocket-transport.impl';
import { CompositeMetadata } from '../extensions/composite-metadata';
import { EncodingRSocket } from '../extensions/encoding-rsocket-client';
import { MessageRoutingRSocket } from '../extensions/messages/message-routing-rsocket';
import { WellKnownMimeTypes } from '../extensions/well-known-mime-types';
import { RSocketState } from './rsocket.api';
/**
* A builder that should work with Spring Messaging for RSocket
*/
export class SpringRSocketMessagingBuilder {
constructor() {
this._config = {
majorVersion: 1,
minorVersion: 0,
honorsLease: false,
keepaliveTime: 30000,
maxLifetime: 100000,
dataMimeType: WellKnownMimeTypes.APPLICATION_JSON.name,
metadataMimeType: WellKnownMimeTypes.MESSAGE_X_RSOCKET_COMPOSITE_METADATA_V0.name,
setupPayload: undefined,
fragmentSize: 16777215 // 16mb
};
this._setupMetdata = new CompositeMetadata();
this._encodingCustomizer = s => { };
this._preConnectionRoutesCustomizer = s => { };
this._automaticReconnect = false;
this._reconnectWaitTime = 5000;
}
keepaliveTime(time) {
this._config.keepaliveTime = time;
return this;
}
maxLifetime(time) {
this._config.maxLifetime = time;
return this;
}
dataMimeType(type) {
this._config.dataMimeType = type;
return this;
}
connectMappingRoute(route) {
this._setupMetdata.route = route;
return this;
}
connectMappingData(data) {
this._setupData = data;
return this;
}
connectAuthentication(authentication) {
this._setupMetdata.authentication = authentication;
return this;
}
connectionString(str) {
this._connectionString = str;
return this;
}
fragmentMaxSize(sizeInBytes) {
this._config.fragmentSize = sizeInBytes;
return this;
}
getSetupConfig(socket) {
return Object.assign(Object.assign({}, this._config), { setupPayload: new Payload(socket.tryEncodeObject(this._setupData, this._config.dataMimeType), socket.tryEncodeObject(this._setupMetdata, WellKnownMimeTypes.MESSAGE_X_RSOCKET_COMPOSITE_METADATA_V0.name)) });
}
customizeEncoding(encodingSocketConsumer) {
this._encodingCustomizer = encodingSocketConsumer;
return this;
}
customizeMessageRoutingRSocket(socketConsumer) {
this._preConnectionRoutesCustomizer = socketConsumer;
return this;
}
automaticReconnect(waitTime = 5000) {
this._automaticReconnect = true;
this._reconnectWaitTime = waitTime;
return this;
}
/**
*
* This establishes the connection on subscribe.
* Note that this observable will never complete.
* Unsubscribing will close the connection.
*
* To add route mappings before the connection is established use 'customizeMessageRoutingRSocket'.
*
* To customize encoders use 'customizeEncoding'
*
* @returns
*/
build() {
const obs = new Observable(observer => {
const transport = this.buildTransport();
const rsocket = new RSocketClient(transport, undefined, undefined);
const encodingSocket = new EncodingRSocket(rsocket);
this._encodingCustomizer(encodingSocket);
rsocket.setSetupConfig(this.getSetupConfig(encodingSocket));
const messageSocket = new MessageRoutingRSocket(encodingSocket);
this._preConnectionRoutesCustomizer(messageSocket);
observer.next(messageSocket);
rsocket.establish();
const stateSub = rsocket.state().pipe(filter(s => s == RSocketState.Error)).subscribe(s => observer.error(new Error("RSocket failed")));
return () => {
messageSocket.close();
stateSub.unsubscribe();
};
});
if (this._automaticReconnect) {
return obs.pipe(retryWhen(delay(this._reconnectWaitTime)));
}
else {
return obs;
}
}
buildTransport() {
if (this._connectionString.match("^(ws:)|(wss:)\/\/.*$") != null) {
const transport = new WebsocketTransport(this._connectionString);
return transport;
}
else {
throw new Error("Currently only supports websocket. Connection string must be 'ws://...'");
}
}
}