Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/common/rpcProtocol.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { RunOnceScheduler } from '../../../../base/common/async.js';
7
import { VSBuffer } from '../../../../base/common/buffer.js';
8
import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
9
import { CharCode } from '../../../../base/common/charCode.js';
10
import * as errors from '../../../../base/common/errors.js';
11
import { Emitter, Event } from '../../../../base/common/event.js';
12
import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';
13
import { MarshalledObject } from '../../../../base/common/marshalling.js';
14
import { MarshalledId } from '../../../../base/common/marshallingIds.js';
15
import { IURITransformer, transformIncomingURIs } from '../../../../base/common/uriIpc.js';
16
import { IMessagePassingProtocol } from '../../../../base/parts/ipc/common/ipc.js';
17
import { CanceledLazyPromise, LazyPromise } from './lazyPromise.js';
18
import { getStringIdentifierForProxy, IRPCProtocol, Proxied, ProxyIdentifier, SerializableObjectWithBuffers } from './proxyIdentifier.js';
19
20
export interface JSONStringifyReplacer {
21
(key: string, value: any): any;
22
}
23
24
function safeStringify(obj: any, replacer: JSONStringifyReplacer | null): string {
25
try {
26
return JSON.stringify(obj, <(key: string, value: any) => any>replacer);
27
} catch (err) {
28
return 'null';
29
}
30
}
31
32
const refSymbolName = '$$ref$$';
33
const undefinedRef = { [refSymbolName]: -1 } as const;
34
35
class StringifiedJsonWithBufferRefs {
36
constructor(
37
public readonly jsonString: string,
38
public readonly referencedBuffers: readonly VSBuffer[],
39
) { }
40
}
41
42
export function stringifyJsonWithBufferRefs<T>(obj: T, replacer: JSONStringifyReplacer | null = null, useSafeStringify = false): StringifiedJsonWithBufferRefs {
43
const foundBuffers: VSBuffer[] = [];
44
const serialized = (useSafeStringify ? safeStringify : JSON.stringify)(obj, (key, value) => {
45
if (typeof value === 'undefined') {
46
return undefinedRef; // JSON.stringify normally converts 'undefined' to 'null'
47
} else if (typeof value === 'object') {
48
if (value instanceof VSBuffer) {
49
const bufferIndex = foundBuffers.push(value) - 1;
50
return { [refSymbolName]: bufferIndex };
51
}
52
if (replacer) {
53
return replacer(key, value);
54
}
55
}
56
return value;
57
});
58
return {
59
jsonString: serialized,
60
referencedBuffers: foundBuffers
61
};
62
}
63
64
export function parseJsonAndRestoreBufferRefs(jsonString: string, buffers: readonly VSBuffer[], uriTransformer: IURITransformer | null): any {
65
return JSON.parse(jsonString, (_key, value) => {
66
if (value) {
67
const ref = value[refSymbolName];
68
if (typeof ref === 'number') {
69
return buffers[ref];
70
}
71
72
if (uriTransformer && (<MarshalledObject>value).$mid === MarshalledId.Uri) {
73
return uriTransformer.transformIncoming(value);
74
}
75
}
76
return value;
77
});
78
}
79
80
81
function stringify(obj: any, replacer: JSONStringifyReplacer | null): string {
82
return JSON.stringify(obj, <(key: string, value: any) => any>replacer);
83
}
84
85
function createURIReplacer(transformer: IURITransformer | null): JSONStringifyReplacer | null {
86
if (!transformer) {
87
return null;
88
}
89
return (key: string, value: any): any => {
90
if (value && value.$mid === MarshalledId.Uri) {
91
return transformer.transformOutgoing(value);
92
}
93
return value;
94
};
95
}
96
97
export const enum RequestInitiator {
98
LocalSide = 0,
99
OtherSide = 1
100
}
101
102
export const enum ResponsiveState {
103
Responsive = 0,
104
Unresponsive = 1
105
}
106
107
export interface IRPCProtocolLogger {
108
logIncoming(msgLength: number, req: number, initiator: RequestInitiator, str: string, data?: any): void;
109
logOutgoing(msgLength: number, req: number, initiator: RequestInitiator, str: string, data?: any): void;
110
}
111
112
const noop = () => { };
113
114
const _RPCProtocolSymbol = Symbol.for('rpcProtocol');
115
const _RPCProxySymbol = Symbol.for('rpcProxy');
116
117
export class RPCProtocol extends Disposable implements IRPCProtocol {
118
119
[_RPCProtocolSymbol] = true;
120
121
private static readonly UNRESPONSIVE_TIME = 3 * 1000; // 3s
122
123
private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());
124
public readonly onDidChangeResponsiveState: Event<ResponsiveState> = this._onDidChangeResponsiveState.event;
125
126
private readonly _protocol: IMessagePassingProtocol;
127
private readonly _logger: IRPCProtocolLogger | null;
128
private readonly _uriTransformer: IURITransformer | null;
129
private readonly _uriReplacer: JSONStringifyReplacer | null;
130
private _isDisposed: boolean;
131
private readonly _locals: any[];
132
private readonly _proxies: any[];
133
private _lastMessageId: number;
134
private readonly _cancelInvokedHandlers: { [req: string]: () => void };
135
private readonly _pendingRPCReplies: { [msgId: string]: PendingRPCReply };
136
private _responsiveState: ResponsiveState;
137
private _unacknowledgedCount: number;
138
private _unresponsiveTime: number;
139
private _asyncCheckUresponsive: RunOnceScheduler;
140
141
constructor(protocol: IMessagePassingProtocol, logger: IRPCProtocolLogger | null = null, transformer: IURITransformer | null = null) {
142
super();
143
this._protocol = protocol;
144
this._logger = logger;
145
this._uriTransformer = transformer;
146
this._uriReplacer = createURIReplacer(this._uriTransformer);
147
this._isDisposed = false;
148
this._locals = [];
149
this._proxies = [];
150
for (let i = 0, len = ProxyIdentifier.count; i < len; i++) {
151
this._locals[i] = null;
152
this._proxies[i] = null;
153
}
154
this._lastMessageId = 0;
155
this._cancelInvokedHandlers = Object.create(null);
156
this._pendingRPCReplies = {};
157
this._responsiveState = ResponsiveState.Responsive;
158
this._unacknowledgedCount = 0;
159
this._unresponsiveTime = 0;
160
this._asyncCheckUresponsive = this._register(new RunOnceScheduler(() => this._checkUnresponsive(), 1000));
161
this._register(this._protocol.onMessage((msg) => this._receiveOneMessage(msg)));
162
}
163
164
public override dispose(): void {
165
this._isDisposed = true;
166
167
// Release all outstanding promises with a canceled error
168
Object.keys(this._pendingRPCReplies).forEach((msgId) => {
169
const pending = this._pendingRPCReplies[msgId];
170
delete this._pendingRPCReplies[msgId];
171
pending.resolveErr(errors.canceled());
172
});
173
174
super.dispose();
175
}
176
177
public drain(): Promise<void> {
178
if (typeof this._protocol.drain === 'function') {
179
return this._protocol.drain();
180
}
181
return Promise.resolve();
182
}
183
184
private _onWillSendRequest(req: number): void {
185
if (this._unacknowledgedCount === 0) {
186
// Since this is the first request we are sending in a while,
187
// mark this moment as the start for the countdown to unresponsive time
188
this._unresponsiveTime = Date.now() + RPCProtocol.UNRESPONSIVE_TIME;
189
}
190
this._unacknowledgedCount++;
191
if (!this._asyncCheckUresponsive.isScheduled()) {
192
this._asyncCheckUresponsive.schedule();
193
}
194
}
195
196
private _onDidReceiveAcknowledge(req: number): void {
197
// The next possible unresponsive time is now + delta.
198
this._unresponsiveTime = Date.now() + RPCProtocol.UNRESPONSIVE_TIME;
199
this._unacknowledgedCount--;
200
if (this._unacknowledgedCount === 0) {
201
// No more need to check for unresponsive
202
this._asyncCheckUresponsive.cancel();
203
}
204
// The ext host is responsive!
205
this._setResponsiveState(ResponsiveState.Responsive);
206
}
207
208
private _checkUnresponsive(): void {
209
if (this._unacknowledgedCount === 0) {
210
// Not waiting for anything => cannot say if it is responsive or not
211
return;
212
}
213
214
if (Date.now() > this._unresponsiveTime) {
215
// Unresponsive!!
216
this._setResponsiveState(ResponsiveState.Unresponsive);
217
} else {
218
// Not (yet) unresponsive, be sure to check again soon
219
this._asyncCheckUresponsive.schedule();
220
}
221
}
222
223
private _setResponsiveState(newResponsiveState: ResponsiveState): void {
224
if (this._responsiveState === newResponsiveState) {
225
// no change
226
return;
227
}
228
this._responsiveState = newResponsiveState;
229
this._onDidChangeResponsiveState.fire(this._responsiveState);
230
}
231
232
public get responsiveState(): ResponsiveState {
233
return this._responsiveState;
234
}
235
236
public transformIncomingURIs<T>(obj: T): T {
237
if (!this._uriTransformer) {
238
return obj;
239
}
240
return transformIncomingURIs(obj, this._uriTransformer);
241
}
242
243
public getProxy<T>(identifier: ProxyIdentifier<T>): Proxied<T> {
244
const { nid: rpcId, sid } = identifier;
245
if (!this._proxies[rpcId]) {
246
this._proxies[rpcId] = this._createProxy(rpcId, sid);
247
}
248
return this._proxies[rpcId];
249
}
250
251
private _createProxy<T>(rpcId: number, debugName: string): T {
252
const handler = {
253
get: (target: any, name: PropertyKey) => {
254
if (typeof name === 'string' && !target[name] && name.charCodeAt(0) === CharCode.DollarSign) {
255
target[name] = (...myArgs: any[]) => {
256
return this._remoteCall(rpcId, name, myArgs);
257
};
258
}
259
if (name === _RPCProxySymbol) {
260
return debugName;
261
}
262
return target[name];
263
}
264
};
265
return new Proxy(Object.create(null), handler);
266
}
267
268
public set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
269
this._locals[identifier.nid] = value;
270
return value;
271
}
272
273
public assertRegistered(identifiers: ProxyIdentifier<any>[]): void {
274
for (let i = 0, len = identifiers.length; i < len; i++) {
275
const identifier = identifiers[i];
276
if (!this._locals[identifier.nid]) {
277
throw new Error(`Missing proxy instance ${identifier.sid}`);
278
}
279
}
280
}
281
282
private _receiveOneMessage(rawmsg: VSBuffer): void {
283
if (this._isDisposed) {
284
return;
285
}
286
287
const msgLength = rawmsg.byteLength;
288
const buff = MessageBuffer.read(rawmsg, 0);
289
const messageType = <MessageType>buff.readUInt8();
290
const req = buff.readUInt32();
291
292
switch (messageType) {
293
case MessageType.RequestJSONArgs:
294
case MessageType.RequestJSONArgsWithCancellation: {
295
let { rpcId, method, args } = MessageIO.deserializeRequestJSONArgs(buff);
296
if (this._uriTransformer) {
297
args = transformIncomingURIs(args, this._uriTransformer);
298
}
299
this._receiveRequest(msgLength, req, rpcId, method, args, (messageType === MessageType.RequestJSONArgsWithCancellation));
300
break;
301
}
302
case MessageType.RequestMixedArgs:
303
case MessageType.RequestMixedArgsWithCancellation: {
304
let { rpcId, method, args } = MessageIO.deserializeRequestMixedArgs(buff);
305
if (this._uriTransformer) {
306
args = transformIncomingURIs(args, this._uriTransformer);
307
}
308
this._receiveRequest(msgLength, req, rpcId, method, args, (messageType === MessageType.RequestMixedArgsWithCancellation));
309
break;
310
}
311
case MessageType.Acknowledged: {
312
this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `ack`);
313
this._onDidReceiveAcknowledge(req);
314
break;
315
}
316
case MessageType.Cancel: {
317
this._receiveCancel(msgLength, req);
318
break;
319
}
320
case MessageType.ReplyOKEmpty: {
321
this._receiveReply(msgLength, req, undefined);
322
break;
323
}
324
case MessageType.ReplyOKJSON: {
325
let value = MessageIO.deserializeReplyOKJSON(buff);
326
if (this._uriTransformer) {
327
value = transformIncomingURIs(value, this._uriTransformer);
328
}
329
this._receiveReply(msgLength, req, value);
330
break;
331
}
332
case MessageType.ReplyOKJSONWithBuffers: {
333
const value = MessageIO.deserializeReplyOKJSONWithBuffers(buff, this._uriTransformer);
334
this._receiveReply(msgLength, req, value);
335
break;
336
}
337
case MessageType.ReplyOKVSBuffer: {
338
const value = MessageIO.deserializeReplyOKVSBuffer(buff);
339
this._receiveReply(msgLength, req, value);
340
break;
341
}
342
case MessageType.ReplyErrError: {
343
let err = MessageIO.deserializeReplyErrError(buff);
344
if (this._uriTransformer) {
345
err = transformIncomingURIs(err, this._uriTransformer);
346
}
347
this._receiveReplyErr(msgLength, req, err);
348
break;
349
}
350
case MessageType.ReplyErrEmpty: {
351
this._receiveReplyErr(msgLength, req, undefined);
352
break;
353
}
354
default:
355
console.error(`received unexpected message`);
356
console.error(rawmsg);
357
}
358
}
359
360
private _receiveRequest(msgLength: number, req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean): void {
361
this._logger?.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveRequest ${getStringIdentifierForProxy(rpcId)}.${method}(`, args);
362
const callId = String(req);
363
364
let promise: Promise<any>;
365
let cancel: () => void;
366
if (usesCancellationToken) {
367
const cancellationTokenSource = new CancellationTokenSource();
368
args.push(cancellationTokenSource.token);
369
promise = this._invokeHandler(rpcId, method, args);
370
cancel = () => cancellationTokenSource.cancel();
371
} else {
372
// cannot be cancelled
373
promise = this._invokeHandler(rpcId, method, args);
374
cancel = noop;
375
}
376
377
this._cancelInvokedHandlers[callId] = cancel;
378
379
// Acknowledge the request
380
const msg = MessageIO.serializeAcknowledged(req);
381
this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `ack`);
382
this._protocol.send(msg);
383
384
promise.then((r) => {
385
delete this._cancelInvokedHandlers[callId];
386
const msg = MessageIO.serializeReplyOK(req, r, this._uriReplacer);
387
this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `reply:`, r);
388
this._protocol.send(msg);
389
}, (err) => {
390
delete this._cancelInvokedHandlers[callId];
391
const msg = MessageIO.serializeReplyErr(req, err);
392
this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `replyErr:`, err);
393
this._protocol.send(msg);
394
});
395
}
396
397
private _receiveCancel(msgLength: number, req: number): void {
398
this._logger?.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveCancel`);
399
const callId = String(req);
400
this._cancelInvokedHandlers[callId]?.();
401
}
402
403
private _receiveReply(msgLength: number, req: number, value: any): void {
404
this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReply:`, value);
405
const callId = String(req);
406
if (!this._pendingRPCReplies.hasOwnProperty(callId)) {
407
return;
408
}
409
410
const pendingReply = this._pendingRPCReplies[callId];
411
delete this._pendingRPCReplies[callId];
412
413
pendingReply.resolveOk(value);
414
}
415
416
private _receiveReplyErr(msgLength: number, req: number, value: any): void {
417
this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReplyErr:`, value);
418
419
const callId = String(req);
420
if (!this._pendingRPCReplies.hasOwnProperty(callId)) {
421
return;
422
}
423
424
const pendingReply = this._pendingRPCReplies[callId];
425
delete this._pendingRPCReplies[callId];
426
427
let err: any = undefined;
428
if (value) {
429
if (value.$isError) {
430
err = new Error();
431
err.name = value.name;
432
err.message = value.message;
433
err.stack = value.stack;
434
} else {
435
err = value;
436
}
437
}
438
pendingReply.resolveErr(err);
439
}
440
441
private _invokeHandler(rpcId: number, methodName: string, args: any[]): Promise<any> {
442
try {
443
return Promise.resolve(this._doInvokeHandler(rpcId, methodName, args));
444
} catch (err) {
445
return Promise.reject(err);
446
}
447
}
448
449
private _doInvokeHandler(rpcId: number, methodName: string, args: any[]): any {
450
const actor = this._locals[rpcId];
451
if (!actor) {
452
throw new Error('Unknown actor ' + getStringIdentifierForProxy(rpcId));
453
}
454
const method = actor[methodName];
455
if (typeof method !== 'function') {
456
throw new Error('Unknown method ' + methodName + ' on actor ' + getStringIdentifierForProxy(rpcId));
457
}
458
return method.apply(actor, args);
459
}
460
461
private _remoteCall(rpcId: number, methodName: string, args: any[]): Promise<any> {
462
if (this._isDisposed) {
463
return new CanceledLazyPromise();
464
}
465
let cancellationToken: CancellationToken | null = null;
466
if (args.length > 0 && CancellationToken.isCancellationToken(args[args.length - 1])) {
467
cancellationToken = args.pop();
468
}
469
470
if (cancellationToken && cancellationToken.isCancellationRequested) {
471
// No need to do anything...
472
return Promise.reject<any>(errors.canceled());
473
}
474
475
const serializedRequestArguments = MessageIO.serializeRequestArguments(args, this._uriReplacer);
476
477
const req = ++this._lastMessageId;
478
const callId = String(req);
479
const result = new LazyPromise();
480
481
const disposable = new DisposableStore();
482
if (cancellationToken) {
483
disposable.add(cancellationToken.onCancellationRequested(() => {
484
const msg = MessageIO.serializeCancel(req);
485
this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `cancel`);
486
this._protocol.send(msg);
487
}));
488
}
489
490
this._pendingRPCReplies[callId] = new PendingRPCReply(result, disposable);
491
this._onWillSendRequest(req);
492
const msg = MessageIO.serializeRequest(req, rpcId, methodName, serializedRequestArguments, !!cancellationToken);
493
this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args);
494
this._protocol.send(msg);
495
return result;
496
}
497
}
498
499
class PendingRPCReply {
500
constructor(
501
private readonly _promise: LazyPromise,
502
private readonly _disposable: IDisposable
503
) { }
504
505
public resolveOk(value: any): void {
506
this._promise.resolveOk(value);
507
this._disposable.dispose();
508
}
509
510
public resolveErr(err: any): void {
511
this._promise.resolveErr(err);
512
this._disposable.dispose();
513
}
514
}
515
516
class MessageBuffer {
517
518
public static alloc(type: MessageType, req: number, messageSize: number): MessageBuffer {
519
const result = new MessageBuffer(VSBuffer.alloc(messageSize + 1 /* type */ + 4 /* req */), 0);
520
result.writeUInt8(type);
521
result.writeUInt32(req);
522
return result;
523
}
524
525
public static read(buff: VSBuffer, offset: number): MessageBuffer {
526
return new MessageBuffer(buff, offset);
527
}
528
529
private _buff: VSBuffer;
530
private _offset: number;
531
532
public get buffer(): VSBuffer {
533
return this._buff;
534
}
535
536
private constructor(buff: VSBuffer, offset: number) {
537
this._buff = buff;
538
this._offset = offset;
539
}
540
541
public static sizeUInt8(): number {
542
return 1;
543
}
544
545
public static readonly sizeUInt32 = 4;
546
547
public writeUInt8(n: number): void {
548
this._buff.writeUInt8(n, this._offset); this._offset += 1;
549
}
550
551
public readUInt8(): number {
552
const n = this._buff.readUInt8(this._offset); this._offset += 1;
553
return n;
554
}
555
556
public writeUInt32(n: number): void {
557
this._buff.writeUInt32BE(n, this._offset); this._offset += 4;
558
}
559
560
public readUInt32(): number {
561
const n = this._buff.readUInt32BE(this._offset); this._offset += 4;
562
return n;
563
}
564
565
public static sizeShortString(str: VSBuffer): number {
566
return 1 /* string length */ + str.byteLength /* actual string */;
567
}
568
569
public writeShortString(str: VSBuffer): void {
570
this._buff.writeUInt8(str.byteLength, this._offset); this._offset += 1;
571
this._buff.set(str, this._offset); this._offset += str.byteLength;
572
}
573
574
public readShortString(): string {
575
const strByteLength = this._buff.readUInt8(this._offset); this._offset += 1;
576
const strBuff = this._buff.slice(this._offset, this._offset + strByteLength);
577
const str = strBuff.toString(); this._offset += strByteLength;
578
return str;
579
}
580
581
public static sizeLongString(str: VSBuffer): number {
582
return 4 /* string length */ + str.byteLength /* actual string */;
583
}
584
585
public writeLongString(str: VSBuffer): void {
586
this._buff.writeUInt32BE(str.byteLength, this._offset); this._offset += 4;
587
this._buff.set(str, this._offset); this._offset += str.byteLength;
588
}
589
590
public readLongString(): string {
591
const strByteLength = this._buff.readUInt32BE(this._offset); this._offset += 4;
592
const strBuff = this._buff.slice(this._offset, this._offset + strByteLength);
593
const str = strBuff.toString(); this._offset += strByteLength;
594
return str;
595
}
596
597
public writeBuffer(buff: VSBuffer): void {
598
this._buff.writeUInt32BE(buff.byteLength, this._offset); this._offset += 4;
599
this._buff.set(buff, this._offset); this._offset += buff.byteLength;
600
}
601
602
public static sizeVSBuffer(buff: VSBuffer): number {
603
return 4 /* buffer length */ + buff.byteLength /* actual buffer */;
604
}
605
606
public writeVSBuffer(buff: VSBuffer): void {
607
this._buff.writeUInt32BE(buff.byteLength, this._offset); this._offset += 4;
608
this._buff.set(buff, this._offset); this._offset += buff.byteLength;
609
}
610
611
public readVSBuffer(): VSBuffer {
612
const buffLength = this._buff.readUInt32BE(this._offset); this._offset += 4;
613
const buff = this._buff.slice(this._offset, this._offset + buffLength); this._offset += buffLength;
614
return buff;
615
}
616
617
public static sizeMixedArray(arr: readonly MixedArg[]): number {
618
let size = 0;
619
size += 1; // arr length
620
for (let i = 0, len = arr.length; i < len; i++) {
621
const el = arr[i];
622
size += 1; // arg type
623
switch (el.type) {
624
case ArgType.String:
625
size += this.sizeLongString(el.value);
626
break;
627
case ArgType.VSBuffer:
628
size += this.sizeVSBuffer(el.value);
629
break;
630
case ArgType.SerializedObjectWithBuffers:
631
size += this.sizeUInt32; // buffer count
632
size += this.sizeLongString(el.value);
633
for (let i = 0; i < el.buffers.length; ++i) {
634
size += this.sizeVSBuffer(el.buffers[i]);
635
}
636
break;
637
case ArgType.Undefined:
638
// empty...
639
break;
640
}
641
}
642
return size;
643
}
644
645
public writeMixedArray(arr: readonly MixedArg[]): void {
646
this._buff.writeUInt8(arr.length, this._offset); this._offset += 1;
647
for (let i = 0, len = arr.length; i < len; i++) {
648
const el = arr[i];
649
switch (el.type) {
650
case ArgType.String:
651
this.writeUInt8(ArgType.String);
652
this.writeLongString(el.value);
653
break;
654
case ArgType.VSBuffer:
655
this.writeUInt8(ArgType.VSBuffer);
656
this.writeVSBuffer(el.value);
657
break;
658
case ArgType.SerializedObjectWithBuffers:
659
this.writeUInt8(ArgType.SerializedObjectWithBuffers);
660
this.writeUInt32(el.buffers.length);
661
this.writeLongString(el.value);
662
for (let i = 0; i < el.buffers.length; ++i) {
663
this.writeBuffer(el.buffers[i]);
664
}
665
break;
666
case ArgType.Undefined:
667
this.writeUInt8(ArgType.Undefined);
668
break;
669
}
670
}
671
}
672
673
public readMixedArray(): Array<string | VSBuffer | SerializableObjectWithBuffers<any> | undefined> {
674
const arrLen = this._buff.readUInt8(this._offset); this._offset += 1;
675
const arr: Array<string | VSBuffer | SerializableObjectWithBuffers<any> | undefined> = new Array(arrLen);
676
for (let i = 0; i < arrLen; i++) {
677
const argType = <ArgType>this.readUInt8();
678
switch (argType) {
679
case ArgType.String:
680
arr[i] = this.readLongString();
681
break;
682
case ArgType.VSBuffer:
683
arr[i] = this.readVSBuffer();
684
break;
685
case ArgType.SerializedObjectWithBuffers: {
686
const bufferCount = this.readUInt32();
687
const jsonString = this.readLongString();
688
const buffers: VSBuffer[] = [];
689
for (let i = 0; i < bufferCount; ++i) {
690
buffers.push(this.readVSBuffer());
691
}
692
arr[i] = new SerializableObjectWithBuffers(parseJsonAndRestoreBufferRefs(jsonString, buffers, null));
693
break;
694
}
695
case ArgType.Undefined:
696
arr[i] = undefined;
697
break;
698
}
699
}
700
return arr;
701
}
702
}
703
704
const enum SerializedRequestArgumentType {
705
Simple,
706
Mixed,
707
}
708
709
type SerializedRequestArguments =
710
| { readonly type: SerializedRequestArgumentType.Simple; args: string }
711
| { readonly type: SerializedRequestArgumentType.Mixed; args: MixedArg[] };
712
713
714
class MessageIO {
715
716
private static _useMixedArgSerialization(arr: any[]): boolean {
717
for (let i = 0, len = arr.length; i < len; i++) {
718
if (arr[i] instanceof VSBuffer) {
719
return true;
720
}
721
if (arr[i] instanceof SerializableObjectWithBuffers) {
722
return true;
723
}
724
if (typeof arr[i] === 'undefined') {
725
return true;
726
}
727
}
728
return false;
729
}
730
731
public static serializeRequestArguments(args: any[], replacer: JSONStringifyReplacer | null): SerializedRequestArguments {
732
if (this._useMixedArgSerialization(args)) {
733
const massagedArgs: MixedArg[] = [];
734
for (let i = 0, len = args.length; i < len; i++) {
735
const arg = args[i];
736
if (arg instanceof VSBuffer) {
737
massagedArgs[i] = { type: ArgType.VSBuffer, value: arg };
738
} else if (typeof arg === 'undefined') {
739
massagedArgs[i] = { type: ArgType.Undefined };
740
} else if (arg instanceof SerializableObjectWithBuffers) {
741
const { jsonString, referencedBuffers } = stringifyJsonWithBufferRefs(arg.value, replacer);
742
massagedArgs[i] = { type: ArgType.SerializedObjectWithBuffers, value: VSBuffer.fromString(jsonString), buffers: referencedBuffers };
743
} else {
744
massagedArgs[i] = { type: ArgType.String, value: VSBuffer.fromString(stringify(arg, replacer)) };
745
}
746
}
747
return {
748
type: SerializedRequestArgumentType.Mixed,
749
args: massagedArgs,
750
};
751
}
752
return {
753
type: SerializedRequestArgumentType.Simple,
754
args: stringify(args, replacer)
755
};
756
}
757
758
public static serializeRequest(req: number, rpcId: number, method: string, serializedArgs: SerializedRequestArguments, usesCancellationToken: boolean): VSBuffer {
759
switch (serializedArgs.type) {
760
case SerializedRequestArgumentType.Simple:
761
return this._requestJSONArgs(req, rpcId, method, serializedArgs.args, usesCancellationToken);
762
case SerializedRequestArgumentType.Mixed:
763
return this._requestMixedArgs(req, rpcId, method, serializedArgs.args, usesCancellationToken);
764
}
765
}
766
767
private static _requestJSONArgs(req: number, rpcId: number, method: string, args: string, usesCancellationToken: boolean): VSBuffer {
768
const methodBuff = VSBuffer.fromString(method);
769
const argsBuff = VSBuffer.fromString(args);
770
771
let len = 0;
772
len += MessageBuffer.sizeUInt8();
773
len += MessageBuffer.sizeShortString(methodBuff);
774
len += MessageBuffer.sizeLongString(argsBuff);
775
776
const result = MessageBuffer.alloc(usesCancellationToken ? MessageType.RequestJSONArgsWithCancellation : MessageType.RequestJSONArgs, req, len);
777
result.writeUInt8(rpcId);
778
result.writeShortString(methodBuff);
779
result.writeLongString(argsBuff);
780
return result.buffer;
781
}
782
783
public static deserializeRequestJSONArgs(buff: MessageBuffer): { rpcId: number; method: string; args: any[] } {
784
const rpcId = buff.readUInt8();
785
const method = buff.readShortString();
786
const args = buff.readLongString();
787
return {
788
rpcId: rpcId,
789
method: method,
790
args: JSON.parse(args)
791
};
792
}
793
794
private static _requestMixedArgs(req: number, rpcId: number, method: string, args: readonly MixedArg[], usesCancellationToken: boolean): VSBuffer {
795
const methodBuff = VSBuffer.fromString(method);
796
797
let len = 0;
798
len += MessageBuffer.sizeUInt8();
799
len += MessageBuffer.sizeShortString(methodBuff);
800
len += MessageBuffer.sizeMixedArray(args);
801
802
const result = MessageBuffer.alloc(usesCancellationToken ? MessageType.RequestMixedArgsWithCancellation : MessageType.RequestMixedArgs, req, len);
803
result.writeUInt8(rpcId);
804
result.writeShortString(methodBuff);
805
result.writeMixedArray(args);
806
return result.buffer;
807
}
808
809
public static deserializeRequestMixedArgs(buff: MessageBuffer): { rpcId: number; method: string; args: any[] } {
810
const rpcId = buff.readUInt8();
811
const method = buff.readShortString();
812
const rawargs = buff.readMixedArray();
813
const args: any[] = new Array(rawargs.length);
814
for (let i = 0, len = rawargs.length; i < len; i++) {
815
const rawarg = rawargs[i];
816
if (typeof rawarg === 'string') {
817
args[i] = JSON.parse(rawarg);
818
} else {
819
args[i] = rawarg;
820
}
821
}
822
return {
823
rpcId: rpcId,
824
method: method,
825
args: args
826
};
827
}
828
829
public static serializeAcknowledged(req: number): VSBuffer {
830
return MessageBuffer.alloc(MessageType.Acknowledged, req, 0).buffer;
831
}
832
833
public static serializeCancel(req: number): VSBuffer {
834
return MessageBuffer.alloc(MessageType.Cancel, req, 0).buffer;
835
}
836
837
public static serializeReplyOK(req: number, res: any, replacer: JSONStringifyReplacer | null): VSBuffer {
838
if (typeof res === 'undefined') {
839
return this._serializeReplyOKEmpty(req);
840
} else if (res instanceof VSBuffer) {
841
return this._serializeReplyOKVSBuffer(req, res);
842
} else if (res instanceof SerializableObjectWithBuffers) {
843
const { jsonString, referencedBuffers } = stringifyJsonWithBufferRefs(res.value, replacer, true);
844
return this._serializeReplyOKJSONWithBuffers(req, jsonString, referencedBuffers);
845
} else {
846
return this._serializeReplyOKJSON(req, safeStringify(res, replacer));
847
}
848
}
849
850
private static _serializeReplyOKEmpty(req: number): VSBuffer {
851
return MessageBuffer.alloc(MessageType.ReplyOKEmpty, req, 0).buffer;
852
}
853
854
private static _serializeReplyOKVSBuffer(req: number, res: VSBuffer): VSBuffer {
855
let len = 0;
856
len += MessageBuffer.sizeVSBuffer(res);
857
858
const result = MessageBuffer.alloc(MessageType.ReplyOKVSBuffer, req, len);
859
result.writeVSBuffer(res);
860
return result.buffer;
861
}
862
863
public static deserializeReplyOKVSBuffer(buff: MessageBuffer): VSBuffer {
864
return buff.readVSBuffer();
865
}
866
867
private static _serializeReplyOKJSON(req: number, res: string): VSBuffer {
868
const resBuff = VSBuffer.fromString(res);
869
870
let len = 0;
871
len += MessageBuffer.sizeLongString(resBuff);
872
873
const result = MessageBuffer.alloc(MessageType.ReplyOKJSON, req, len);
874
result.writeLongString(resBuff);
875
return result.buffer;
876
}
877
878
private static _serializeReplyOKJSONWithBuffers(req: number, res: string, buffers: readonly VSBuffer[]): VSBuffer {
879
const resBuff = VSBuffer.fromString(res);
880
881
let len = 0;
882
len += MessageBuffer.sizeUInt32; // buffer count
883
len += MessageBuffer.sizeLongString(resBuff);
884
for (const buffer of buffers) {
885
len += MessageBuffer.sizeVSBuffer(buffer);
886
}
887
888
const result = MessageBuffer.alloc(MessageType.ReplyOKJSONWithBuffers, req, len);
889
result.writeUInt32(buffers.length);
890
result.writeLongString(resBuff);
891
for (const buffer of buffers) {
892
result.writeBuffer(buffer);
893
}
894
895
return result.buffer;
896
}
897
898
public static deserializeReplyOKJSON(buff: MessageBuffer): any {
899
const res = buff.readLongString();
900
return JSON.parse(res);
901
}
902
903
public static deserializeReplyOKJSONWithBuffers(buff: MessageBuffer, uriTransformer: IURITransformer | null): SerializableObjectWithBuffers<any> {
904
const bufferCount = buff.readUInt32();
905
const res = buff.readLongString();
906
907
const buffers: VSBuffer[] = [];
908
for (let i = 0; i < bufferCount; ++i) {
909
buffers.push(buff.readVSBuffer());
910
}
911
912
return new SerializableObjectWithBuffers(parseJsonAndRestoreBufferRefs(res, buffers, uriTransformer));
913
}
914
915
public static serializeReplyErr(req: number, err: any): VSBuffer {
916
const errStr: string | undefined = (err ? safeStringify(errors.transformErrorForSerialization(err), null) : undefined);
917
if (typeof errStr !== 'string') {
918
return this._serializeReplyErrEmpty(req);
919
}
920
const errBuff = VSBuffer.fromString(errStr);
921
922
let len = 0;
923
len += MessageBuffer.sizeLongString(errBuff);
924
925
const result = MessageBuffer.alloc(MessageType.ReplyErrError, req, len);
926
result.writeLongString(errBuff);
927
return result.buffer;
928
}
929
930
public static deserializeReplyErrError(buff: MessageBuffer): Error {
931
const err = buff.readLongString();
932
return JSON.parse(err);
933
}
934
935
private static _serializeReplyErrEmpty(req: number): VSBuffer {
936
return MessageBuffer.alloc(MessageType.ReplyErrEmpty, req, 0).buffer;
937
}
938
}
939
940
const enum MessageType {
941
RequestJSONArgs = 1,
942
RequestJSONArgsWithCancellation = 2,
943
RequestMixedArgs = 3,
944
RequestMixedArgsWithCancellation = 4,
945
Acknowledged = 5,
946
Cancel = 6,
947
ReplyOKEmpty = 7,
948
ReplyOKVSBuffer = 8,
949
ReplyOKJSON = 9,
950
ReplyOKJSONWithBuffers = 10,
951
ReplyErrError = 11,
952
ReplyErrEmpty = 12,
953
}
954
955
const enum ArgType {
956
String = 1,
957
VSBuffer = 2,
958
SerializedObjectWithBuffers = 3,
959
Undefined = 4,
960
}
961
962
963
type MixedArg =
964
| { readonly type: ArgType.String; readonly value: VSBuffer }
965
| { readonly type: ArgType.VSBuffer; readonly value: VSBuffer }
966
| { readonly type: ArgType.SerializedObjectWithBuffers; readonly value: VSBuffer; readonly buffers: readonly VSBuffer[] }
967
| { readonly type: ArgType.Undefined }
968
;
969
970