Compare commits

..

14 Commits

Author SHA1 Message Date
c390881a4e 1.1.56 2019-11-08 17:31:04 +01:00
5e64f4ca25 fix(core): update 2019-11-08 17:31:04 +01:00
765bc73197 1.1.55 2019-11-08 17:11:41 +01:00
105acaf97b fix(core): update 2019-11-08 17:11:41 +01:00
5139136af4 1.1.54 2019-11-07 00:26:47 +01:00
bcc4ce9a87 fix(core): update 2019-11-07 00:26:47 +01:00
20e7584eb9 1.1.53 2019-11-03 20:23:15 +01:00
59cbc343cc fix(core): update 2019-11-03 20:23:15 +01:00
75aa1f6f0d 1.1.52 2019-11-03 19:17:26 +01:00
3f073ab9b3 fix(core): update 2019-11-03 19:17:26 +01:00
08c1618ea8 1.1.51 2019-11-03 18:33:46 +01:00
eb181fa2f6 fix(core): update 2019-11-03 18:33:46 +01:00
c901ab75d3 1.1.50 2019-11-03 16:48:35 +01:00
075c59ed2c fix(core): update 2019-11-03 16:48:35 +01:00
9 changed files with 581 additions and 346 deletions

741
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@pushrocks/smartsocket", "name": "@pushrocks/smartsocket",
"version": "1.1.49", "version": "1.1.56",
"description": "easy and secure websocket communication", "description": "easy and secure websocket communication",
"main": "dist/index.js", "main": "dist/index.js",
"typings": "dist/index.d.ts", "typings": "dist/index.d.ts",
@ -21,25 +21,25 @@
"dependencies": { "dependencies": {
"@apiglobal/typedrequest-interfaces": "^1.0.7", "@apiglobal/typedrequest-interfaces": "^1.0.7",
"@pushrocks/lik": "^3.0.11", "@pushrocks/lik": "^3.0.11",
"@pushrocks/smartdelay": "^2.0.3", "@pushrocks/smartdelay": "^2.0.6",
"@pushrocks/smartexpress": "^3.0.40", "@pushrocks/smartexpress": "^3.0.52",
"@pushrocks/smarthash": "^2.0.6", "@pushrocks/smarthash": "^2.0.6",
"@pushrocks/smartlog": "^2.0.19", "@pushrocks/smartlog": "^2.0.21",
"@pushrocks/smartpromise": "^3.0.2", "@pushrocks/smartpromise": "^3.0.6",
"@types/shortid": "0.0.29", "@pushrocks/smartrx": "^2.0.5",
"@types/socket.io": "^2.1.2", "@pushrocks/smartunique": "^3.0.1",
"@types/socket.io": "^2.1.4",
"@types/socket.io-client": "^1.4.32", "@types/socket.io-client": "^1.4.32",
"shortid": "^2.2.15", "socket.io": "^2.3.0",
"socket.io": "^2.2.0", "socket.io-client": "^2.3.0"
"socket.io-client": "^2.2.0"
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.1.17", "@gitzone/tsbuild": "^2.1.17",
"@gitzone/tsrun": "^1.2.8", "@gitzone/tsrun": "^1.2.8",
"@gitzone/tstest": "^1.0.24", "@gitzone/tstest": "^1.0.28",
"@pushrocks/tapbundle": "^3.0.13", "@pushrocks/tapbundle": "^3.0.13",
"@types/node": "^12.7.4", "@types/node": "^12.12.5",
"tslint": "^5.19.0", "tslint": "^5.20.0",
"tslint-config-prettier": "^1.18.0" "tslint-config-prettier": "^1.18.0"
}, },
"private": false, "private": false,

View File

@ -21,7 +21,7 @@ export interface IReqResClient {
}; };
response: { response: {
value1: string; value1: string;
} };
} }
export interface IReqResServer { export interface IReqResServer {

View File

@ -0,0 +1,5 @@
export interface IRequestAuthPayload {
serverShortId: string;
}
export type TConnectionEvent = 'terminated' | 'error';

1
ts/interfaces/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './connection';

View File

@ -16,6 +16,10 @@ export interface ISmartsocketConstructorOptions {
} }
export class Smartsocket { export class Smartsocket {
/**
* a unique id to detect server restarts
*/
public shortId = plugins.smartunique.shortId();
public options: ISmartsocketConstructorOptions; public options: ISmartsocketConstructorOptions;
public io: SocketIO.Server; public io: SocketIO.Server;
public socketConnections = new Objectmap<SocketConnection>(); public socketConnections = new Objectmap<SocketConnection>();
@ -80,7 +84,7 @@ export class Smartsocket {
funcName: functionNameArg funcName: functionNameArg
}, },
originSocketConnection: targetSocketConnectionArg, originSocketConnection: targetSocketConnectionArg,
shortId: plugins.shortid.generate(), shortId: plugins.smartunique.shortId(),
side: 'requesting' side: 'requesting'
}); });
const response: ISocketFunctionCallDataResponse<T> = await socketRequest.dispatch(); const response: ISocketFunctionCallDataResponse<T> = await socketRequest.dispatch();

View File

@ -1,9 +1,11 @@
import * as plugins from './smartsocket.plugins'; import * as plugins from './smartsocket.plugins';
import * as interfaces from './interfaces';
import { SocketConnection } from './smartsocket.classes.socketconnection'; import { SocketConnection } from './smartsocket.classes.socketconnection';
import { ISocketFunctionCallDataRequest, SocketFunction } from './smartsocket.classes.socketfunction'; import { ISocketFunctionCallDataRequest, SocketFunction } from './smartsocket.classes.socketfunction';
import { ISocketRequestDataObject, SocketRequest } from './smartsocket.classes.socketrequest'; import { ISocketRequestDataObject, SocketRequest } from './smartsocket.classes.socketrequest';
import { SocketRole } from './smartsocket.classes.socketrole'; import { SocketRole } from './smartsocket.classes.socketrole';
import { defaultLogger } from '@pushrocks/smartlog';
/** /**
* interface for class SmartsocketClient * interface for class SmartsocketClient
@ -14,14 +16,24 @@ export interface ISmartsocketClientOptions {
alias: string; // an alias makes it easier to identify this client in a multo client environment alias: string; // an alias makes it easier to identify this client in a multo client environment
role: string; role: string;
password: string; // by setting a password access to functions can be limited password: string; // by setting a password access to functions can be limited
autoReconnect?: boolean;
} }
export class SmartsocketClient { export class SmartsocketClient {
// a unique id
public shortId = plugins.smartunique.shortId();
// the shortId of the remote we connect to
public remoteShortId: string = null;
public alias: string; public alias: string;
public socketRole: SocketRole; public socketRole: SocketRole;
public socketConnection: SocketConnection; public socketConnection: SocketConnection;
public serverUrl: string; public serverUrl: string;
public serverPort: number; public serverPort: number;
public autoReconnect: boolean;
public eventSubject = new plugins.smartrx.rxjs.Subject<interfaces.TConnectionEvent>();
public socketFunctions = new plugins.lik.Objectmap<SocketFunction<any>>(); public socketFunctions = new plugins.lik.Objectmap<SocketFunction<any>>();
public socketRequests = new plugins.lik.Objectmap<SocketRequest<any>>(); public socketRequests = new plugins.lik.Objectmap<SocketRequest<any>>();
@ -35,6 +47,7 @@ export class SmartsocketClient {
name: optionsArg.role, name: optionsArg.role,
passwordHash: optionsArg.password passwordHash: optionsArg.password
}); });
this.autoReconnect = optionsArg.autoReconnect;
} }
public addSocketFunction(socketFunction: SocketFunction<any>) { public addSocketFunction(socketFunction: SocketFunction<any>) {
@ -55,21 +68,58 @@ export class SmartsocketClient {
role: this.socketRole, role: this.socketRole,
side: 'client', side: 'client',
smartsocketHost: this, smartsocketHost: this,
socket: plugins.socketIoClient(socketUrl, { multiplex: false }) socket: plugins.socketIoClient(socketUrl, {
multiplex: false,
reconnectionAttempts: 5,
})
}); });
this.socketConnection.socket.on('requestAuth', () => {
const timer = new plugins.smarttime.Timer(5000);
timer.start();
timer.completed.then(() => {
defaultLogger.log('warn', 'connection to server timed out.');
this.disconnect();
});
// authentication flow
this.socketConnection.socket.on('requestAuth', (requestAuthPayload: interfaces.IRequestAuthPayload) => {
timer.reset();
plugins.smartlog.defaultLogger.log('info', 'server requested authentication'); plugins.smartlog.defaultLogger.log('info', 'server requested authentication');
this.socketConnection.socket.emit('dataAuth', {
role: this.socketRole.name, // lets register the authenticated event
password: this.socketRole.passwordHash,
alias: this.alias
});
this.socketConnection.socket.on('authenticated', () => { this.socketConnection.socket.on('authenticated', () => {
this.remoteShortId = requestAuthPayload.serverShortId;
plugins.smartlog.defaultLogger.log('info', 'client is authenticated'); plugins.smartlog.defaultLogger.log('info', 'client is authenticated');
this.socketConnection.authenticated = true; this.socketConnection.authenticated = true;
this.socketConnection.listenToFunctionRequests(); this.socketConnection.listenToFunctionRequests();
done.resolve(); done.resolve();
}); });
// lets register the forbidden event
this.socketConnection.socket.on('forbidden', async () => {
defaultLogger.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', {
role: this.socketRole.name,
password: this.socketRole.passwordHash,
alias: this.alias
});
});
// handle disconnection and errors
this.socketConnection.socket.on('disconnect', async () => {
await this.disconnect();
});
this.socketConnection.socket.on('reconnect_failed', async () => {
await this.disconnect();
});
this.socketConnection.socket.on('connect_error', async () => {
await this.disconnect();
}); });
return done.promise; return done.promise;
} }
@ -78,10 +128,27 @@ export class SmartsocketClient {
* disconnect from the server * disconnect from the server
*/ */
public async disconnect() { public async disconnect() {
this.socketConnection.socket.disconnect(true); if (this.socketConnection) {
this.socketConnection.disconnect();
this.socketConnection = undefined; this.socketConnection = undefined;
plugins.smartlog.defaultLogger.log('ok', 'disconnected!'); plugins.smartlog.defaultLogger.log('ok', 'disconnected!');
} }
defaultLogger.log('warn', `disconnected from server ${this.remoteShortId}`);
this.remoteShortId = null;
this.eventSubject.next('terminated');
if (this.autoReconnect) {
this.tryDebouncedReconnect();
}
}
/**
* try a reconnection
*/
public async tryDebouncedReconnect() {
await plugins.smartdelay.delayForRandom(10000, 60000);
await this.connect();
}
/** /**
* dispatches a server call * dispatches a server call
@ -93,7 +160,7 @@ export class SmartsocketClient {
const socketRequest = new SocketRequest<T>(this, { const socketRequest = new SocketRequest<T>(this, {
side: 'requesting', side: 'requesting',
originSocketConnection: this.socketConnection, originSocketConnection: this.socketConnection,
shortId: plugins.shortid.generate(), shortId: plugins.smartunique.shortId(),
funcCallData: { funcCallData: {
funcName: functionNameArg, funcName: functionNameArg,
funcDataArg: dataArg funcDataArg: dataArg

View File

@ -1,4 +1,5 @@
import * as plugins from './smartsocket.plugins'; import * as plugins from './smartsocket.plugins';
import * as interfaces from './interfaces';
import { Objectmap } from '@pushrocks/lik'; import { Objectmap } from '@pushrocks/lik';
@ -53,6 +54,9 @@ export class SocketConnection {
public role: SocketRole; public role: SocketRole;
public smartsocketRef: Smartsocket | SmartsocketClient; public smartsocketRef: Smartsocket | SmartsocketClient;
public socket: SocketIO.Socket | SocketIOClient.Socket; public socket: SocketIO.Socket | SocketIOClient.Socket;
public eventSubject = new plugins.smartrx.rxjs.Subject<interfaces.TConnectionEvent>();
constructor(optionsArg: ISocketConnectionConstructorOptions) { constructor(optionsArg: ISocketConnectionConstructorOptions) {
this.alias = optionsArg.alias; this.alias = optionsArg.alias;
this.authenticated = optionsArg.authenticated; this.authenticated = optionsArg.authenticated;
@ -63,12 +67,12 @@ export class SocketConnection {
// standard behaviour that is always true // standard behaviour that is always true
allSocketConnections.add(this); allSocketConnections.add(this);
this.socket.on('disconnect', () => { this.socket.on('disconnect', async () => {
plugins.smartlog.defaultLogger.log( plugins.smartlog.defaultLogger.log(
'info', 'info',
`SocketConnection with >alias ${this.alias} on >side ${this.side} disconnected` `SocketConnection with >alias ${this.alias} on >side ${this.side} disconnected`
); );
this.socket.disconnect(); await this.disconnect();
allSocketConnections.remove(this); allSocketConnections.remove(this);
}); });
} }
@ -80,7 +84,7 @@ export class SocketConnection {
*/ */
public authenticate() { public authenticate() {
const done = plugins.smartpromise.defer(); const done = plugins.smartpromise.defer();
this.socket.on('dataAuth', (dataArg: ISocketConnectionAuthenticationObject) => { this.socket.on('dataAuth', async (dataArg: ISocketConnectionAuthenticationObject) => {
plugins.smartlog.defaultLogger.log( plugins.smartlog.defaultLogger.log(
'info', 'info',
'received authentication data. now hashing and comparing...' 'received authentication data. now hashing and comparing...'
@ -99,11 +103,14 @@ export class SocketConnection {
done.resolve(this); done.resolve(this);
} else { } else {
this.authenticated = false; this.authenticated = false;
this.socket.disconnect(); await this.disconnect();
done.reject('not authenticated'); done.reject('not authenticated');
} }
}); });
this.socket.emit('requestAuth'); const requestAuthPayload: interfaces.IRequestAuthPayload = {
serverShortId: this.smartsocketRef.shortId
};
this.socket.emit('requestAuth', requestAuthPayload);
return done.promise; return done.promise;
} }
@ -163,5 +170,9 @@ export class SocketConnection {
return done.promise; return done.promise;
} }
// sending ---------------------- // disconnecting ----------------------
public async disconnect() {
this.socket.disconnect(true);
this.eventSubject.next('terminated');
}
} }

View File

@ -10,6 +10,9 @@ import * as smarthash from '@pushrocks/smarthash';
import * as smartdelay from '@pushrocks/smartdelay'; import * as smartdelay from '@pushrocks/smartdelay';
import * as smartexpress from '@pushrocks/smartexpress'; import * as smartexpress from '@pushrocks/smartexpress';
import * as smartpromise from '@pushrocks/smartpromise'; import * as smartpromise from '@pushrocks/smartpromise';
import * as smarttime from '@pushrocks/smarttime';
import * as smartunique from '@pushrocks/smartunique';
import * as smartrx from '@pushrocks/smartrx';
export { export {
@ -18,16 +21,17 @@ export {
smarthash, smarthash,
smartdelay, smartdelay,
smartexpress, smartexpress,
smartpromise smartpromise,
smarttime,
smartunique,
smartrx
}; };
// third party scope // third party scope
import * as shortid from 'shortid';
import socketIo from 'socket.io'; import socketIo from 'socket.io';
import socketIoClient from 'socket.io-client'; import socketIoClient from 'socket.io-client';
export { export {
shortid,
socketIo, socketIo,
socketIoClient socketIoClient
}; };