ing
This commit is contained in:
parent
abba37caae
commit
b7ec96bbe3
37
package-lock.json
generated
37
package-lock.json
generated
|
@ -478,6 +478,12 @@
|
||||||
"integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==",
|
"integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/pako": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://nexus.loafle.net/repository/npm-all/@types/pako/-/pako-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-6q6DZNG391LiY7w/1o3+yY5hNsU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/q": {
|
"@types/q": {
|
||||||
"version": "0.0.32",
|
"version": "0.0.32",
|
||||||
"resolved": "https://nexus.loafle.net/repository/npm-all/@types/q/-/q-0.0.32.tgz",
|
"resolved": "https://nexus.loafle.net/repository/npm-all/@types/q/-/q-0.0.32.tgz",
|
||||||
|
@ -4060,12 +4066,14 @@
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
|
@ -4080,17 +4088,20 @@
|
||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -4207,7 +4218,8 @@
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
|
@ -4219,6 +4231,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
@ -4233,6 +4246,7 @@
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
@ -4240,12 +4254,14 @@
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.1",
|
"safe-buffer": "^5.1.1",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
|
@ -4264,6 +4280,7 @@
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
|
@ -4344,7 +4361,8 @@
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
|
@ -4356,6 +4374,7 @@
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
|
@ -4477,6 +4496,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
@ -7899,8 +7919,7 @@
|
||||||
"pako": {
|
"pako": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://nexus.loafle.net/repository/npm-all/pako/-/pako-1.0.6.tgz",
|
"resolved": "https://nexus.loafle.net/repository/npm-all/pako/-/pako-1.0.6.tgz",
|
||||||
"integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==",
|
"integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"parallel-transform": {
|
"parallel-transform": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"@loafer/decorator": "^0.0.1",
|
"@loafer/decorator": "^0.0.1",
|
||||||
"@ngrx/store": "^5.2.0",
|
"@ngrx/store": "^5.2.0",
|
||||||
"core-js": "^2.5.4",
|
"core-js": "^2.5.4",
|
||||||
|
"pako": "^1.0.6",
|
||||||
"rxjs": "^6.0.0",
|
"rxjs": "^6.0.0",
|
||||||
"zone.js": "^0.8.26"
|
"zone.js": "^0.8.26"
|
||||||
},
|
},
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
"@types/jasmine": "~2.8.6",
|
"@types/jasmine": "~2.8.6",
|
||||||
"@types/jasminewd2": "~2.0.3",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
"@types/node": "~8.9.4",
|
"@types/node": "~8.9.4",
|
||||||
|
"@types/pako": "^1.0.0",
|
||||||
"codelyzer": "~4.2.1",
|
"codelyzer": "~4.2.1",
|
||||||
"jasmine-core": "~2.99.1",
|
"jasmine-core": "~2.99.1",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
RPCClientResponseCodec,
|
RPCClientResponseCodec,
|
||||||
RPCClientNotificationCodec,
|
RPCClientNotificationCodec,
|
||||||
} from '../protocol/RPCClientCodec';
|
} from '../protocol/RPCClientCodec';
|
||||||
|
import { RPCMessage } from '../core/type';
|
||||||
|
|
||||||
export interface RPCRequestState {
|
export interface RPCRequestState {
|
||||||
subject: Subject<any>;
|
subject: Subject<any>;
|
||||||
|
@ -17,32 +18,37 @@ export interface RPCRequestState {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class RPCClient<T> {
|
export abstract class RPCClient {
|
||||||
private _requestID: number;
|
private requestID: number;
|
||||||
|
|
||||||
private _pendingRequestsCount: number;
|
private pendingRequestsCount: number;
|
||||||
private _pendingRequests: Map<number, RPCRequestState>;
|
private pendingRequests: Map<number, RPCRequestState>;
|
||||||
|
|
||||||
|
protected rpcClientCodec: RPCClientCodec;
|
||||||
|
protected rpcClientRWC: RPCClientRWC;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private _codec: RPCClientCodec,
|
rpcClientCodec: RPCClientCodec,
|
||||||
private _rwc: RPCClientRWC<T>,
|
rpcClientRWC: RPCClientRWC,
|
||||||
) {
|
) {
|
||||||
this._requestID = 0;
|
this.rpcClientCodec = rpcClientCodec;
|
||||||
this._pendingRequestsCount = 0;
|
this.rpcClientRWC = rpcClientRWC;
|
||||||
this._pendingRequests = new Map();
|
this.requestID = 0;
|
||||||
|
this.pendingRequestsCount = 0;
|
||||||
|
this.pendingRequests = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRequestID(): number {
|
private getRequestID(): number {
|
||||||
return ++this._requestID;
|
return ++this.requestID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* connect
|
* connect
|
||||||
*/
|
*/
|
||||||
public connect(queryString?: string): void {
|
public connect(queryString?: string): void {
|
||||||
this._rwc.connect(queryString);
|
this.rpcClientRWC.connect(queryString);
|
||||||
this._rwc.readResponse().subscribe(
|
this.rpcClientRWC.readResponse().subscribe(
|
||||||
(value: T) => {
|
(value: RPCMessage) => {
|
||||||
this.onMessage(value);
|
this.onMessage(value);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
|
@ -58,7 +64,7 @@ export abstract class RPCClient<T> {
|
||||||
* close
|
* close
|
||||||
*/
|
*/
|
||||||
public disconnect() {
|
public disconnect() {
|
||||||
this._rwc.disconnect();
|
this.rpcClientRWC.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,12 +102,11 @@ export abstract class RPCClient<T> {
|
||||||
params: args,
|
params: args,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this._pendingRequests.set(id, reqState);
|
this.pendingRequests.set(id, reqState);
|
||||||
this._pendingRequestsCount++;
|
this.pendingRequestsCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const req = this._codec.request(method, args, id);
|
this.rpcClientRWC.writeRequest(this.rpcClientCodec.request(method, args, id));
|
||||||
this._rwc.writeRequest(req);
|
|
||||||
|
|
||||||
if (undefined !== resSubject) {
|
if (undefined !== resSubject) {
|
||||||
return resSubject.asObservable();
|
return resSubject.asObservable();
|
||||||
|
@ -109,8 +114,8 @@ export abstract class RPCClient<T> {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMessage(message: Object): void {
|
private onMessage(message: RPCMessage): void {
|
||||||
const resCodec = this._codec.response(message);
|
const resCodec = this.rpcClientCodec.response(message);
|
||||||
|
|
||||||
if (resCodec.isNotification()) {
|
if (resCodec.isNotification()) {
|
||||||
this.onNotification(resCodec.notification());
|
this.onNotification(resCodec.notification());
|
||||||
|
@ -124,10 +129,10 @@ export abstract class RPCClient<T> {
|
||||||
const result = resCodec.result();
|
const result = resCodec.result();
|
||||||
const error = resCodec.error();
|
const error = resCodec.error();
|
||||||
|
|
||||||
const reqState: RPCRequestState = this._pendingRequests.get(id);
|
const reqState: RPCRequestState = this.pendingRequests.get(id);
|
||||||
|
|
||||||
this._pendingRequests.delete(id);
|
this.pendingRequests.delete(id);
|
||||||
this._pendingRequestsCount--;
|
this.pendingRequestsCount--;
|
||||||
|
|
||||||
if (undefined !== error) {
|
if (undefined !== error) {
|
||||||
const rpcClientError: RPCClientError = {
|
const rpcClientError: RPCClientError = {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { RPCMessage } from '../core/type';
|
||||||
|
|
||||||
export interface RPCClientRWC<T> {
|
|
||||||
|
export interface RPCClientRWC {
|
||||||
connect(queryString?: string): void;
|
connect(queryString?: string): void;
|
||||||
readResponse(): Observable<T>;
|
readResponse(): Observable<RPCMessage>;
|
||||||
writeRequest(data: any): void;
|
writeRequest(data: RPCMessage): void;
|
||||||
disconnect(): void;
|
disconnect(): void;
|
||||||
connectionStatus(): Observable<boolean>;
|
connectionStatus(): Observable<boolean>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,17 @@ import {
|
||||||
} from './RxWebsocketSubject';
|
} from './RxWebsocketSubject';
|
||||||
|
|
||||||
import { RPCClientRWC } from '../../RPCClientRWC';
|
import { RPCClientRWC } from '../../RPCClientRWC';
|
||||||
|
import { RPCMessage } from '../../../core/type';
|
||||||
|
|
||||||
export class RPCClientWebsocketRWC<T> implements RPCClientRWC<T> {
|
|
||||||
private _wsSocketSubject: RxWebsocketSubject<T>;
|
export class RPCClientWebsocketRWC implements RPCClientRWC {
|
||||||
private _responseSubject: Subject<T>;
|
private _wsSocketSubject: RxWebsocketSubject;
|
||||||
|
private _responseSubject: Subject<RPCMessage>;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private _config: RxWebsocketSubjectConfig,
|
private _config: RxWebsocketSubjectConfig,
|
||||||
) {
|
) {
|
||||||
this._wsSocketSubject = new RxWebsocketSubject<T>(this._config);
|
this._wsSocketSubject = new RxWebsocketSubject(this._config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public connect(queryString?: string): void {
|
public connect(queryString?: string): void {
|
||||||
|
@ -23,7 +25,7 @@ export class RPCClientWebsocketRWC<T> implements RPCClientRWC<T> {
|
||||||
}
|
}
|
||||||
this._wsSocketSubject.connect();
|
this._wsSocketSubject.connect();
|
||||||
this._wsSocketSubject.subscribe(
|
this._wsSocketSubject.subscribe(
|
||||||
(value: T) => {
|
(value: RPCMessage) => {
|
||||||
if (undefined !== this._responseSubject) {
|
if (undefined !== this._responseSubject) {
|
||||||
this._responseSubject.next(value);
|
this._responseSubject.next(value);
|
||||||
}
|
}
|
||||||
|
@ -47,14 +49,14 @@ export class RPCClientWebsocketRWC<T> implements RPCClientRWC<T> {
|
||||||
return this._wsSocketSubject.connectionStatus;
|
return this._wsSocketSubject.connectionStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readResponse(): Observable<T> {
|
public readResponse(): Observable<RPCMessage> {
|
||||||
if (undefined === this._responseSubject) {
|
if (undefined === this._responseSubject) {
|
||||||
this._responseSubject = new Subject<T>();
|
this._responseSubject = new Subject<RPCMessage>();
|
||||||
}
|
}
|
||||||
return this._responseSubject.asObservable();
|
return this._responseSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeRequest(data: any): void {
|
public writeRequest(data: RPCMessage): void {
|
||||||
this._wsSocketSubject.write(data);
|
this._wsSocketSubject.write(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,38 @@
|
||||||
import { Observable, Observer, Subject, interval } from 'rxjs';
|
import { Observable, Observer, Subject, interval } from 'rxjs';
|
||||||
import { share, distinctUntilChanged, takeWhile } from 'rxjs/operators';
|
import { share, distinctUntilChanged, takeWhile } from 'rxjs/operators';
|
||||||
import { WebSocketSubject, WebSocketSubjectConfig } from 'rxjs/webSocket';
|
import { WebSocketSubject, WebSocketSubjectConfig } from 'rxjs/webSocket';
|
||||||
|
import { RPCMessage } from '../../../core/type';
|
||||||
|
|
||||||
export interface RxWebsocketSubjectConfig {
|
export interface RxWebsocketSubjectConfig<T = RPCMessage> extends WebSocketSubjectConfig<T> {
|
||||||
url: string;
|
|
||||||
protocol?: string | Array<string>;
|
|
||||||
reconnectInterval?: 5000;
|
reconnectInterval?: 5000;
|
||||||
reconnectRetry?: 10;
|
reconnectRetry?: 10;
|
||||||
|
compressionThreshold?: 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RxWebsocketSubject<T> extends Subject<T> {
|
const DEFAULT_RX_WEBSOCKET_CONFIG: RxWebsocketSubjectConfig<any> = {
|
||||||
|
url: '',
|
||||||
|
deserializer: (e: MessageEvent) => e,
|
||||||
|
serializer: (value: any) => value,
|
||||||
|
openObserver: {
|
||||||
|
next: (e: Event) => {
|
||||||
|
this._connectionObserver.next(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closeObserver: {
|
||||||
|
next: (e: CloseEvent) => {
|
||||||
|
this._socket = null;
|
||||||
|
this._connectionObserver.next(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reconnectInterval: 5000,
|
||||||
|
reconnectRetry: 10,
|
||||||
|
compressionThreshold: 1024,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class RxWebsocketSubject extends Subject<RPCMessage> {
|
||||||
private _reconnectionObservable: Observable<number>;
|
private _reconnectionObservable: Observable<number>;
|
||||||
private _wsSubjectConfig: WebSocketSubjectConfig<T>;
|
private _wsSubjectConfig: RxWebsocketSubjectConfig;
|
||||||
private _socket: WebSocketSubject<any>;
|
private _socket: WebSocketSubject<RPCMessage>;
|
||||||
private _connectionObserver: Observer<boolean>;
|
private _connectionObserver: Observer<boolean>;
|
||||||
private _connectionStatus: Observable<boolean>;
|
private _connectionStatus: Observable<boolean>;
|
||||||
private _queryString: string;
|
private _queryString: string;
|
||||||
|
@ -27,23 +47,38 @@ export class RxWebsocketSubject<T> extends Subject<T> {
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
);
|
);
|
||||||
|
|
||||||
this._wsSubjectConfig = {
|
this._wsSubjectConfig = this._config = { ...DEFAULT_RX_WEBSOCKET_CONFIG };
|
||||||
url: _config.url,
|
for (const key in _config) {
|
||||||
protocol: _config.protocol,
|
if (_config.hasOwnProperty(key)) {
|
||||||
serializer: (value: any) => value,
|
switch (key) {
|
||||||
closeObserver: {
|
case 'openObserver':
|
||||||
|
const oo = _config[key];
|
||||||
|
this._wsSubjectConfig[key] = {
|
||||||
|
next: (e: Event) => {
|
||||||
|
this._connectionObserver.next(true);
|
||||||
|
oo.next(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'closeObserver':
|
||||||
|
const co = _config[key];
|
||||||
|
this._wsSubjectConfig[key] = {
|
||||||
next: (e: CloseEvent) => {
|
next: (e: CloseEvent) => {
|
||||||
this._socket = null;
|
this._socket = null;
|
||||||
this._connectionObserver.next(false);
|
this._connectionObserver.next(false);
|
||||||
|
co.next(e);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
openObserver: {
|
|
||||||
next: (e: Event) => {
|
|
||||||
this._connectionObserver.next(true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this._wsSubjectConfig[key] = _config[key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._connectionStatus.subscribe((isConnected: boolean) => {
|
this._connectionStatus.subscribe((isConnected: boolean) => {
|
||||||
if (!this._reconnectionObservable && typeof(isConnected) === 'boolean' && !isConnected) {
|
if (!this._reconnectionObservable && typeof(isConnected) === 'boolean' && !isConnected) {
|
||||||
this.reconnect();
|
this.reconnect();
|
||||||
|
@ -69,12 +104,12 @@ export class RxWebsocketSubject<T> extends Subject<T> {
|
||||||
wsSubjectConfig.url = wsSubjectConfig.url + '?' + this._queryString;
|
wsSubjectConfig.url = wsSubjectConfig.url + '?' + this._queryString;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._socket = new WebSocketSubject(wsSubjectConfig);
|
this._socket = new WebSocketSubject<RPCMessage>(wsSubjectConfig);
|
||||||
this._socket.subscribe(
|
this._socket.subscribe(
|
||||||
(m) => {
|
(m: RPCMessage) => {
|
||||||
this.next(m);
|
this.next(m);
|
||||||
},
|
},
|
||||||
(error: Event) => {
|
(e: Event) => {
|
||||||
if (!this._socket) {
|
if (!this._socket) {
|
||||||
this.reconnect();
|
this.reconnect();
|
||||||
}
|
}
|
||||||
|
@ -107,7 +142,7 @@ export class RxWebsocketSubject<T> extends Subject<T> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public write(data: any): void {
|
public write(data: RPCMessage): void {
|
||||||
this._socket.next(data);
|
this._socket.next(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
projects/loafer/ng-rpc/src/lib/codec/codec.ts
Normal file
33
projects/loafer/ng-rpc/src/lib/codec/codec.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { RPCMessage } from '../core/type';
|
||||||
|
|
||||||
|
export interface Codec {
|
||||||
|
encode(message: string): RPCMessage;
|
||||||
|
decode(message: RPCMessage): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DefaultCodec implements Codec {
|
||||||
|
public encode(message: string): RPCMessage {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
public decode(message: RPCMessage): string {
|
||||||
|
return <string>message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultCodec = new DefaultCodec();
|
||||||
|
|
||||||
|
export interface CodecSelector {
|
||||||
|
encode(message: string): RPCMessage;
|
||||||
|
decode(message: RPCMessage): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DefaultCodecSelector implements CodecSelector {
|
||||||
|
public encode(message: string): RPCMessage {
|
||||||
|
return defaultCodec.encode(message);
|
||||||
|
}
|
||||||
|
public decode(message: RPCMessage): string {
|
||||||
|
return defaultCodec.decode(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultCodecSelector = new DefaultCodecSelector();
|
38
projects/loafer/ng-rpc/src/lib/codec/compression_codec.ts
Normal file
38
projects/loafer/ng-rpc/src/lib/codec/compression_codec.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { Codec, CodecSelector, defaultCodec } from './codec';
|
||||||
|
import { RPCMessage } from '../core/type';
|
||||||
|
import { gzip, ungzip } from 'pako';
|
||||||
|
|
||||||
|
export class GZipCodec implements Codec {
|
||||||
|
public encode(message: string): RPCMessage {
|
||||||
|
return <ArrayBuffer>gzip(message).buffer;
|
||||||
|
}
|
||||||
|
public decode(message: RPCMessage): string {
|
||||||
|
return ungzip(Buffer.from(<ArrayBuffer>message), {to: 'string'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const gZipCodec = new GZipCodec();
|
||||||
|
|
||||||
|
export class CompressionCodecSelector implements CodecSelector {
|
||||||
|
private readonly threshold: number;
|
||||||
|
|
||||||
|
public constructor(threshold: number) {
|
||||||
|
this.threshold = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public encode(message: string): RPCMessage {
|
||||||
|
if (this.threshold < Buffer.byteLength(message)) {
|
||||||
|
return gZipCodec.encode(message);
|
||||||
|
} else {
|
||||||
|
return defaultCodec.encode(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public decode(message: RPCMessage): string {
|
||||||
|
if (message instanceof ArrayBuffer) {
|
||||||
|
return gZipCodec.decode(message);
|
||||||
|
} else {
|
||||||
|
return defaultCodec.decode(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
projects/loafer/ng-rpc/src/lib/core/type.ts
Normal file
1
projects/loafer/ng-rpc/src/lib/core/type.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export type RPCMessage = string | ArrayBuffer;
|
|
@ -1,8 +1,9 @@
|
||||||
import { RPCError } from './RPCError';
|
import { RPCError } from './RPCError';
|
||||||
|
import { RPCMessage } from '../core/type';
|
||||||
|
|
||||||
export interface RPCClientCodec {
|
export interface RPCClientCodec {
|
||||||
request(method: string, args: any[], id: number): any;
|
request(method: string, args: any[], id: number): RPCMessage;
|
||||||
response(res: any): RPCClientResponseCodec;
|
response(message: RPCMessage): RPCClientResponseCodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RPCClientResponseCodec {
|
export interface RPCClientResponseCodec {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import {
|
||||||
import {
|
import {
|
||||||
RPCError,
|
RPCError,
|
||||||
} from '../RPCError';
|
} from '../RPCError';
|
||||||
|
import { RPCMessage } from '../../core/type';
|
||||||
|
import { CodecSelector, defaultCodecSelector } from '../../codec/codec';
|
||||||
|
|
||||||
export interface ClientNotification {
|
export interface ClientNotification {
|
||||||
method: string;
|
method: string;
|
||||||
|
@ -28,7 +30,13 @@ export interface ClientResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class JSONRPCClientCodec implements RPCClientCodec {
|
export class JSONRPCClientCodec implements RPCClientCodec {
|
||||||
public request(method: string, args: any[], id?: number): any {
|
private readonly codecSelector: CodecSelector;
|
||||||
|
|
||||||
|
public constructor(codecSelector: CodecSelector = defaultCodecSelector) {
|
||||||
|
this.codecSelector = codecSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public request(method: string, args: any[], id?: number): RPCMessage {
|
||||||
const params = convertParamsToStringArray(args);
|
const params = convertParamsToStringArray(args);
|
||||||
|
|
||||||
const req: ClientRequest = {
|
const req: ClientRequest = {
|
||||||
|
@ -37,16 +45,12 @@ export class JSONRPCClientCodec implements RPCClientCodec {
|
||||||
params: 0 === params.length ? null : params,
|
params: 0 === params.length ? null : params,
|
||||||
id: id,
|
id: id,
|
||||||
};
|
};
|
||||||
return JSON.stringify(req);
|
|
||||||
|
return this.codecSelector.encode(JSON.stringify(req));
|
||||||
}
|
}
|
||||||
public response(res: any): RPCClientResponseCodec {
|
|
||||||
const _res: ClientResponse = {
|
public response(message: RPCMessage): RPCClientResponseCodec {
|
||||||
id: res.id,
|
return new JSONRPCClientResponseCodec(this.codecSelector.decode(message));
|
||||||
jsonrpc: res.jsonrpc,
|
|
||||||
result: res.result,
|
|
||||||
error: res.error,
|
|
||||||
};
|
|
||||||
return new JSONRPCClientResponseCodec(_res);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,17 +84,20 @@ function convertParamsToStringArray(args: any[]): string[] | undefined {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class JSONRPCClientResponseCodec implements RPCClientResponseCodec {
|
export class JSONRPCClientResponseCodec implements RPCClientResponseCodec {
|
||||||
public constructor(private _res: ClientResponse) {
|
private res: ClientResponse;
|
||||||
|
|
||||||
|
public constructor(json: string) {
|
||||||
|
this.res = JSON.parse(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
public id(): number | undefined {
|
public id(): number | undefined {
|
||||||
return this._res.id;
|
return this.res.id;
|
||||||
}
|
}
|
||||||
public error(): RPCError | undefined {
|
public error(): RPCError | undefined {
|
||||||
return this._res.error;
|
return this.res.error;
|
||||||
}
|
}
|
||||||
public result(): any | undefined {
|
public result(): any | undefined {
|
||||||
return this._res.result;
|
return this.res.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isNotification(): boolean {
|
public isNotification(): boolean {
|
||||||
|
@ -104,22 +111,22 @@ export class JSONRPCClientResponseCodec implements RPCClientResponseCodec {
|
||||||
if (undefined !== this.id() || undefined === this.result()) {
|
if (undefined !== this.id() || undefined === this.result()) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const _noti: ClientNotification = {
|
const noti: ClientNotification = {
|
||||||
method: this._res.result.method,
|
method: this.res.result.method,
|
||||||
params: this._res.result.params,
|
params: this.res.result.params,
|
||||||
};
|
};
|
||||||
return new JSONRPCClientNotificationCodec(_noti);
|
return new JSONRPCClientNotificationCodec(noti);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class JSONRPCClientNotificationCodec implements RPCClientNotificationCodec {
|
export class JSONRPCClientNotificationCodec implements RPCClientNotificationCodec {
|
||||||
public constructor(private _noti: ClientNotification) {
|
public constructor(private noti: ClientNotification) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public method(): string {
|
public method(): string {
|
||||||
return this._noti.method;
|
return this.noti.method;
|
||||||
}
|
}
|
||||||
public params(): any[] | undefined {
|
public params(): any[] | undefined {
|
||||||
return this._noti.params;
|
return this.noti.params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user