1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | Object.defineProperty(exports, "__esModule", { value: true });
|
19 | exports.RpcProtocol = void 0;
|
20 | const cancellation_1 = require("../cancellation");
|
21 | const disposable_1 = require("../disposable");
|
22 | const event_1 = require("../event");
|
23 | const promise_util_1 = require("../promise-util");
|
24 | const rpc_message_encoder_1 = require("./rpc-message-encoder");
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | class RpcProtocol {
|
34 | constructor(channel, requestHandler, options = {}) {
|
35 | var _a, _b, _c;
|
36 | this.channel = channel;
|
37 | this.requestHandler = requestHandler;
|
38 | this.pendingRequests = new Map();
|
39 | this.nextMessageId = 0;
|
40 | this.onNotificationEmitter = new event_1.Emitter();
|
41 | this.cancellationTokenSources = new Map();
|
42 | this.toDispose = new disposable_1.DisposableCollection();
|
43 | this.encoder = (_a = options.encoder) !== null && _a !== void 0 ? _a : new rpc_message_encoder_1.MsgPackMessageEncoder();
|
44 | this.decoder = (_b = options.decoder) !== null && _b !== void 0 ? _b : new rpc_message_encoder_1.MsgPackMessageDecoder();
|
45 | this.toDispose.push(this.onNotificationEmitter);
|
46 | channel.onClose(event => {
|
47 | this.pendingRequests.forEach(pending => pending.reject(new Error(event.reason)));
|
48 | this.pendingRequests.clear();
|
49 | this.toDispose.dispose();
|
50 | });
|
51 | this.toDispose.push(channel.onMessage(readBuffer => this.handleMessage(this.decoder.parse(readBuffer()))));
|
52 | this.mode = (_c = options.mode) !== null && _c !== void 0 ? _c : 'default';
|
53 | if (this.mode !== 'clientOnly' && requestHandler === undefined) {
|
54 | console.error('RPCProtocol was initialized without a request handler but was not set to clientOnly mode.');
|
55 | }
|
56 | }
|
57 | get onNotification() {
|
58 | return this.onNotificationEmitter.event;
|
59 | }
|
60 | handleMessage(message) {
|
61 | if (this.mode !== 'clientOnly') {
|
62 | switch (message.type) {
|
63 | case 5 : {
|
64 | this.handleCancel(message.id);
|
65 | return;
|
66 | }
|
67 | case 1 : {
|
68 | this.handleRequest(message.id, message.method, message.args);
|
69 | return;
|
70 | }
|
71 | case 2 : {
|
72 | this.handleNotify(message.method, message.args, message.id);
|
73 | return;
|
74 | }
|
75 | }
|
76 | }
|
77 | if (this.mode !== 'serverOnly') {
|
78 | switch (message.type) {
|
79 | case 3 : {
|
80 | this.handleReply(message.id, message.res);
|
81 | return;
|
82 | }
|
83 | case 4 : {
|
84 | this.handleReplyErr(message.id, message.err);
|
85 | return;
|
86 | }
|
87 | }
|
88 | }
|
89 |
|
90 | console.warn(`Received message incompatible with this RPCProtocol's mode '${this.mode}'. Type: ${message.type}. ID: ${message.id}.`);
|
91 | }
|
92 | handleReply(id, value) {
|
93 | const replyHandler = this.pendingRequests.get(id);
|
94 | if (replyHandler) {
|
95 | this.pendingRequests.delete(id);
|
96 | replyHandler.resolve(value);
|
97 | }
|
98 | else {
|
99 | throw new Error(`No reply handler for reply with id: ${id}`);
|
100 | }
|
101 | }
|
102 | handleReplyErr(id, error) {
|
103 | const replyHandler = this.pendingRequests.get(id);
|
104 | if (replyHandler) {
|
105 | this.pendingRequests.delete(id);
|
106 | replyHandler.reject(error);
|
107 | }
|
108 | else {
|
109 | throw new Error(`No reply handler for error reply with id: ${id}`);
|
110 | }
|
111 | }
|
112 | sendRequest(method, args) {
|
113 |
|
114 |
|
115 | const cancellationToken = args.length && cancellation_1.CancellationToken.is(args[args.length - 1]) ? args.pop() : undefined;
|
116 | const id = this.nextMessageId++;
|
117 | const reply = new promise_util_1.Deferred();
|
118 | if (cancellationToken) {
|
119 | args.push(RpcProtocol.CANCELLATION_TOKEN_KEY);
|
120 | }
|
121 | this.pendingRequests.set(id, reply);
|
122 | const output = this.channel.getWriteBuffer();
|
123 | this.encoder.request(output, id, method, args);
|
124 | output.commit();
|
125 | if (cancellationToken === null || cancellationToken === void 0 ? void 0 : cancellationToken.isCancellationRequested) {
|
126 | this.sendCancel(id);
|
127 | }
|
128 | else {
|
129 | cancellationToken === null || cancellationToken === void 0 ? void 0 : cancellationToken.onCancellationRequested(() => this.sendCancel(id));
|
130 | }
|
131 | return reply.promise;
|
132 | }
|
133 | sendNotification(method, args) {
|
134 |
|
135 |
|
136 | if (args.length && cancellation_1.CancellationToken.is(args[args.length - 1])) {
|
137 | this.sendRequest(method, args);
|
138 | return;
|
139 | }
|
140 | const output = this.channel.getWriteBuffer();
|
141 | this.encoder.notification(output, method, args, this.nextMessageId++);
|
142 | output.commit();
|
143 | }
|
144 | sendCancel(requestId) {
|
145 | const output = this.channel.getWriteBuffer();
|
146 | this.encoder.cancel(output, requestId);
|
147 | output.commit();
|
148 | }
|
149 | handleCancel(id) {
|
150 | const cancellationTokenSource = this.cancellationTokenSources.get(id);
|
151 | if (cancellationTokenSource) {
|
152 | cancellationTokenSource.cancel();
|
153 | }
|
154 | }
|
155 | async handleRequest(id, method, args) {
|
156 | const output = this.channel.getWriteBuffer();
|
157 |
|
158 |
|
159 | const addToken = args.length && args[args.length - 1] === RpcProtocol.CANCELLATION_TOKEN_KEY ? args.pop() : false;
|
160 | if (addToken) {
|
161 | const tokenSource = new cancellation_1.CancellationTokenSource();
|
162 | this.cancellationTokenSources.set(id, tokenSource);
|
163 | args.push(tokenSource.token);
|
164 | }
|
165 | try {
|
166 | const result = await this.requestHandler(method, args);
|
167 | this.cancellationTokenSources.delete(id);
|
168 | this.encoder.replyOK(output, id, result);
|
169 | output.commit();
|
170 | }
|
171 | catch (err) {
|
172 |
|
173 |
|
174 | if (disposable_1.Disposable.is(output)) {
|
175 | output.dispose();
|
176 | }
|
177 | const errorOutput = this.channel.getWriteBuffer();
|
178 | this.cancellationTokenSources.delete(id);
|
179 | this.encoder.replyErr(errorOutput, id, err);
|
180 | errorOutput.commit();
|
181 | }
|
182 | }
|
183 | async handleNotify(method, args, id) {
|
184 | if (this.toDispose.disposed) {
|
185 | return;
|
186 | }
|
187 | this.onNotificationEmitter.fire({ method, args });
|
188 | }
|
189 | }
|
190 | exports.RpcProtocol = RpcProtocol;
|
191 | RpcProtocol.CANCELLATION_TOKEN_KEY = 'add.cancellation.token';
|
192 |
|
\ | No newline at end of file |