@celljs/rpc 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/cell.yml +8 -0
- package/lib/common/annotation/autorpc.d.ts +6 -0
- package/lib/common/annotation/autorpc.d.ts.map +1 -0
- package/lib/common/annotation/autorpc.js +19 -0
- package/lib/common/annotation/autorpc.js.map +1 -0
- package/lib/common/annotation/detached/autorpc.d.ts +3 -0
- package/lib/common/annotation/detached/autorpc.d.ts.map +1 -0
- package/lib/common/annotation/detached/autorpc.js +15 -0
- package/lib/common/annotation/detached/autorpc.js.map +1 -0
- package/lib/common/annotation/detached/index.d.ts +2 -0
- package/lib/common/annotation/detached/index.d.ts.map +1 -0
- package/lib/common/annotation/detached/index.js +18 -0
- package/lib/common/annotation/detached/index.js.map +1 -0
- package/lib/common/annotation/index.d.ts +3 -0
- package/lib/common/annotation/index.d.ts.map +1 -0
- package/lib/common/annotation/index.js +19 -0
- package/lib/common/annotation/index.js.map +1 -0
- package/lib/common/annotation/rpc.d.ts +20 -0
- package/lib/common/annotation/rpc.d.ts.map +1 -0
- package/lib/common/annotation/rpc.js +41 -0
- package/lib/common/annotation/rpc.js.map +1 -0
- package/lib/common/channal/channel-protocol.d.ts +52 -0
- package/lib/common/channal/channel-protocol.d.ts.map +1 -0
- package/lib/common/channal/channel-protocol.js +38 -0
- package/lib/common/channal/channel-protocol.js.map +1 -0
- package/lib/common/channal/http-channel.d.ts +9 -0
- package/lib/common/channal/http-channel.d.ts.map +1 -0
- package/lib/common/channal/http-channel.js +26 -0
- package/lib/common/channal/http-channel.js.map +1 -0
- package/lib/common/channal/index.d.ts +3 -0
- package/lib/common/channal/index.d.ts.map +1 -0
- package/lib/common/channal/index.js +19 -0
- package/lib/common/channal/index.js.map +1 -0
- package/lib/common/constants.d.ts +2 -0
- package/lib/common/constants.d.ts.map +1 -0
- package/lib/common/constants.js +5 -0
- package/lib/common/constants.js.map +1 -0
- package/lib/common/converter/converter-protocol.d.ts +7 -0
- package/lib/common/converter/converter-protocol.d.ts.map +1 -0
- package/lib/common/converter/converter-protocol.js +6 -0
- package/lib/common/converter/converter-protocol.js.map +1 -0
- package/lib/common/converter/index.d.ts +2 -0
- package/lib/common/converter/index.d.ts.map +1 -0
- package/lib/common/converter/index.js +18 -0
- package/lib/common/converter/index.js.map +1 -0
- package/lib/common/endpoint/endpoint-protocol.d.ts +5 -0
- package/lib/common/endpoint/endpoint-protocol.d.ts.map +1 -0
- package/lib/common/endpoint/endpoint-protocol.js +5 -0
- package/lib/common/endpoint/endpoint-protocol.js.map +1 -0
- package/lib/common/endpoint/endpoint-resolver.d.ts +13 -0
- package/lib/common/endpoint/endpoint-resolver.d.ts.map +1 -0
- package/lib/common/endpoint/endpoint-resolver.js +61 -0
- package/lib/common/endpoint/endpoint-resolver.js.map +1 -0
- package/lib/common/endpoint/index.d.ts +3 -0
- package/lib/common/endpoint/index.d.ts.map +1 -0
- package/lib/common/endpoint/index.js +19 -0
- package/lib/common/endpoint/index.js.map +1 -0
- package/lib/common/error/error.d.ts +6 -0
- package/lib/common/error/error.d.ts.map +1 -0
- package/lib/common/error/error.js +12 -0
- package/lib/common/error/error.js.map +1 -0
- package/lib/common/error/index.d.ts +2 -0
- package/lib/common/error/index.d.ts.map +1 -0
- package/lib/common/error/index.js +18 -0
- package/lib/common/error/index.js.map +1 -0
- package/lib/common/factory/connection-factory.d.ts +10 -0
- package/lib/common/factory/connection-factory.d.ts.map +1 -0
- package/lib/common/factory/connection-factory.js +28 -0
- package/lib/common/factory/connection-factory.js.map +1 -0
- package/lib/common/factory/index.d.ts +5 -0
- package/lib/common/factory/index.d.ts.map +1 -0
- package/lib/common/factory/index.js +21 -0
- package/lib/common/factory/index.js.map +1 -0
- package/lib/common/factory/proxy-factory.d.ts +57 -0
- package/lib/common/factory/proxy-factory.d.ts.map +1 -0
- package/lib/common/factory/proxy-factory.js +182 -0
- package/lib/common/factory/proxy-factory.js.map +1 -0
- package/lib/common/factory/reader.d.ts +16 -0
- package/lib/common/factory/reader.d.ts.map +1 -0
- package/lib/common/factory/reader.js +48 -0
- package/lib/common/factory/reader.js.map +1 -0
- package/lib/common/factory/writer.d.ts +10 -0
- package/lib/common/factory/writer.d.ts.map +1 -0
- package/lib/common/factory/writer.js +25 -0
- package/lib/common/factory/writer.js.map +1 -0
- package/lib/common/handler/connection-handler.d.ts +7 -0
- package/lib/common/handler/connection-handler.d.ts.map +1 -0
- package/lib/common/handler/connection-handler.js +8 -0
- package/lib/common/handler/connection-handler.js.map +1 -0
- package/lib/common/handler/handler-protocol.d.ts +7 -0
- package/lib/common/handler/handler-protocol.d.ts.map +1 -0
- package/lib/common/handler/handler-protocol.js +5 -0
- package/lib/common/handler/handler-protocol.js.map +1 -0
- package/lib/common/handler/index.d.ts +3 -0
- package/lib/common/handler/index.d.ts.map +1 -0
- package/lib/common/handler/index.js +19 -0
- package/lib/common/handler/index.js.map +1 -0
- package/lib/common/index.d.ts +12 -0
- package/lib/common/index.d.ts.map +1 -0
- package/lib/common/index.js +28 -0
- package/lib/common/index.js.map +1 -0
- package/lib/common/module.d.ts +4 -0
- package/lib/common/module.d.ts.map +1 -0
- package/lib/common/module.js +22 -0
- package/lib/common/module.js.map +1 -0
- package/lib/common/processor/client-config-processor.d.ts +6 -0
- package/lib/common/processor/client-config-processor.d.ts.map +1 -0
- package/lib/common/processor/client-config-processor.js +20 -0
- package/lib/common/processor/client-config-processor.js.map +1 -0
- package/lib/common/processor/index.d.ts +3 -0
- package/lib/common/processor/index.d.ts.map +1 -0
- package/lib/common/processor/index.js +19 -0
- package/lib/common/processor/index.js.map +1 -0
- package/lib/common/processor/processor-protocol.d.ts +6 -0
- package/lib/common/processor/processor-protocol.d.ts.map +1 -0
- package/lib/common/processor/processor-protocol.js +5 -0
- package/lib/common/processor/processor-protocol.js.map +1 -0
- package/lib/common/proxy/delegating-http-proxy-creator.d.ts +24 -0
- package/lib/common/proxy/delegating-http-proxy-creator.d.ts.map +1 -0
- package/lib/common/proxy/delegating-http-proxy-creator.js +89 -0
- package/lib/common/proxy/delegating-http-proxy-creator.js.map +1 -0
- package/lib/common/proxy/http-proxy-creator.d.ts +43 -0
- package/lib/common/proxy/http-proxy-creator.d.ts.map +1 -0
- package/lib/common/proxy/http-proxy-creator.js +137 -0
- package/lib/common/proxy/http-proxy-creator.js.map +1 -0
- package/lib/common/proxy/index.d.ts +5 -0
- package/lib/common/proxy/index.d.ts.map +1 -0
- package/lib/common/proxy/index.js +21 -0
- package/lib/common/proxy/index.js.map +1 -0
- package/lib/common/proxy/proxy-protocol.d.ts +22 -0
- package/lib/common/proxy/proxy-protocol.d.ts.map +1 -0
- package/lib/common/proxy/proxy-protocol.js +6 -0
- package/lib/common/proxy/proxy-protocol.js.map +1 -0
- package/lib/common/proxy/proxy-provider.d.ts +10 -0
- package/lib/common/proxy/proxy-provider.d.ts.map +1 -0
- package/lib/common/proxy/proxy-provider.js +43 -0
- package/lib/common/proxy/proxy-provider.js.map +1 -0
- package/lib/common/utils/converter-util.d.ts +8 -0
- package/lib/common/utils/converter-util.d.ts.map +1 -0
- package/lib/common/utils/converter-util.js +32 -0
- package/lib/common/utils/converter-util.js.map +1 -0
- package/lib/common/utils/index.d.ts +4 -0
- package/lib/common/utils/index.d.ts.map +1 -0
- package/lib/common/utils/index.js +20 -0
- package/lib/common/utils/index.js.map +1 -0
- package/lib/common/utils/logger.d.ts +9 -0
- package/lib/common/utils/logger.d.ts.map +1 -0
- package/lib/common/utils/logger.js +22 -0
- package/lib/common/utils/logger.js.map +1 -0
- package/lib/common/utils/rpc-util.d.ts +13 -0
- package/lib/common/utils/rpc-util.d.ts.map +1 -0
- package/lib/common/utils/rpc-util.js +46 -0
- package/lib/common/utils/rpc-util.js.map +1 -0
- package/lib/node/channel/channel-manager.d.ts +10 -0
- package/lib/node/channel/channel-manager.d.ts.map +1 -0
- package/lib/node/channel/channel-manager.js +59 -0
- package/lib/node/channel/channel-manager.js.map +1 -0
- package/lib/node/channel/channel-middleware.d.ts +8 -0
- package/lib/node/channel/channel-middleware.d.ts.map +1 -0
- package/lib/node/channel/channel-middleware.js +40 -0
- package/lib/node/channel/channel-middleware.js.map +1 -0
- package/lib/node/channel/channel-protocol.d.ts +13 -0
- package/lib/node/channel/channel-protocol.d.ts.map +1 -0
- package/lib/node/channel/channel-protocol.js +9 -0
- package/lib/node/channel/channel-protocol.js.map +1 -0
- package/lib/node/channel/channel-strategy.d.ts +10 -0
- package/lib/node/channel/channel-strategy.d.ts.map +1 -0
- package/lib/node/channel/channel-strategy.js +64 -0
- package/lib/node/channel/channel-strategy.js.map +1 -0
- package/lib/node/channel/index.d.ts +5 -0
- package/lib/node/channel/index.d.ts.map +1 -0
- package/lib/node/channel/index.js +21 -0
- package/lib/node/channel/index.js.map +1 -0
- package/lib/node/handler/handler-adapter.d.ts +14 -0
- package/lib/node/handler/handler-adapter.d.ts.map +1 -0
- package/lib/node/handler/handler-adapter.js +55 -0
- package/lib/node/handler/handler-adapter.js.map +1 -0
- package/lib/node/handler/handler-protocol.d.ts +2 -0
- package/lib/node/handler/handler-protocol.d.ts.map +1 -0
- package/lib/node/handler/handler-protocol.js +5 -0
- package/lib/node/handler/handler-protocol.js.map +1 -0
- package/lib/node/handler/index.d.ts +3 -0
- package/lib/node/handler/index.d.ts.map +1 -0
- package/lib/node/handler/index.js +19 -0
- package/lib/node/handler/index.js.map +1 -0
- package/lib/node/index.d.ts +3 -0
- package/lib/node/index.d.ts.map +1 -0
- package/lib/node/index.js +19 -0
- package/lib/node/index.js.map +1 -0
- package/lib/node/module.d.ts +4 -0
- package/lib/node/module.d.ts.map +1 -0
- package/lib/node/module.js +6 -0
- package/lib/node/module.js.map +1 -0
- package/lib/package.spec.d.ts +1 -0
- package/lib/package.spec.d.ts.map +1 -0
- package/lib/package.spec.js +4 -0
- package/lib/package.spec.js.map +1 -0
- package/package.json +45 -0
- package/src/common/annotation/autorpc.ts +17 -0
- package/src/common/annotation/detached/autorpc.ts +12 -0
- package/src/common/annotation/detached/index.ts +1 -0
- package/src/common/annotation/index.ts +2 -0
- package/src/common/annotation/rpc.ts +41 -0
- package/src/common/channal/channel-protocol.ts +82 -0
- package/src/common/channal/http-channel.ts +30 -0
- package/src/common/channal/index.ts +2 -0
- package/src/common/constants.ts +1 -0
- package/src/common/converter/converter-protocol.ts +8 -0
- package/src/common/converter/index.ts +1 -0
- package/src/common/endpoint/endpoint-protocol.ts +5 -0
- package/src/common/endpoint/endpoint-resolver.ts +46 -0
- package/src/common/endpoint/index.ts +2 -0
- package/src/common/error/error.ts +7 -0
- package/src/common/error/index.ts +1 -0
- package/src/common/factory/connection-factory.ts +21 -0
- package/src/common/factory/index.ts +4 -0
- package/src/common/factory/proxy-factory.ts +219 -0
- package/src/common/factory/reader.ts +50 -0
- package/src/common/factory/writer.ts +26 -0
- package/src/common/handler/connection-handler.ts +7 -0
- package/src/common/handler/handler-protocol.ts +8 -0
- package/src/common/handler/index.ts +2 -0
- package/src/common/index.ts +11 -0
- package/src/common/module.ts +21 -0
- package/src/common/processor/client-config-processor.ts +12 -0
- package/src/common/processor/index.ts +2 -0
- package/src/common/processor/processor-protocol.ts +7 -0
- package/src/common/proxy/delegating-http-proxy-creator.ts +68 -0
- package/src/common/proxy/http-proxy-creator.ts +179 -0
- package/src/common/proxy/index.ts +5 -0
- package/src/common/proxy/proxy-protocol.ts +26 -0
- package/src/common/proxy/proxy-provider.ts +29 -0
- package/src/common/utils/converter-util.ts +29 -0
- package/src/common/utils/index.ts +3 -0
- package/src/common/utils/logger.ts +23 -0
- package/src/common/utils/rpc-util.ts +46 -0
- package/src/node/channel/channel-manager.ts +44 -0
- package/src/node/channel/channel-middleware.ts +27 -0
- package/src/node/channel/channel-protocol.ts +21 -0
- package/src/node/channel/channel-strategy.ts +56 -0
- package/src/node/channel/index.ts +4 -0
- package/src/node/handler/handler-adapter.ts +38 -0
- package/src/node/handler/handler-protocol.ts +1 -0
- package/src/node/handler/index.ts +2 -0
- package/src/node/index.ts +2 -0
- package/src/node/module.ts +4 -0
- package/src/package.spec.ts +4 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { MessageConnection, ResponseError, Emitter, Event } from 'vscode-jsonrpc';
|
|
2
|
+
import { ApplicationError, Disposable, Logger, getTargetClass, getPropertyNames } from '@celljs/core';
|
|
3
|
+
import { ErrorConverter } from '../converter';
|
|
4
|
+
import { ConnectionHandler } from '../handler';
|
|
5
|
+
import { PipeManager } from '@celljs/web';
|
|
6
|
+
|
|
7
|
+
export type JsonRpcServer<Client> = Disposable & {
|
|
8
|
+
/**
|
|
9
|
+
* If this server is a proxy to a remote server then
|
|
10
|
+
* a client is used as a local object
|
|
11
|
+
* to handle JSON-RPC messages from the remote server.
|
|
12
|
+
*/
|
|
13
|
+
setClient(client: Client | undefined): void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export interface JsonRpcConnectionEventEmitter {
|
|
17
|
+
readonly onDidOpenConnection: Event<void> ;
|
|
18
|
+
readonly onDidCloseConnection: Event<void>;
|
|
19
|
+
}
|
|
20
|
+
export type JsonRpcProxy<T> = T & JsonRpcConnectionEventEmitter;
|
|
21
|
+
|
|
22
|
+
export class JsonRpcConnectionHandler<T extends object> implements ConnectionHandler {
|
|
23
|
+
constructor(
|
|
24
|
+
readonly path: string,
|
|
25
|
+
readonly targetFactory: (proxy: JsonRpcProxy<T>) => any,
|
|
26
|
+
readonly errorConverters: ErrorConverter[],
|
|
27
|
+
readonly pipeManager: PipeManager,
|
|
28
|
+
readonly logger: Logger
|
|
29
|
+
) { }
|
|
30
|
+
|
|
31
|
+
onConnection(connection: MessageConnection): void {
|
|
32
|
+
const factory = new JsonRpcProxyFactory<T>(undefined, this.errorConverters, this.pipeManager, this.logger);
|
|
33
|
+
const proxy = factory.createProxy();
|
|
34
|
+
factory.target = this.targetFactory(proxy);
|
|
35
|
+
factory.listen(connection);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
40
|
+
|
|
41
|
+
private static SET_CLIENT = 'setClient';
|
|
42
|
+
private static ON_DID_OPEN_CONNECTION = 'onDidOpenConnection';
|
|
43
|
+
private static ON_DID_CLOSE_CONNECTION = 'onDidCloseConnection';
|
|
44
|
+
private readonly onDidOpenConnectionEmitter = new Emitter<void>();
|
|
45
|
+
private readonly onDidCloseConnectionEmitter = new Emitter<void>();
|
|
46
|
+
protected internalMethods: Map<string, () => any>;
|
|
47
|
+
|
|
48
|
+
protected connectionPromiseResolve: (connection: MessageConnection) => void;
|
|
49
|
+
protected connectionPromise: Promise<MessageConnection>;
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
public target?: any,
|
|
53
|
+
protected errorConverters?: ErrorConverter[],
|
|
54
|
+
protected pipeManager?: PipeManager,
|
|
55
|
+
protected logger?: Logger) {
|
|
56
|
+
this.waitForConnection();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
protected waitForConnection(): void {
|
|
60
|
+
this.connectionPromise = new Promise(resolve =>
|
|
61
|
+
this.connectionPromiseResolve = resolve
|
|
62
|
+
);
|
|
63
|
+
this.connectionPromise.then(connection => {
|
|
64
|
+
connection.onClose(() =>
|
|
65
|
+
this.onDidCloseConnectionEmitter.fire(undefined)
|
|
66
|
+
);
|
|
67
|
+
this.onDidOpenConnectionEmitter.fire(undefined);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected getRequestHander(prop: string) {
|
|
72
|
+
return (...args: any[]) => this.onRequest(prop, ...args);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected getNotificationHander(prop: string) {
|
|
76
|
+
return (...args: any[]) => this.onNotification(prop, ...args);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
protected registerMethods(connection: MessageConnection) {
|
|
80
|
+
if (this.target) {
|
|
81
|
+
for (const prop of getPropertyNames(this.target)) {
|
|
82
|
+
if (typeof this.target[prop] === 'function') {
|
|
83
|
+
connection.onRequest(prop, this.getRequestHander(prop));
|
|
84
|
+
connection.onNotification(prop, this.getNotificationHander(prop));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
listen(connection: MessageConnection) {
|
|
91
|
+
this.registerMethods(connection);
|
|
92
|
+
connection.onDispose(() => this.waitForConnection());
|
|
93
|
+
connection.listen();
|
|
94
|
+
this.connectionPromiseResolve(connection);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
protected async onRequest(method: string, ...args: any[]): Promise<any> {
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
const message = `${getTargetClass(this.target).name}.${method}`;
|
|
100
|
+
// eslint-disable-next-line no-unused-expressions
|
|
101
|
+
this.logger?.info(`starting ${message}`);
|
|
102
|
+
try {
|
|
103
|
+
// eslint-disable-next-line no-unused-expressions
|
|
104
|
+
await this.pipeManager?.apply({ target: this.target, method: method }, args);
|
|
105
|
+
|
|
106
|
+
return await this.target[method](...args);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const e = this.serializeError(error);
|
|
109
|
+
if (e instanceof ResponseError) {
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
const reason = e.message || '';
|
|
113
|
+
const stack = e.stack || '';
|
|
114
|
+
// eslint-disable-next-line no-unused-expressions
|
|
115
|
+
this.logger?.error(`Request ${method} failed with error: ${reason}`, stack);
|
|
116
|
+
throw e;
|
|
117
|
+
} finally {
|
|
118
|
+
// eslint-disable-next-line no-unused-expressions
|
|
119
|
+
this.logger?.info(`ending ${message}, cost ${Date.now() - now}ms`);
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected onNotification(method: string, ...args: any[]): void {
|
|
125
|
+
this.target[method](...args);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
createProxy(): JsonRpcProxy<T> {
|
|
129
|
+
const proxy = new Proxy<T>(this as any, this);
|
|
130
|
+
return proxy as any;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get(target: T, p: PropertyKey, receiver: any): any {
|
|
134
|
+
const method = this.getInternalMethod(p.toString());
|
|
135
|
+
if (method) {
|
|
136
|
+
return method;
|
|
137
|
+
}
|
|
138
|
+
return this.createProxyMethod(p.toString());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected getInternalMethod(method: string) {
|
|
142
|
+
if (method === JsonRpcProxyFactory.SET_CLIENT) {
|
|
143
|
+
return (client: any) => this.target = client;
|
|
144
|
+
}
|
|
145
|
+
if (method === JsonRpcProxyFactory.ON_DID_OPEN_CONNECTION) {
|
|
146
|
+
return this.onDidOpenConnectionEmitter.event;
|
|
147
|
+
}
|
|
148
|
+
if (method === JsonRpcProxyFactory.ON_DID_CLOSE_CONNECTION) {
|
|
149
|
+
return this.onDidCloseConnectionEmitter.event;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
protected createProxyMethod(method: string) {
|
|
154
|
+
if (method === 'then') {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
const isNotify = this.isNotification(method);
|
|
158
|
+
return async (...args: any[]) => {
|
|
159
|
+
const capturedError = new Error(`Request '${method}' failed`);
|
|
160
|
+
const connection = await this.connectionPromise;
|
|
161
|
+
if (isNotify) {
|
|
162
|
+
connection.sendNotification(method, ...args);
|
|
163
|
+
} else {
|
|
164
|
+
try {
|
|
165
|
+
return await connection.sendRequest(method, ...args) as Promise<any>;
|
|
166
|
+
} catch (err) {
|
|
167
|
+
throw this.deserializeError(capturedError, err);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
protected isNotification(p: PropertyKey): boolean {
|
|
174
|
+
return p.toString().startsWith('notify') || p.toString().startsWith('on');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
protected serializeError(e: any): any {
|
|
178
|
+
if (this.errorConverters) {
|
|
179
|
+
for (const converter of this.errorConverters) {
|
|
180
|
+
const serialized = converter.serialize(e);
|
|
181
|
+
if (serialized) {
|
|
182
|
+
e = serialized;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (ApplicationError.is(e)) {
|
|
188
|
+
return new ResponseError(e.code, '',
|
|
189
|
+
Object.assign({ kind: 'application' }, e.toJson())
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return e;
|
|
193
|
+
}
|
|
194
|
+
protected deserializeError(capturedError: Error, e: any): any {
|
|
195
|
+
if (e instanceof ResponseError) {
|
|
196
|
+
const capturedStack = capturedError.stack || '';
|
|
197
|
+
if (e.data && e.data.kind === 'application') {
|
|
198
|
+
const { stack, data, message } = e.data;
|
|
199
|
+
e = ApplicationError.fromJson(e.code, {
|
|
200
|
+
message: message || capturedError.message,
|
|
201
|
+
data,
|
|
202
|
+
stack: `${capturedStack}\nCaused by: ${stack}`
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
e.stack = capturedStack;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (this.errorConverters) {
|
|
209
|
+
for (const converter of this.errorConverters) {
|
|
210
|
+
const deserialized = converter.deserialize(e);
|
|
211
|
+
if (deserialized) {
|
|
212
|
+
return deserialized;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return e;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { DataCallback, AbstractMessageReader, Disposable } from 'vscode-jsonrpc';
|
|
2
|
+
import { Channel } from '../channal/channel-protocol';
|
|
3
|
+
|
|
4
|
+
export class ChannelMessageReader extends AbstractMessageReader {
|
|
5
|
+
|
|
6
|
+
protected state: 'initial' | 'listening' | 'closed' = 'initial';
|
|
7
|
+
protected callback: DataCallback | undefined;
|
|
8
|
+
protected readonly events: { message?: any, error?: any }[] = [];
|
|
9
|
+
|
|
10
|
+
constructor(protected readonly channel: Channel) {
|
|
11
|
+
super();
|
|
12
|
+
this.channel.onMessage(message =>
|
|
13
|
+
this.readMessage(message)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
listen(callback: DataCallback): Disposable {
|
|
18
|
+
if (this.state === 'initial') {
|
|
19
|
+
this.state = 'listening';
|
|
20
|
+
this.callback = callback;
|
|
21
|
+
while (this.events.length !== 0) {
|
|
22
|
+
const event = this.events.pop()!;
|
|
23
|
+
if (event.message) {
|
|
24
|
+
this.readMessage(event.message);
|
|
25
|
+
} else if (event.error) {
|
|
26
|
+
this.fireError(event.error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return Disposable.create(() => {});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected readMessage(message: any): void {
|
|
34
|
+
if (this.state === 'initial') {
|
|
35
|
+
this.events.splice(0, 0, { message });
|
|
36
|
+
} else if (this.state === 'listening') {
|
|
37
|
+
const data = JSON.parse(message);
|
|
38
|
+
this.callback!(data);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected override fireError(error: any): void {
|
|
43
|
+
if (this.state === 'initial') {
|
|
44
|
+
this.events.splice(0, 0, { error });
|
|
45
|
+
} else if (this.state === 'listening') {
|
|
46
|
+
super.fireError(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Message, AbstractMessageWriter } from 'vscode-jsonrpc';
|
|
2
|
+
import { Channel } from '../channal';
|
|
3
|
+
|
|
4
|
+
export class ChannelMessageWriter extends AbstractMessageWriter {
|
|
5
|
+
|
|
6
|
+
protected errorCount = 0;
|
|
7
|
+
|
|
8
|
+
constructor(protected readonly channel: Channel) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async write(msg: Message): Promise<void> {
|
|
13
|
+
try {
|
|
14
|
+
const content = JSON.stringify(msg);
|
|
15
|
+
this.channel.send(content);
|
|
16
|
+
} catch (e) {
|
|
17
|
+
this.errorCount++;
|
|
18
|
+
this.fireError(e, msg, this.errorCount);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
end() {
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './annotation';
|
|
2
|
+
export * from './channal';
|
|
3
|
+
export * from './endpoint';
|
|
4
|
+
export * from './error';
|
|
5
|
+
export * from './factory';
|
|
6
|
+
export * from './handler';
|
|
7
|
+
export * from './proxy';
|
|
8
|
+
export * from './utils';
|
|
9
|
+
export * from './constants';
|
|
10
|
+
export * from './converter';
|
|
11
|
+
export * from './processor';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { autoBind } from '@celljs/core';
|
|
2
|
+
import { ProxyProvider } from './proxy';
|
|
3
|
+
import { ID_KEY, RPC } from './annotation';
|
|
4
|
+
import { RpcUtil, ConverterUtil } from './utils';
|
|
5
|
+
import './index';
|
|
6
|
+
|
|
7
|
+
export default autoBind(bind => {
|
|
8
|
+
|
|
9
|
+
bind(RPC).toDynamicValue(ctx => {
|
|
10
|
+
const id = ctx.currentRequest.target.getCustomTags()?.find(m => m.key === ID_KEY)!.value;
|
|
11
|
+
const path = RpcUtil.toPath(id);
|
|
12
|
+
const proxyProvider = ctx.container.get<ProxyProvider>(ProxyProvider);
|
|
13
|
+
const errorConverters = ConverterUtil.getGlobalErrorConverters(ctx.container);
|
|
14
|
+
const errorConverter = ConverterUtil.getErrorConverters(id, ctx.container);
|
|
15
|
+
if (errorConverter) {
|
|
16
|
+
errorConverters.push(errorConverter);
|
|
17
|
+
}
|
|
18
|
+
return proxyProvider.provide(path, errorConverters);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Component } from '@celljs/core';
|
|
2
|
+
import { ClientConfigProcessor } from './processor-protocol';
|
|
3
|
+
import { AxiosRequestConfig } from 'axios';
|
|
4
|
+
|
|
5
|
+
@Component(ClientConfigProcessor)
|
|
6
|
+
export class NoOpClientConfigProcessor implements ClientConfigProcessor {
|
|
7
|
+
|
|
8
|
+
async process(config: AxiosRequestConfig): Promise<void> {
|
|
9
|
+
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Component, Autowired, Value, PostConstruct } from '@celljs/core';
|
|
2
|
+
import { RestOperations } from '@celljs/http';
|
|
3
|
+
import { ProxyCreator } from './proxy-protocol';
|
|
4
|
+
import { EndpointResolver } from '../endpoint';
|
|
5
|
+
import { ConnnectionFactory, JsonRpcProxy } from '../factory';
|
|
6
|
+
import { Channel } from '../channal';
|
|
7
|
+
import { ErrorConverter } from '../converter';
|
|
8
|
+
import { AxiosRequestConfig } from 'axios';
|
|
9
|
+
import { ClientConfigProcessor } from '../processor';
|
|
10
|
+
import { HttpProxyCreator } from './http-proxy-creator';
|
|
11
|
+
|
|
12
|
+
@Component(ProxyCreator)
|
|
13
|
+
export class DelegatingHttpProxyCreator implements ProxyCreator {
|
|
14
|
+
|
|
15
|
+
@Autowired(ConnnectionFactory)
|
|
16
|
+
protected connnectionFactory: ConnnectionFactory<Channel>;
|
|
17
|
+
|
|
18
|
+
@Autowired(EndpointResolver)
|
|
19
|
+
protected endpointResolver: EndpointResolver;
|
|
20
|
+
|
|
21
|
+
@Autowired(RestOperations)
|
|
22
|
+
protected restOperations: RestOperations;
|
|
23
|
+
|
|
24
|
+
@Autowired(ClientConfigProcessor)
|
|
25
|
+
protected clientConfigProcessor: ClientConfigProcessor;
|
|
26
|
+
|
|
27
|
+
@Value('cell.rpc.client.config')
|
|
28
|
+
protected readonly clientConfig: AxiosRequestConfig;
|
|
29
|
+
|
|
30
|
+
@Value('cell.rpc.merge.maxCount')
|
|
31
|
+
protected readonly maxCount: number;
|
|
32
|
+
|
|
33
|
+
@Value('cell.rpc.merge.maxLength')
|
|
34
|
+
protected readonly maxLength: number;
|
|
35
|
+
|
|
36
|
+
@Value('cell.rpc.merge.timerDelay')
|
|
37
|
+
protected readonly timerDelay: number;
|
|
38
|
+
|
|
39
|
+
@Value('cell.rpc.merge.enabled')
|
|
40
|
+
protected readonly enabled: boolean;
|
|
41
|
+
|
|
42
|
+
protected delegate: ProxyCreator;
|
|
43
|
+
|
|
44
|
+
@PostConstruct()
|
|
45
|
+
protected init() {
|
|
46
|
+
this.delegate = new HttpProxyCreator({
|
|
47
|
+
connnectionFactory: this.connnectionFactory,
|
|
48
|
+
endpointResolver: this.endpointResolver,
|
|
49
|
+
restOperations: this.restOperations,
|
|
50
|
+
clientConfigProcessor: this.clientConfigProcessor,
|
|
51
|
+
clientConfig: this.clientConfig,
|
|
52
|
+
merge: {
|
|
53
|
+
maxCount: this.maxCount,
|
|
54
|
+
maxLength: this.maxLength,
|
|
55
|
+
timerDelay: this.timerDelay,
|
|
56
|
+
enabled: this.enabled
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
create<T extends object>(path: string, errorConverters?: ErrorConverter[], target?: object | undefined): JsonRpcProxy<T> {
|
|
62
|
+
return this.delegate.create(path, errorConverters, target);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
support(path: string): number {
|
|
66
|
+
return this.delegate.support(path);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { HttpHeaders, HttpMethod, MediaType, XML_HTTP_REQUEST, RestOperations, DefaultRestOperationsFactory } from '@celljs/http';
|
|
2
|
+
import { Logger } from 'vscode-jsonrpc';
|
|
3
|
+
import { ProxyCreator, ConnectionOptions, RequestTaskMeta } from './proxy-protocol';
|
|
4
|
+
import { EndpointResolver } from '../endpoint';
|
|
5
|
+
import { ConnnectionFactory, ConnnectionFactoryImpl, JsonRpcProxy, JsonRpcProxyFactory } from '../factory';
|
|
6
|
+
import { Channel, HttpChannel } from '../channal';
|
|
7
|
+
import { ErrorConverter } from '../converter';
|
|
8
|
+
import { ConnectionHandler } from '../handler';
|
|
9
|
+
import { AxiosRequestConfig } from 'axios';
|
|
10
|
+
import { ClientConfigProcessor } from '../processor';
|
|
11
|
+
import { ConsoleLogger } from '../utils';
|
|
12
|
+
|
|
13
|
+
export interface MergeOptions {
|
|
14
|
+
maxCount: number;
|
|
15
|
+
maxLength: number;
|
|
16
|
+
timerDelay: number;
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface HttpProxyCreatorOptions<T> {
|
|
21
|
+
|
|
22
|
+
connnectionFactory: ConnnectionFactory<Channel>;
|
|
23
|
+
endpointResolver: EndpointResolver;
|
|
24
|
+
restOperations: RestOperations;
|
|
25
|
+
clientConfigProcessor?: ClientConfigProcessor;
|
|
26
|
+
clientConfig: AxiosRequestConfig;
|
|
27
|
+
merge: T;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DEFAULT_OPTIONS: Omit<HttpProxyCreatorOptions<MergeOptions>, 'endpointResolver'> = {
|
|
31
|
+
connnectionFactory: new ConnnectionFactoryImpl(),
|
|
32
|
+
restOperations: new DefaultRestOperationsFactory().create(),
|
|
33
|
+
clientConfig: {},
|
|
34
|
+
merge: {
|
|
35
|
+
maxCount: 100,
|
|
36
|
+
maxLength: 5242880,
|
|
37
|
+
timerDelay: 35,
|
|
38
|
+
enabled: false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export class HttpProxyCreator implements ProxyCreator {
|
|
44
|
+
|
|
45
|
+
protected channelIdSeq = 0;
|
|
46
|
+
|
|
47
|
+
protected requestMap = new Map<string, RequestTaskMeta>();
|
|
48
|
+
protected channelMap = new Map<number, Channel>();
|
|
49
|
+
protected options: HttpProxyCreatorOptions<MergeOptions>;
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
options: Partial<Omit<HttpProxyCreatorOptions<Partial<MergeOptions>>, 'endpointResolver'>> & Pick<HttpProxyCreatorOptions<Partial<MergeOptions>>, 'endpointResolver'>) {
|
|
53
|
+
this.options = { ...DEFAULT_OPTIONS, ...options, merge: { ...DEFAULT_OPTIONS.merge, ...options.merge } };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
create<T extends object>(path: string, errorConverters?: ErrorConverter[], target?: object | undefined): JsonRpcProxy<T> {
|
|
57
|
+
const factory = new JsonRpcProxyFactory<T>(target, errorConverters);
|
|
58
|
+
this.options.endpointResolver.resolve(path).then(endpoint => this.listen({
|
|
59
|
+
path: endpoint,
|
|
60
|
+
onConnection: c => factory.listen(c)
|
|
61
|
+
}));
|
|
62
|
+
return factory.createProxy();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
support(path: string): number {
|
|
66
|
+
return 500;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
listen(handler: ConnectionHandler, options?: ConnectionOptions): void {
|
|
70
|
+
this.openChannel(handler.path, channel => {
|
|
71
|
+
const connection = this.options.connnectionFactory.create(channel, this.createLogger());
|
|
72
|
+
handler.onConnection(connection);
|
|
73
|
+
}, options);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
openChannel(path: string, handler: (channel: Channel) => void, options?: ConnectionOptions): void {
|
|
77
|
+
this.doOpenChannel(path, handler, options);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected doOpenChannel(path: string, handler: (channel: Channel) => void, options?: ConnectionOptions): void {
|
|
81
|
+
const id = this.channelIdSeq++;
|
|
82
|
+
const channel = this.createChannel(id, path);
|
|
83
|
+
handler(channel);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected createChannel(id: number, path: string): Channel {
|
|
87
|
+
const parts = path.split('/');
|
|
88
|
+
const serviceName = parts.pop();
|
|
89
|
+
const endpoint = parts.join('/');
|
|
90
|
+
const channel = new HttpChannel(id, async content => {
|
|
91
|
+
if (this.options.merge.enabled) {
|
|
92
|
+
if (this.canMerge(endpoint, content)) {
|
|
93
|
+
this.pushContent(endpoint, content);
|
|
94
|
+
} else {
|
|
95
|
+
this.executeTask(endpoint);
|
|
96
|
+
this.pushContent(endpoint, content);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
this.pushContent(endpoint, content);
|
|
100
|
+
this.executeTask(endpoint);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
}, serviceName);
|
|
104
|
+
this.channelMap.set(id, channel);
|
|
105
|
+
return channel;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
protected executeTask(endpoint: string) {
|
|
109
|
+
const meta = this.requestMap.get(endpoint);
|
|
110
|
+
if (meta) {
|
|
111
|
+
return meta.task();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected canMerge(endpoint: string, content: string) {
|
|
116
|
+
const meta = this.requestMap.get(endpoint);
|
|
117
|
+
if (meta) {
|
|
118
|
+
if (meta.contentLength + content.length > this.options.merge.maxLength ||
|
|
119
|
+
meta.contents.length + 1 > this.options.merge.maxCount) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
protected pushContent(endpoint: string, content: string) {
|
|
127
|
+
let meta = this.requestMap.get(endpoint);
|
|
128
|
+
if (!meta) {
|
|
129
|
+
const task = this.createTask(endpoint);
|
|
130
|
+
meta = {
|
|
131
|
+
id: setTimeout(task, this.options.merge.timerDelay),
|
|
132
|
+
contents: [],
|
|
133
|
+
contentLength: 0,
|
|
134
|
+
task
|
|
135
|
+
};
|
|
136
|
+
this.requestMap.set(endpoint, meta);
|
|
137
|
+
}
|
|
138
|
+
meta.contents.push(content);
|
|
139
|
+
meta.contentLength += content.length;
|
|
140
|
+
return meta;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
protected createTask(endpoint: string) {
|
|
144
|
+
return async () => {
|
|
145
|
+
const meta = this.requestMap.get(endpoint);
|
|
146
|
+
if (!meta) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
clearTimeout(meta.id as any);
|
|
150
|
+
const contents = meta.contents;
|
|
151
|
+
this.requestMap.delete(endpoint);
|
|
152
|
+
const config: AxiosRequestConfig = {
|
|
153
|
+
url: endpoint,
|
|
154
|
+
method: HttpMethod.POST,
|
|
155
|
+
data: contents.length > 1 ? JSON.stringify(contents) : contents[0],
|
|
156
|
+
headers: {
|
|
157
|
+
[HttpHeaders.CONTENT_TYPE]: MediaType.APPLICATION_JSON_UTF8,
|
|
158
|
+
[HttpHeaders.X_REQUESTED_WITH]: XML_HTTP_REQUEST
|
|
159
|
+
},
|
|
160
|
+
...this.options.clientConfig
|
|
161
|
+
};
|
|
162
|
+
await this.options.clientConfigProcessor?.process(config);
|
|
163
|
+
const { data } = await this.options.restOperations.request(config);
|
|
164
|
+
if (Array.isArray(data)) {
|
|
165
|
+
for (const message of data) {
|
|
166
|
+
const parsed = JSON.parse(message);
|
|
167
|
+
this.channelMap.get(parsed.id)!.handleMessage(parsed);
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
this.channelMap.get(data.id)!.handleMessage(data);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
protected createLogger(): Logger {
|
|
176
|
+
return new ConsoleLogger();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ErrorConverter } from '../converter';
|
|
2
|
+
import { JsonRpcProxy } from '../factory';
|
|
3
|
+
|
|
4
|
+
export const ProxyProvider = Symbol('ProxyProvider');
|
|
5
|
+
|
|
6
|
+
export interface ProxyProvider {
|
|
7
|
+
provide<T extends object>(path: string, errorConverters?: ErrorConverter[], target?: object): JsonRpcProxy<T>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ProxyCreator = Symbol('ProxyCreator');
|
|
11
|
+
|
|
12
|
+
export interface ProxyCreator {
|
|
13
|
+
create<T extends object>(path: string, errorConverters?: ErrorConverter[], target?: object): JsonRpcProxy<T>;
|
|
14
|
+
support(path: string): number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ConnectionOptions {
|
|
18
|
+
reconnecting?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RequestTaskMeta {
|
|
22
|
+
id: number | NodeJS.Timeout;
|
|
23
|
+
contentLength: number;
|
|
24
|
+
contents: string[];
|
|
25
|
+
task: () => Promise<void>;
|
|
26
|
+
}
|