diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e30c1138b..0d3268f784 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- `MessagingSession` reconnects with refreshed endpoint and credentials if needed. EndpointUrl on `MessagingSessionConfiguration` is deprecated as it is resolved by calling `getMessagingSessionEndpoint` internally. - ### Removed ### Changed ### Fixed + +- `MessagingSession` reconnects with refreshed endpoint and credentials if needed. EndpointUrl on `MessagingSessionConfiguration` is deprecated as it is resolved by calling `getMessagingSessionEndpoint` internally. ## [2.30.0] - 2022-03-08 diff --git a/src/messagingsession/DefaultMessagingSession.ts b/src/messagingsession/DefaultMessagingSession.ts index 2d556a1d21..586392816f 100644 --- a/src/messagingsession/DefaultMessagingSession.ts +++ b/src/messagingsession/DefaultMessagingSession.ts @@ -107,17 +107,6 @@ export default class DefaultMessagingSession implements MessagingSession { private async startConnecting(reconnecting: boolean): Promise { this.isConnecting = true; try { - // reconnect needs to re-resolve endpoint url, which will also refresh credentials on client if they are expired. - let endpointUrl = !reconnecting ? this.configuration.endpointUrl : undefined; - if (endpointUrl === undefined) { - const endpoint = await this.configuration.chimeClient - .getMessagingSessionEndpoint() - .promise(); - endpointUrl = endpoint.Endpoint.Url; - } - - const signedUrl = this.prepareWebSocketUrl(endpointUrl); - this.logger.info(`opening connection to ${signedUrl}`); if (!reconnecting) { this.reconnectController.reset(); } @@ -126,6 +115,30 @@ export default class DefaultMessagingSession implements MessagingSession { } else { this.reconnectController.startedConnectionAttempt(true); } + + // reconnect needs to re-resolve endpoint url, which will also refresh credentials on client if they are expired. + let endpointUrl = !reconnecting ? this.configuration.endpointUrl : undefined; + if (endpointUrl === undefined) { + try { + const endpoint = await this.configuration.chimeClient + .getMessagingSessionEndpoint() + .promise(); + endpointUrl = endpoint.Endpoint.Url; + } catch (e) { + const closeEvent = new CloseEvent('close', { + wasClean: false, + code: 4999, + reason: 'Failed to getMessagingSessionEndpoint', + bubbles: false, + }); + this.closeEventHandler(closeEvent); + return; + } + } + + const signedUrl = this.prepareWebSocketUrl(endpointUrl); + this.logger.info(`opening connection to ${signedUrl}`); + this.webSocket.create(signedUrl, [], true); this.forEachObserver(observer => { if (observer.messagingSessionDidStartConnecting) { @@ -191,16 +204,18 @@ export default class DefaultMessagingSession implements MessagingSession { } } + private retryConnection(): boolean { + return this.reconnectController.retryWithBackoff(async () => { + await this.startConnecting(true); + }, null); + } + private closeEventHandler(event: CloseEvent): void { this.logger.info(`WebSocket close: ${event.code} ${event.reason}`); - this.webSocket.destroy(); - if ( - !this.isClosing && - this.canReconnect(event.code) && - this.reconnectController.retryWithBackoff(async () => { - await this.startConnecting(true); - }, null) - ) { + if (event.code !== 4999) { + this.webSocket.destroy(); + } + if (!this.isClosing && this.canReconnect(event.code) && this.retryConnection()) { return; } this.isClosing = false; diff --git a/test/messagingsession/DefaultMessagingSession.test.ts b/test/messagingsession/DefaultMessagingSession.test.ts index 758a2b45da..459eee8e58 100644 --- a/test/messagingsession/DefaultMessagingSession.test.ts +++ b/test/messagingsession/DefaultMessagingSession.test.ts @@ -300,6 +300,47 @@ describe('DefaultMessagingSession', () => { messagingSession.start(); }); + it('can reconnect with failures on getMessagingSession', done => { + let didStartCount = 0; + let didStartConnecting = 0; + const savedClientBehavior = chimeClient.getMessagingSessionEndpoint; + messagingSession.addObserver({ + messagingSessionDidStartConnecting(reconnecting: boolean): void { + didStartConnecting++; + if (!reconnecting) { + webSocket.addEventListener('open', () => { + webSocket.close(1006); + chimeClient.getMessagingSessionEndpoint = function () { + throw 'some error'; + }; + setTimeout(function () { + chimeClient.getMessagingSessionEndpoint = savedClientBehavior; + }, 100); + }); + } else { + webSocket.addEventListener('open', () => { + webSocket.send(SESSION_SUBSCRIBED_MSG); + }); + webSocket.addEventListener('message', (_event: MessageEvent) => { + new TimeoutScheduler(10).start(() => { + messagingSession.stop(); + }); + }); + } + }, + messagingSessionDidStart(): void { + didStartCount++; + }, + messagingSessionDidStop(_event: CloseEvent): void { + expect(didStartConnecting).to.be.eq(2); + expect(didStartCount).to.be.eq(1); + expect(getMessSessionCnt).to.be.eq(2); + done(); + }, + }); + messagingSession.start(); + }); + it('will not reconnect', done => { let didStartConnecting = 0; messagingSession.addObserver({