UNPKG

rsocket-rxjs

Version:
127 lines (126 loc) 4.92 kB
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://...'"); } } }