Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
6fdf0d9955 | |||
499a1893f9 | |||
2754447aae | |||
544277cb8a | |||
9a23960d21 | |||
23cca6cce3 | |||
2a9e58cc35 | |||
7d6a9921b5 | |||
3ee46a31f7 | |||
d72310ce10 | |||
1e14166ddb | |||
be38e91548 | |||
6c2057b119 | |||
08d7224016 | |||
bfa3330eb6 | |||
644fa2a49d | |||
c0dad3a977 | |||
a921033cc4 | |||
21e4712b04 | |||
df43bc2974 | |||
564988185d | |||
8442f3570f | |||
196357c878 | |||
bc187b7e41 | |||
0e54bf889f | |||
4c211bc82e | |||
a1be281670 | |||
1a44d2027c |
23488
package-lock.json
generated
23488
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@pushrocks/smartsocket",
|
||||
"version": "1.1.68",
|
||||
"version": "1.2.10",
|
||||
"description": "easy and secure websocket communication",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
@ -22,25 +22,26 @@
|
||||
"@apiglobal/typedrequest-interfaces": "^1.0.15",
|
||||
"@pushrocks/isohash": "^1.0.2",
|
||||
"@pushrocks/isounique": "^1.0.4",
|
||||
"@pushrocks/lik": "^4.0.17",
|
||||
"@pushrocks/smartdelay": "^2.0.10",
|
||||
"@pushrocks/smartenv": "^4.0.15",
|
||||
"@pushrocks/smartexpress": "^3.0.76",
|
||||
"@pushrocks/smartlog": "^2.0.39",
|
||||
"@pushrocks/smartpromise": "^3.0.6",
|
||||
"@pushrocks/lik": "^5.0.0",
|
||||
"@pushrocks/smartdelay": "^2.0.13",
|
||||
"@pushrocks/smartenv": "^4.0.16",
|
||||
"@pushrocks/smartexpress": "^3.0.108",
|
||||
"@pushrocks/smartjson": "^4.0.6",
|
||||
"@pushrocks/smartlog": "^2.0.44",
|
||||
"@pushrocks/smartpromise": "^3.1.6",
|
||||
"@pushrocks/smartrx": "^2.0.19",
|
||||
"@pushrocks/smarttime": "^3.0.35",
|
||||
"@types/socket.io": "^2.1.11",
|
||||
"@types/socket.io-client": "^1.4.33",
|
||||
"socket.io": "^2.3.0",
|
||||
"socket.io-client": "^2.3.0"
|
||||
"@pushrocks/smarttime": "^3.0.43",
|
||||
"@types/socket.io": "^3.0.2",
|
||||
"@types/socket.io-client": "^3.0.0",
|
||||
"socket.io": "^3.1.0",
|
||||
"socket.io-client": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gitzone/tsbuild": "^2.1.25",
|
||||
"@gitzone/tsrun": "^1.2.12",
|
||||
"@gitzone/tstest": "^1.0.48",
|
||||
"@pushrocks/tapbundle": "^3.2.9",
|
||||
"@types/node": "^14.11.2",
|
||||
"@gitzone/tsbuild": "^2.1.28",
|
||||
"@gitzone/tsrun": "^1.2.18",
|
||||
"@gitzone/tstest": "^1.0.60",
|
||||
"@pushrocks/tapbundle": "^3.2.15",
|
||||
"@types/node": "^17.0.9",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-prettier": "^1.18.0"
|
||||
},
|
||||
|
@ -110,7 +110,6 @@ mySmartsocketClient.serverCall('function', functionCallData).then((functionRespo
|
||||
> `data` is always a js object that you can design for your specific needs.
|
||||
> It supports buffers for large binary data network exchange.
|
||||
|
||||
|
||||
## Contribution
|
||||
|
||||
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
|
||||
|
@ -1,11 +1,9 @@
|
||||
// tslint:disable-next-line:no-implicit-dependencies
|
||||
import { expect, tap } from '@pushrocks/tapbundle';
|
||||
|
||||
import * as smarthash from '@pushrocks/smarthash';
|
||||
import * as smartpromise from '@pushrocks/smartpromise';
|
||||
import * as isohash from '@pushrocks/isohash';
|
||||
import * as smartexpress from '@pushrocks/smartexpress';
|
||||
|
||||
import socketIoClient = require('socket.io-client');
|
||||
import smartsocket = require('../ts/index');
|
||||
|
||||
let testSmartsocket: smartsocket.Smartsocket;
|
||||
@ -15,7 +13,7 @@ let testSocketFunction1: smartsocket.SocketFunction<any>;
|
||||
let myseServer: smartexpress.Server;
|
||||
|
||||
const testConfig = {
|
||||
port: 3005,
|
||||
port: 3000,
|
||||
};
|
||||
|
||||
// class smartsocket
|
||||
@ -40,7 +38,7 @@ tap.test('Should accept an smartExpressServer as server', async () => {
|
||||
tap.test('should add a socketrole', async () => {
|
||||
testSocketRole1 = new smartsocket.SocketRole({
|
||||
name: 'testRole1',
|
||||
passwordHash: smarthash.sha256FromStringSync('testPassword'),
|
||||
passwordHash: await isohash.sha256FromString('testPassword'),
|
||||
});
|
||||
testSmartsocket.addSocketRoles([testSocketRole1]);
|
||||
});
|
||||
@ -87,17 +85,23 @@ tap.test('2 clients should connect in parallel', async () => {
|
||||
});
|
||||
|
||||
tap.test('should be able to make a functionCall from client to server', async () => {
|
||||
const totalCycles = 20000
|
||||
const totalCycles = 20000;
|
||||
let counter = 0;
|
||||
let startTime = Date.now();
|
||||
while (counter < totalCycles) {
|
||||
const response = await testSmartsocketClient.serverCall('testFunction1', {
|
||||
value1: 'hello',
|
||||
const randomString = `hello ${Math.random()}`;
|
||||
const response: any = await testSmartsocketClient.serverCall('testFunction1', {
|
||||
value1: randomString,
|
||||
});
|
||||
expect(response.value1).to.equal(randomString);
|
||||
if (counter % 100 === 0) {
|
||||
console.log(`processed 100 more messages in ${Date.now() - startTime}ms. ${totalCycles - counter} messages to go.`);
|
||||
console.log(
|
||||
`processed 100 more messages in ${Date.now() - startTime}ms. ${
|
||||
totalCycles - counter
|
||||
} messages to go.`
|
||||
);
|
||||
startTime = Date.now();
|
||||
};
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
34
test/test.ts
34
test/test.ts
@ -1,11 +1,8 @@
|
||||
// tslint:disable-next-line:no-implicit-dependencies
|
||||
import { expect, tap } from '@pushrocks/tapbundle';
|
||||
|
||||
import * as smarthash from '@pushrocks/smarthash';
|
||||
import * as smartpromise from '@pushrocks/smartpromise';
|
||||
|
||||
import socketIoClient = require('socket.io-client');
|
||||
import smartsocket = require('../ts/index');
|
||||
import * as isohash from '@pushrocks/isohash';
|
||||
|
||||
let testSmartsocket: smartsocket.Smartsocket;
|
||||
let testSmartsocketClient: smartsocket.SmartsocketClient;
|
||||
@ -48,7 +45,7 @@ tap.test('should create a new smartsocket', async () => {
|
||||
tap.test('should add a socketrole', async () => {
|
||||
testSocketRole1 = new smartsocket.SocketRole({
|
||||
name: 'testRole1',
|
||||
passwordHash: smarthash.sha256FromStringSync('testPassword'),
|
||||
passwordHash: await isohash.sha256FromString('testPassword'),
|
||||
});
|
||||
testSmartsocket.addSocketRoles([testSocketRole1]);
|
||||
});
|
||||
@ -93,6 +90,31 @@ tap.test('should react to a new websocket connection from client', async () => {
|
||||
await testSmartsocketClient.connect();
|
||||
});
|
||||
|
||||
tap.test('should be able to tag a connection from client', async (tools) => {
|
||||
await testSmartsocketClient.addTag({
|
||||
id: 'awesome',
|
||||
payload: 'yes',
|
||||
});
|
||||
const tagOnServerSide = await testSmartsocket.socketConnections
|
||||
.findSync((socketConnection) => {
|
||||
return true;
|
||||
})
|
||||
.getTagById('awesome');
|
||||
expect(tagOnServerSide.payload).to.equal('yes');
|
||||
});
|
||||
|
||||
tap.test('should be able to tag a connection from server', async (tools) => {
|
||||
await testSmartsocket.socketConnections
|
||||
.findSync((socketConnection) => {
|
||||
return true;
|
||||
}).addTag({
|
||||
id: 'awesome2',
|
||||
payload: 'absolutely',
|
||||
});
|
||||
const tagOnClientSide = await testSmartsocketClient.socketConnection.getTagById('awesome2');
|
||||
expect(tagOnClientSide.payload).to.equal('absolutely');
|
||||
});
|
||||
|
||||
tap.test('2 clients should connect in parallel', async () => {
|
||||
// TODO: implement parallel test
|
||||
});
|
||||
@ -111,7 +133,7 @@ tap.test('should be able to make a functionCall from server to client', async ()
|
||||
{
|
||||
hi: 'hi there from server',
|
||||
},
|
||||
testSmartsocket.socketConnections.find((socketConnection) => {
|
||||
testSmartsocket.socketConnections.findSync((socketConnection) => {
|
||||
return true;
|
||||
})
|
||||
);
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './connection';
|
||||
export * from './tag';
|
||||
|
6
ts/interfaces/tag.ts
Normal file
6
ts/interfaces/tag.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface ITag<T = any> {
|
||||
id: string;
|
||||
payload: T;
|
||||
}
|
||||
|
||||
export type TTagStore = { [key: string]: ITag };
|
@ -62,7 +62,7 @@ export class Smartsocket {
|
||||
this.socketConnections.forEach((socketObjectArg: SocketConnection) => {
|
||||
if (socketObjectArg) {
|
||||
logger.log('info', `disconnect socket with >>alias ${socketObjectArg.alias}`);
|
||||
socketObjectArg.socket.disconnect();
|
||||
socketObjectArg.socket.disconnect(true);
|
||||
}
|
||||
});
|
||||
this.socketConnections.wipe();
|
||||
@ -113,7 +113,7 @@ export class Smartsocket {
|
||||
/**
|
||||
* the standard handler for new socket connections
|
||||
*/
|
||||
private async _handleSocketConnection(socketArg: pluginsTyped.socketIo.Socket) {
|
||||
private async _handleSocketConnection(socketArg: SocketIO.Socket) {
|
||||
const socketConnection: SocketConnection = new SocketConnection({
|
||||
alias: undefined,
|
||||
authenticated: false,
|
||||
@ -124,6 +124,12 @@ export class Smartsocket {
|
||||
});
|
||||
logger.log('info', 'Socket connected. Trying to authenticate...');
|
||||
this.socketConnections.add(socketConnection);
|
||||
const disconnectSubscription = socketConnection.eventSubject.subscribe((eventArg) => {
|
||||
if (eventArg === 'disconnected') {
|
||||
this.socketConnections.remove(socketConnection);
|
||||
disconnectSubscription.unsubscribe();
|
||||
}
|
||||
})
|
||||
await socketConnection.authenticate();
|
||||
await socketConnection.listenToFunctionRequests();
|
||||
}
|
||||
|
@ -45,6 +45,40 @@ export class SmartsocketClient {
|
||||
public socketRequests = new plugins.lik.ObjectMap<SocketRequest<any>>();
|
||||
public socketRoles = new plugins.lik.ObjectMap<SocketRole>();
|
||||
|
||||
// tagStore
|
||||
private tagStore: { [key: string]: interfaces.ITag } = {};
|
||||
private tagStoreSubscription: plugins.smartrx.rxjs.Subscription;
|
||||
|
||||
/**
|
||||
* adds a tag to a connection
|
||||
*/
|
||||
public async addTag(tagArg: interfaces.ITag) {
|
||||
if (this.socketConnection) {
|
||||
await this.socketConnection.addTag(tagArg);
|
||||
} else {
|
||||
this.tagStore[tagArg.id] = tagArg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a tag by id
|
||||
* @param tagIdArg
|
||||
*/
|
||||
public async getTagById(tagIdArg: interfaces.ITag['id']) {
|
||||
return this.tagStore[tagIdArg];
|
||||
}
|
||||
|
||||
/**
|
||||
* removes a tag from a connection
|
||||
*/
|
||||
public async removeTagById(tagIdArg: interfaces.ITag['id']) {
|
||||
if (this.socketConnection) {
|
||||
this.socketConnection.removeTagById(tagIdArg);
|
||||
} else {
|
||||
delete this.tagStore[tagIdArg];
|
||||
}
|
||||
}
|
||||
|
||||
constructor(optionsArg: ISmartsocketClientOptions) {
|
||||
this.alias = optionsArg.alias;
|
||||
this.serverUrl = optionsArg.url;
|
||||
@ -85,6 +119,7 @@ export class SmartsocketClient {
|
||||
socket: await socketIoClient.connect(socketUrl, {
|
||||
multiplex: false,
|
||||
reconnectionAttempts: 5,
|
||||
rejectUnauthorized: socketUrl.startsWith('https://localhost') ? false : true,
|
||||
}),
|
||||
});
|
||||
|
||||
@ -128,6 +163,16 @@ export class SmartsocketClient {
|
||||
|
||||
// handle connection
|
||||
this.socketConnection.socket.on('connect', async () => {
|
||||
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.updateStatus('connected');
|
||||
});
|
||||
|
||||
@ -163,6 +208,14 @@ export class SmartsocketClient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stops the client completely
|
||||
*/
|
||||
public async stop() {
|
||||
this.autoReconnect = false;
|
||||
await this.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* try a reconnection
|
||||
*/
|
||||
|
@ -8,7 +8,7 @@ import { SocketRequest, ISocketRequestDataObject } from './smartsocket.classes.s
|
||||
import { SocketRole } from './smartsocket.classes.socketrole';
|
||||
|
||||
// socket.io
|
||||
import * as SocketIO from 'socket.io';
|
||||
import * as pluginsTyped from './smartsocket.pluginstyped';
|
||||
import { SmartsocketClient } from './smartsocket.classes.smartsocketclient';
|
||||
import { logger } from './smartsocket.logging';
|
||||
|
||||
@ -57,6 +57,10 @@ export class SocketConnection {
|
||||
public eventSubject = new plugins.smartrx.rxjs.Subject<interfaces.TConnectionStatus>();
|
||||
public eventStatus: interfaces.TConnectionStatus = 'new';
|
||||
|
||||
private tagStore: interfaces.TTagStore = {};
|
||||
public tagStoreObservable = new plugins.smartrx.rxjs.Subject<interfaces.TTagStore>();
|
||||
public remoteTagStoreObservable = new plugins.smartrx.rxjs.Subject<interfaces.TTagStore>();
|
||||
|
||||
constructor(optionsArg: ISocketConnectionConstructorOptions) {
|
||||
this.alias = optionsArg.alias;
|
||||
this.authenticated = optionsArg.authenticated;
|
||||
@ -79,9 +83,46 @@ export class SocketConnection {
|
||||
);
|
||||
await this.disconnect();
|
||||
allSocketConnections.remove(this);
|
||||
this.eventSubject.next('disconnected');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a tag to a connection
|
||||
*/
|
||||
public async addTag(tagArg: interfaces.ITag) {
|
||||
const done = plugins.smartpromise.defer();
|
||||
this.tagStore[tagArg.id] = tagArg;
|
||||
this.tagStoreObservable.next(this.tagStore);
|
||||
const remoteSubscription = this.remoteTagStoreObservable.subscribe((remoteTagStore) => {
|
||||
const localTagString = plugins.smartjson.stringify(tagArg);
|
||||
const remoteTagString = plugins.smartjson.stringify(remoteTagStore[tagArg.id]);
|
||||
if (localTagString === remoteTagString) {
|
||||
remoteSubscription.unsubscribe();
|
||||
done.resolve();
|
||||
}
|
||||
});
|
||||
this.socket.emit('updateTagStore', this.tagStore);
|
||||
await done.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a tag by id
|
||||
* @param tagIdArg
|
||||
*/
|
||||
public async getTagById(tagIdArg: interfaces.ITag['id']) {
|
||||
return this.tagStore[tagIdArg];
|
||||
}
|
||||
|
||||
/**
|
||||
* removes a tag from a connection
|
||||
*/
|
||||
public async removeTagById(tagIdArg: interfaces.ITag['id']) {
|
||||
delete this.tagStore[tagIdArg];
|
||||
this.tagStoreObservable.next(this.tagStore);
|
||||
this.socket.emit('updateTagStore', this.tagStore);
|
||||
}
|
||||
|
||||
// authenticating --------------------------
|
||||
|
||||
/**
|
||||
@ -91,7 +132,7 @@ export class SocketConnection {
|
||||
const done = plugins.smartpromise.defer();
|
||||
this.socket.on('dataAuth', async (dataArg: ISocketConnectionAuthenticationObject) => {
|
||||
logger.log('info', 'received authentication data. now hashing and comparing...');
|
||||
this.socket.removeListener('dataAuth', () => {});
|
||||
this.socket.removeAllListeners('dataAuth');
|
||||
if (await SocketRole.checkPasswordForRole(dataArg, this.smartsocketRef)) {
|
||||
// TODO: authenticate password
|
||||
this.alias = dataArg.alias;
|
||||
@ -124,7 +165,7 @@ export class SocketConnection {
|
||||
this.socket.on('function', (dataArg: ISocketRequestDataObject<any>) => {
|
||||
// check if requested function is available to the socket's scope
|
||||
// logger.log('info', 'function request received');
|
||||
const referencedFunction: SocketFunction<any> = this.role.allowedFunctions.find(
|
||||
const referencedFunction: SocketFunction<any> = this.role.allowedFunctions.findSync(
|
||||
(socketFunctionArg) => {
|
||||
return socketFunctionArg.name === dataArg.funcCallData.funcName;
|
||||
}
|
||||
@ -150,6 +191,16 @@ export class SocketConnection {
|
||||
);
|
||||
targetSocketRequest.handleResponse(dataArg);
|
||||
});
|
||||
|
||||
this.socket.on('updateTagStore', async (tagStoreArg: interfaces.TTagStore) => {
|
||||
if (!plugins.smartjson.deepEqualObjects(this.tagStore, tagStoreArg)) {
|
||||
this.tagStore = tagStoreArg;
|
||||
this.socket.emit('updateTagStore', this.tagStore);
|
||||
this.tagStoreObservable.next(this.tagStore);
|
||||
}
|
||||
this.remoteTagStoreObservable.next(tagStoreArg);
|
||||
});
|
||||
|
||||
logger.log('info', `now listening to function requests for ${this.alias}`);
|
||||
done.resolve(this);
|
||||
} else {
|
||||
|
@ -58,7 +58,7 @@ export class SocketFunction<T extends plugins.typedrequestInterfaces.ITypedReque
|
||||
smartsocketRefArg: Smartsocket | SmartsocketClient,
|
||||
functionNameArg: string
|
||||
): SocketFunction<Q> {
|
||||
return smartsocketRefArg.socketFunctions.find((socketFunctionArg) => {
|
||||
return smartsocketRefArg.socketFunctions.findSync((socketFunctionArg) => {
|
||||
return socketFunctionArg.name === functionNameArg;
|
||||
});
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ export class SocketRequest<T extends plugins.typedrequestInterfaces.ITypedReques
|
||||
smartsocketRef: Smartsocket | SmartsocketClient,
|
||||
shortIdArg: string
|
||||
): SocketRequest<any> {
|
||||
return smartsocketRef.socketRequests.find((socketRequestArg) => {
|
||||
return smartsocketRef.socketRequests.findSync((socketRequestArg) => {
|
||||
return socketRequestArg.shortid === shortIdArg;
|
||||
});
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export class SocketRole {
|
||||
referenceSmartsocket: Smartsocket | SmartsocketClient,
|
||||
socketRoleNameArg: string
|
||||
): SocketRole {
|
||||
return referenceSmartsocket.socketRoles.find((socketRoleArg) => {
|
||||
return referenceSmartsocket.socketRoles.findSync((socketRoleArg) => {
|
||||
return socketRoleArg.name === socketRoleNameArg;
|
||||
});
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import * as isohash from '@pushrocks/isohash';
|
||||
import * as isounique from '@pushrocks/isounique';
|
||||
import * as lik from '@pushrocks/lik';
|
||||
import * as smartenv from '@pushrocks/smartenv';
|
||||
import * as smartjson from '@pushrocks/smartjson';
|
||||
import * as smartlog from '@pushrocks/smartlog';
|
||||
import * as smartdelay from '@pushrocks/smartdelay';
|
||||
import * as smartpromise from '@pushrocks/smartpromise';
|
||||
@ -19,6 +20,7 @@ export {
|
||||
isounique,
|
||||
lik,
|
||||
smartenv,
|
||||
smartjson,
|
||||
smartlog,
|
||||
smartdelay,
|
||||
smartpromise,
|
||||
|
@ -13,4 +13,4 @@ export { smartexpress };
|
||||
import type socketIo from 'socket.io';
|
||||
import type socketIoClient from 'socket.io-client';
|
||||
|
||||
export { socketIo, socketIoClient };
|
||||
export { socketIoClient, socketIo };
|
||||
|
Reference in New Issue
Block a user