diff --git a/test/test.reconnect.ts b/test/test.reconnect.ts index 671809f..3eba3c7 100644 --- a/test/test.reconnect.ts +++ b/test/test.reconnect.ts @@ -117,15 +117,16 @@ tap.test('should be able to make a functionCall from server to client', async () expect(response.hi).to.equal('hi there from server'); }); -tap.test('client should disconnect and reconnect', async (tools) => { +tap.test('client should disconnect and reconnect', async (toolsArg) => { await testSmartsocketClient.disconnect(); await testSmartsocketClient.connect(); + await toolsArg.delayFor(2000); + expect(testSmartsocket.socketConnections.getArray().length).to.equal(1); }); // class smartsocket tap.test('should be able to switch to a new server', async (toolsArg) => { await testSmartsocket.stop(); - await toolsArg.delayFor(5000); testSmartsocket = new smartsocket.Smartsocket({ alias: 'testserver2', port: testConfig.port }); await testSmartsocket.start(); await toolsArg.delayFor(30000); diff --git a/ts/smartsocket.classes.smartsocket.ts b/ts/smartsocket.classes.smartsocket.ts index 00dce88..d0a5649 100644 --- a/ts/smartsocket.classes.smartsocket.ts +++ b/ts/smartsocket.classes.smartsocket.ts @@ -47,7 +47,7 @@ export class Smartsocket { */ public async start() { const socketIoModule = this.smartenv.getSafeNodeModule('socket.io'); - this.io = socketIoModule(this.socketServer.getServerForSocketIo()); + this.io = socketIoModule(await this.socketServer.getServerForSocketIo()); await this.socketServer.start(); this.io.on('connection', (socketArg) => { this._handleSocketConnection(socketArg); diff --git a/ts/smartsocket.classes.smartsocketclient.ts b/ts/smartsocket.classes.smartsocketclient.ts index b695483..d698be2 100644 --- a/ts/smartsocket.classes.smartsocketclient.ts +++ b/ts/smartsocket.classes.smartsocketclient.ts @@ -105,82 +105,81 @@ export class SmartsocketClient { authenticated: false, side: 'client', smartsocketHost: this, - socket: await socketIoClient.connect(socketUrl, { - multiplex: false, - reconnectionAttempts: 5, - rejectUnauthorized: socketUrl.startsWith('https://localhost') ? false : true, - }), + socket: await socketIoClient + .connect(socketUrl, { + multiplex: false, + autoConnect: false, + reconnectionAttempts: 0, + rejectUnauthorized: socketUrl.startsWith('https://localhost') ? false : true, + }) + .open(), }); const timer = new plugins.smarttime.Timer(5000); timer.start(); timer.completed.then(() => { logger.log('warn', 'connection to server timed out.'); - this.disconnect(); + this.disconnect(true); }); // authentication flow - this.socketConnection.socket.on( - 'requestAuth', - (dataArg: interfaces.IRequestAuthPayload) => { - timer.reset(); - logger.log('info', `server ${dataArg.serverAlias} requested authentication`); + this.socketConnection.socket.on('requestAuth', (dataArg: interfaces.IRequestAuthPayload) => { + timer.reset(); + logger.log('info', `server ${dataArg.serverAlias} requested authentication`); - // lets register the authenticated event - this.socketConnection.socket.on('authenticated', async () => { - this.remoteShortId = dataArg.serverAlias; - logger.log('info', 'client is authenticated'); - this.socketConnection.authenticated = true; - await this.socketConnection.listenToFunctionRequests(); - }); + // lets register the authenticated event + this.socketConnection.socket.on('authenticated', async () => { + this.remoteShortId = dataArg.serverAlias; + logger.log('info', 'client is authenticated'); + this.socketConnection.authenticated = true; + await this.socketConnection.listenToFunctionRequests(); + }); - this.socketConnection.socket.on('serverFullyReactive', async () => { - // lets take care of retagging - const oldTagStore = this.tagStore; - this.tagStoreSubscription?.unsubscribe(); - for (const keyArg of Object.keys(this.tagStore)) { - this.socketConnection.addTag(this.tagStore[keyArg]); + this.socketConnection.socket.on('serverFullyReactive', async () => { + // lets take care of retagging + const oldTagStore = this.tagStore; + this.tagStoreSubscription?.unsubscribe(); + for (const keyArg of Object.keys(this.tagStore)) { + this.socketConnection.addTag(this.tagStore[keyArg]); + } + this.tagStoreSubscription = this.socketConnection.tagStoreObservable.subscribe( + (tagStoreArg) => { + this.tagStore = tagStoreArg; } - this.tagStoreSubscription = this.socketConnection.tagStoreObservable.subscribe( - (tagStoreArg) => { - this.tagStore = tagStoreArg; - } - ); + ); - for (const tag of Object.keys(oldTagStore)) { - await this.addTag(oldTagStore[tag]); - } - done.resolve(); - }); + for (const tag of Object.keys(oldTagStore)) { + await this.addTag(oldTagStore[tag]); + } + this.updateStatus('connected'); + done.resolve(); + }); - // lets register the forbidden event - this.socketConnection.socket.on('forbidden', async () => { - logger.log('warn', `disconnecting due to being forbidden to use the ressource`); - await this.disconnect(); - }); + // lets register the forbidden event + this.socketConnection.socket.on('forbidden', async () => { + logger.log('warn', `disconnecting due to being forbidden to use the ressource`); + await this.disconnect(); + }); - // lets provide the actual auth data - this.socketConnection.socket.emit('dataAuth', { - alias: this.alias, - }); - } - ); + // lets provide the actual auth data + this.socketConnection.socket.emit('dataAuth', { + alias: this.alias, + }); + }); // handle connection - this.socketConnection.socket.on('connect', async () => { - this.updateStatus('connected'); - }); + this.socketConnection.socket.on('connect', async () => {}); // handle disconnection and errors this.socketConnection.socket.on('disconnect', async () => { - await this.disconnect(); + await this.disconnect(true); }); this.socketConnection.socket.on('reconnect_failed', async () => { - await this.disconnect(); + await this.disconnect(true); }); this.socketConnection.socket.on('connect_error', async () => { - await this.disconnect(); + await this.disconnect(true); }); return done.promise; } @@ -188,19 +187,25 @@ export class SmartsocketClient { /** * disconnect from the server */ - public async disconnect() { + public async disconnect(useAutoconnectSetting = false) { + if (this.eventStatus === 'disconnecting') { + return; + } + this.updateStatus('disconnecting'); this.tagStoreSubscription?.unsubscribe(); if (this.socketConnection) { await this.socketConnection.disconnect(); this.socketConnection = undefined; - logger.log('ok', 'disconnected!'); + logger.log('ok', 'disconnected socket!'); } logger.log('warn', `disconnected from server ${this.remoteShortId}`); this.remoteShortId = null; - this.updateStatus('disconnected'); - if (this.autoReconnect) { + if (this.autoReconnect && useAutoconnectSetting && this.eventStatus !== 'connecting') { + this.updateStatus('connecting'); this.tryDebouncedReconnect(); + } else if (this.eventStatus === 'connected') { + this.updateStatus('disconnected'); } } @@ -216,6 +221,7 @@ export class SmartsocketClient { * try a reconnection */ public async tryDebouncedReconnect() { + console.log('debounced reconnect!'); await plugins.smartdelay.delayForRandom(10000, 20000); await this.connect(); } diff --git a/ts/smartsocket.classes.socketconnection.ts b/ts/smartsocket.classes.socketconnection.ts index 70cdbb4..1bde7ea 100644 --- a/ts/smartsocket.classes.socketconnection.ts +++ b/ts/smartsocket.classes.socketconnection.ts @@ -197,7 +197,7 @@ export class SocketConnection { this.remoteTagStoreObservable.next(tagStoreArg); }); - logger.log('info', `now listening to function requests for ${this.alias}`); + logger.log('info', `now listening to function requests for ${this.alias} on side ${this.side}`); done.resolve(this); } else { const errMessage = 'socket needs to be authenticated first'; diff --git a/ts/smartsocket.classes.socketserver.ts b/ts/smartsocket.classes.socketserver.ts index d8d50ca..0c0acb1 100644 --- a/ts/smartsocket.classes.socketserver.ts +++ b/ts/smartsocket.classes.socketserver.ts @@ -11,6 +11,7 @@ import { logger } from './smartsocket.logging'; */ export class SocketServer { private smartsocket: Smartsocket; + private httpServerDeferred: plugins.smartpromise.Deferred; private httpServer: pluginsTyped.http.Server | pluginsTyped.https.Server; /** @@ -30,14 +31,19 @@ export class SocketServer { serverType: 'smartexpress', serverArg: pluginsTyped.smartexpress.Server ) { + this.httpServerDeferred = plugins.smartpromise.defer(); await serverArg.startedPromise; this.httpServer = serverArg.httpServer; + this.httpServerDeferred.resolve(); } /** * gets the server for socket.io */ - public getServerForSocketIo() { + public async getServerForSocketIo() { + if (this.httpServerDeferred) { + await this.httpServerDeferred.promise; + } if (this.httpServer) { return this.httpServer; } else {