chore(vscode): update to 1.53.2

These conflicts will be resolved in the following commits. We do it this way so
that PR review is possible.
This commit is contained in:
Joe Previte
2021-02-25 11:27:27 -07:00
1900 changed files with 83066 additions and 64589 deletions

View File

@@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { Client as MessagePortClient } from 'vs/base/parts/ipc/common/ipc.mp';
/**
* An implementation of a `IPCClient` on top of DOM `MessagePort`.
*/
export class Client extends MessagePortClient implements IDisposable {
/**
* @param clientId a way to uniquely identify this client among
* other clients. this is important for routing because every
* client can also be a server
*/
constructor(port: MessagePort, clientId: string) {
super(port, clientId);
}
}

View File

@@ -11,6 +11,11 @@ export interface Sender {
send(channel: string, msg: unknown): void;
}
/**
* The Electron `Protocol` leverages Electron style IPC communication (`ipcRenderer`, `ipcMain`)
* for the implementation of the `IMessagePassingProtocol`. That style of API requires a channel
* name for sending data.
*/
export class Protocol implements IMessagePassingProtocol {
constructor(private sender: Sender, readonly onMessage: Event<VSBuffer>) { }
@@ -23,7 +28,7 @@ export class Protocol implements IMessagePassingProtocol {
}
}
dispose(): void {
disconnect(): void {
this.sender.send('vscode:disconnect', null);
}
}

View File

@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { IMessagePassingProtocol, IPCClient } from 'vs/base/parts/ipc/common/ipc';
import { IDisposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
/**
* Declare minimal `MessageEvent` and `MessagePort` interfaces here
* so that this utility can be used both from `browser` and
* `electron-main` namespace where message ports are available.
*/
export interface MessageEvent {
/**
* For our use we only consider `Uint8Array` a valid data transfer
* via message ports because our protocol implementation is buffer based.
*/
data: Uint8Array;
}
export interface MessagePort {
addEventListener(type: 'message', listener: (this: MessagePort, e: MessageEvent) => unknown): void;
removeEventListener(type: 'message', listener: (this: MessagePort, e: MessageEvent) => unknown): void;
postMessage(message: Uint8Array): void;
start(): void;
close(): void;
}
/**
* The MessagePort `Protocol` leverages MessagePort style IPC communication
* for the implementation of the `IMessagePassingProtocol`. That style of API
* is a simple `onmessage` / `postMessage` pattern.
*/
export class Protocol implements IMessagePassingProtocol {
readonly onMessage = Event.fromDOMEventEmitter<VSBuffer>(this.port, 'message', (e: MessageEvent) => VSBuffer.wrap(e.data));
constructor(private port: MessagePort) {
// we must call start() to ensure messages are flowing
port.start();
}
send(message: VSBuffer): void {
this.port.postMessage(message.buffer);
}
disconnect(): void {
this.port.close();
}
}
/**
* An implementation of a `IPCClient` on top of MessagePort style IPC communication.
*/
export class Client extends IPCClient implements IDisposable {
private protocol: Protocol;
constructor(port: MessagePort, clientId: string) {
const protocol = new Protocol(port);
super(protocol, clientId);
this.protocol = protocol;
}
dispose(): void {
this.protocol.disconnect();
}
}

View File

@@ -365,8 +365,8 @@ export class Protocol extends Disposable implements IMessagePassingProtocol {
private readonly _onMessage = new Emitter<VSBuffer>();
readonly onMessage: Event<VSBuffer> = this._onMessage.event;
private readonly _onClose = new Emitter<void>();
readonly onClose: Event<void> = this._onClose.event;
private readonly _onDidDispose = new Emitter<void>();
readonly onDidDispose: Event<void> = this._onDidDispose.event;
constructor(socket: ISocket) {
super();
@@ -380,7 +380,7 @@ export class Protocol extends Disposable implements IMessagePassingProtocol {
}
}));
this._register(this._socket.onClose(() => this._onClose.fire()));
this._register(this._socket.onClose(() => this._onDidDispose.fire()));
}
drain(): Promise<void> {
@@ -406,7 +406,7 @@ export class Client<TContext = string> extends IPCClient<TContext> {
return new Client(new Protocol(socket), id);
}
get onClose(): Event<void> { return this.protocol.onClose; }
get onDidDispose(): Event<void> { return this.protocol.onDidDispose; }
constructor(private protocol: Protocol | PersistentProtocol, id: TContext, ipcLogger: IIPCLogger | null = null) {
super(protocol, id, ipcLogger);
@@ -621,8 +621,8 @@ export class PersistentProtocol implements IMessagePassingProtocol {
private readonly _onMessage = new BufferedEmitter<VSBuffer>();
readonly onMessage: Event<VSBuffer> = this._onMessage.event;
private readonly _onClose = new BufferedEmitter<void>();
readonly onClose: Event<void> = this._onClose.event;
private readonly _onDidDispose = new BufferedEmitter<void>();
readonly onDidDispose: Event<void> = this._onDidDispose.event;
private readonly _onSocketClose = new BufferedEmitter<void>();
readonly onSocketClose: Event<void> = this._onSocketClose.event;
@@ -747,6 +747,10 @@ export class PersistentProtocol implements IMessagePassingProtocol {
return this._socket;
}
public getMillisSinceLastIncomingData(): number {
return Date.now() - this._socketReader.lastReadTime;
}
public beginAcceptReconnection(socket: ISocket, initialDataChunk: VSBuffer | null): void {
this._isReconnecting = true;
@@ -783,7 +787,7 @@ export class PersistentProtocol implements IMessagePassingProtocol {
}
public acceptDisconnect(): void {
this._onClose.fire();
this._onDidDispose.fire();
}
private _receiveMessage(msg: ProtocolMessage): void {
@@ -820,7 +824,7 @@ export class PersistentProtocol implements IMessagePassingProtocol {
} else if (msg.type === ProtocolMessageType.Control) {
this._onControlMessage.fire(msg.data);
} else if (msg.type === ProtocolMessageType.Disconnect) {
this._onClose.fire();
this._onDidDispose.fire();
} else if (msg.type === ProtocolMessageType.ReplayRequest) {
// Send again all unacknowledged messages
const toSend = this._outgoingUnackMsg.toArray();

View File

@@ -505,6 +505,7 @@ export interface IIPCLogger {
export class ChannelClient implements IChannelClient, IDisposable {
private isDisposed: boolean = false;
private state: State = State.Uninitialized;
private activeRequests = new Set<IDisposable>();
private handlers = new Map<number, IHandler>();
@@ -525,9 +526,15 @@ export class ChannelClient implements IChannelClient, IDisposable {
return {
call(command: string, arg?: any, cancellationToken?: CancellationToken) {
if (that.isDisposed) {
return Promise.reject(errors.canceled());
}
return that.requestPromise(channelName, command, arg, cancellationToken);
},
listen(event: string, arg: any) {
if (that.isDisposed) {
return Promise.reject(errors.canceled());
}
return that.requestEvent(channelName, event, arg);
}
} as T;
@@ -725,6 +732,7 @@ export class ChannelClient implements IChannelClient, IDisposable {
}
dispose(): void {
this.isDisposed = true;
if (this.protocolListener) {
this.protocolListener.dispose();
this.protocolListener = null;

View File

@@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ipcMain, WebContents } from 'electron';
import { Event, Emitter } from 'vs/base/common/event';
import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/common/ipc';
import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron';
import { ipcMain, WebContents } from 'electron';
import { Protocol as ElectronProtocol } from 'vs/base/parts/ipc/common/ipc.electron';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
@@ -18,9 +18,13 @@ interface IIPCEvent {
function createScopedOnMessageEvent(senderId: number, eventName: string): Event<VSBuffer | null> {
const onMessage = Event.fromNodeEventEmitter<IIPCEvent>(ipcMain, eventName, (event, message) => ({ event, message }));
const onMessageFromSender = Event.filter(onMessage, ({ event }) => event.sender.id === senderId);
return Event.map(onMessageFromSender, ({ message }) => message ? VSBuffer.wrap(message) : message);
}
/**
* An implemention of `IPCServer` on top of Electron `ipcMain` API.
*/
export class Server extends IPCServer {
private static readonly Clients = new Map<number, IDisposable>();
@@ -41,7 +45,7 @@ export class Server extends IPCServer {
const onMessage = createScopedOnMessageEvent(id, 'vscode:message') as Event<VSBuffer>;
const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'vscode:disconnect')), onDidClientReconnect.event);
const protocol = new Protocol(webContents, onMessage);
const protocol = new ElectronProtocol(webContents, onMessage);
return { protocol, onDidClientDisconnect };
});

View File

@@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BrowserWindow, ipcMain, IpcMainEvent, MessagePortMain } from 'electron';
import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { generateUuid } from 'vs/base/common/uuid';
import { Client as MessagePortClient } from 'vs/base/parts/ipc/common/ipc.mp';
/**
* An implementation of a `IPCClient` on top of Electron `MessagePortMain`.
*/
export class Client extends MessagePortClient implements IDisposable {
/**
* @param clientId a way to uniquely identify this client among
* other clients. this is important for routing because every
* client can also be a server
*/
constructor(port: MessagePortMain, clientId: string) {
super({
addEventListener: (type, listener) => port.addListener(type, listener),
removeEventListener: (type, listener) => port.removeListener(type, listener),
postMessage: message => port.postMessage(message),
start: () => port.start(),
close: () => port.close()
}, clientId);
}
}
/**
* This method opens a message channel connection
* in the target window. The target window needs
* to use the `Server` from `electron-sandbox/ipc.mp`.
*/
export async function connect(window: BrowserWindow): Promise<MessagePortMain> {
// Assert healthy window to talk to
if (window.isDestroyed() || window.webContents.isDestroyed()) {
throw new Error('ipc.mp#connect: Cannot talk to window because it is closed or destroyed');
}
// Ask to create message channel inside the window
// and send over a UUID to correlate the response
const nonce = generateUuid();
window.webContents.send('vscode:createMessageChannel', nonce);
// Wait until the window has returned the `MessagePort`
// We need to filter by the `nonce` to ensure we listen
// to the right response.
const onMessageChannelResult = Event.fromNodeEventEmitter<{ nonce: string, port: MessagePortMain }>(ipcMain, 'vscode:createMessageChannelResult', (e: IpcMainEvent, nonce: string) => ({ nonce, port: e.ports[0] }));
const { port } = await Event.toPromise(Event.once(Event.filter(onMessageChannelResult, e => e.nonce === nonce)));
return port;
}

View File

@@ -5,28 +5,34 @@
import { Event } from 'vs/base/common/event';
import { IPCClient } from 'vs/base/parts/ipc/common/ipc';
import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron';
import { Protocol as ElectronProtocol } from 'vs/base/parts/ipc/common/ipc.electron';
import { IDisposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
/**
* An implemention of `IPCClient` on top of Electron `ipcRenderer` IPC communication
* provided from sandbox globals (via preload script).
*/
export class Client extends IPCClient implements IDisposable {
private protocol: Protocol;
private protocol: ElectronProtocol;
private static createProtocol(): Protocol {
private static createProtocol(): ElectronProtocol {
const onMessage = Event.fromNodeEventEmitter<VSBuffer>(ipcRenderer, 'vscode:message', (_, message) => VSBuffer.wrap(message));
ipcRenderer.send('vscode:hello');
return new Protocol(ipcRenderer, onMessage);
return new ElectronProtocol(ipcRenderer, onMessage);
}
constructor(id: string) {
const protocol = Client.createProtocol();
super(protocol, id);
this.protocol = protocol;
}
dispose(): void {
this.protocol.dispose();
this.protocol.disconnect();
}
}

View File

@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { Event } from 'vs/base/common/event';
import { ClientConnectionEvent, IPCServer } from 'vs/base/parts/ipc/common/ipc';
import { Protocol as MessagePortProtocol } from 'vs/base/parts/ipc/common/ipc.mp';
/**
* An implementation of a `IPCServer` on top of MessagePort style IPC communication.
* The clients register themselves via Electron IPC transfer.
*/
export class Server extends IPCServer {
private static getOnDidClientConnect(): Event<ClientConnectionEvent> {
// Clients connect via `vscode:createMessageChannel` to get a
// `MessagePort` that is ready to be used. For every connection
// we create a pair of message ports and send it back.
//
// The `nonce` is included so that the main side has a chance to
// correlate the response back to the sender.
const onCreateMessageChannel = Event.fromNodeEventEmitter<string>(ipcRenderer, 'vscode:createMessageChannel', (_, nonce: string) => nonce);
return Event.map(onCreateMessageChannel, nonce => {
// Create a new pair of ports and protocol for this connection
const { port1: incomingPort, port2: outgoingPort } = new MessageChannel();
const protocol = new MessagePortProtocol(incomingPort);
const result: ClientConnectionEvent = {
protocol,
// Not part of the standard spec, but in Electron we get a `close` event
// when the other side closes. We can use this to detect disconnects
// (https://github.com/electron/electron/blob/11-x-y/docs/api/message-port-main.md#event-close)
onDidClientDisconnect: Event.fromDOMEventEmitter(incomingPort, 'close')
};
// Send one port back to the requestor
ipcRenderer.postMessage('vscode:createMessageChannelResult', nonce, [outgoingPort]);
return result;
});
}
constructor() {
super(Server.getOnDidClientConnect());
}
}

View File

@@ -5,6 +5,7 @@
import { createHash } from 'crypto';
import { Socket, Server as NetServer, createConnection, createServer } from 'net';
import * as zlib from 'zlib';
import { Event, Emitter } from 'vs/base/common/event';
import { ClientConnectionEvent, IPCServer } from 'vs/base/parts/ipc/common/ipc';
import { join } from 'vs/base/common/path';
@@ -120,24 +121,130 @@ const enum ReadState {
export class WebSocketNodeSocket extends Disposable implements ISocket {
public readonly socket: NodeSocket;
public readonly permessageDeflate: boolean;
private _totalIncomingWireBytes: number;
private _totalIncomingDataBytes: number;
private _totalOutgoingWireBytes: number;
private _totalOutgoingDataBytes: number;
private readonly _zlibInflate: zlib.InflateRaw | null;
private readonly _zlibDeflate: zlib.DeflateRaw | null;
private _zlibDeflateFlushWaitingCount: number;
private readonly _onDidZlibFlush = this._register(new Emitter<void>());
private readonly _recordInflateBytes: boolean;
private readonly _recordedInflateBytes: Buffer[] = [];
private readonly _pendingInflateData: Buffer[] = [];
private readonly _pendingDeflateData: Buffer[] = [];
private readonly _incomingData: ChunkStream;
private readonly _onData = this._register(new Emitter<VSBuffer>());
private readonly _onClose = this._register(new Emitter<void>());
private _isEnded: boolean = false;
private readonly _state = {
state: ReadState.PeekHeader,
readLen: Constants.MinHeaderByteSize,
fin: 0,
mask: 0
};
constructor(socket: NodeSocket) {
public get totalIncomingWireBytes(): number {
return this._totalIncomingWireBytes;
}
public get totalIncomingDataBytes(): number {
return this._totalIncomingDataBytes;
}
public get totalOutgoingWireBytes(): number {
return this._totalOutgoingWireBytes;
}
public get totalOutgoingDataBytes(): number {
return this._totalOutgoingDataBytes;
}
public get recordedInflateBytes(): VSBuffer {
if (this._recordInflateBytes) {
return VSBuffer.wrap(Buffer.concat(this._recordedInflateBytes));
}
return VSBuffer.alloc(0);
}
/**
* Create a socket which can communicate using WebSocket frames.
*
* **NOTE**: When using the permessage-deflate WebSocket extension, if parts of inflating was done
* in a different zlib instance, we need to pass all those bytes into zlib, otherwise the inflate
* might hit an inflated portion referencing a distance too far back.
*
* @param socket The underlying socket
* @param permessageDeflate Use the permessage-deflate WebSocket extension
* @param inflateBytes "Seed" zlib inflate with these bytes.
* @param recordInflateBytes Record all bytes sent to inflate
*/
constructor(socket: NodeSocket, permessageDeflate: boolean, inflateBytes: VSBuffer | null, recordInflateBytes: boolean) {
super();
this.socket = socket;
this._totalIncomingWireBytes = 0;
this._totalIncomingDataBytes = 0;
this._totalOutgoingWireBytes = 0;
this._totalOutgoingDataBytes = 0;
this.permessageDeflate = permessageDeflate;
this._recordInflateBytes = recordInflateBytes;
if (permessageDeflate) {
// See https://tools.ietf.org/html/rfc7692#page-16
// To simplify our logic, we don't negociate the window size
// and simply dedicate (2^15) / 32kb per web socket
this._zlibInflate = zlib.createInflateRaw({
windowBits: 15
});
this._zlibInflate.on('error', (err) => {
// zlib errors are fatal, since we have no idea how to recover
console.error(err);
onUnexpectedError(err);
this._onClose.fire();
});
this._zlibInflate.on('data', (data: Buffer) => {
this._pendingInflateData.push(data);
});
if (inflateBytes) {
this._zlibInflate.write(inflateBytes.buffer);
this._zlibInflate.flush(() => {
this._pendingInflateData.length = 0;
});
}
this._zlibDeflate = zlib.createDeflateRaw({
windowBits: 15
});
this._zlibDeflate.on('error', (err) => {
// zlib errors are fatal, since we have no idea how to recover
console.error(err);
onUnexpectedError(err);
this._onClose.fire();
});
this._zlibDeflate.on('data', (data: Buffer) => {
this._pendingDeflateData.push(data);
});
} else {
this._zlibInflate = null;
this._zlibDeflate = null;
}
this._zlibDeflateFlushWaitingCount = 0;
this._incomingData = new ChunkStream();
this._register(this.socket.onData(data => this._acceptChunk(data)));
this._register(this.socket.onClose(() => this._onClose.fire()));
}
public dispose(): void {
this.socket.dispose();
if (this._zlibDeflateFlushWaitingCount > 0) {
// Wait for any outstanding writes to finish before disposing
this._register(this._onDidZlibFlush.event(() => {
this.dispose();
}));
} else {
this.socket.dispose();
super.dispose();
}
}
public onData(listener: (e: VSBuffer) => void): IDisposable {
@@ -145,7 +252,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
}
public onClose(listener: () => void): IDisposable {
return this.socket.onClose(listener);
return this._onClose.event(listener);
}
public onEnd(listener: () => void): IDisposable {
@@ -153,6 +260,36 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
}
public write(buffer: VSBuffer): void {
this._totalOutgoingDataBytes += buffer.byteLength;
if (this._zlibDeflate) {
this._zlibDeflate.write(<Buffer>buffer.buffer);
this._zlibDeflateFlushWaitingCount++;
// See https://zlib.net/manual.html#Constants
this._zlibDeflate.flush(/*Z_SYNC_FLUSH*/2, () => {
this._zlibDeflateFlushWaitingCount--;
let data = Buffer.concat(this._pendingDeflateData);
this._pendingDeflateData.length = 0;
// See https://tools.ietf.org/html/rfc7692#section-7.2.1
data = data.slice(0, data.length - 4);
if (!this._isEnded) {
// Avoid ERR_STREAM_WRITE_AFTER_END
this._write(VSBuffer.wrap(data), true);
}
if (this._zlibDeflateFlushWaitingCount === 0) {
this._onDidZlibFlush.fire();
}
});
} else {
this._write(buffer, false);
}
}
private _write(buffer: VSBuffer, compressed: boolean): void {
let headerLen = Constants.MinHeaderByteSize;
if (buffer.byteLength < 126) {
headerLen += 0;
@@ -163,7 +300,12 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
}
const header = VSBuffer.alloc(headerLen);
header.writeUInt8(0b10000010, 0);
if (compressed) {
// The RSV1 bit indicates a compressed frame
header.writeUInt8(0b11000010, 0);
} else {
header.writeUInt8(0b10000010, 0);
}
if (buffer.byteLength < 126) {
header.writeUInt8(buffer.byteLength, 1);
} else if (buffer.byteLength < 2 ** 16) {
@@ -184,10 +326,12 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
header.writeUInt8((buffer.byteLength >>> 0) & 0b11111111, ++offset);
}
this._totalOutgoingWireBytes += header.byteLength + buffer.byteLength;
this.socket.write(VSBuffer.concat([header, buffer]));
}
public end(): void {
this._isEnded = true;
this.socket.end();
}
@@ -195,6 +339,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
if (data.byteLength === 0) {
return;
}
this._totalIncomingWireBytes += data.byteLength;
this._incomingData.acceptChunk(data);
@@ -203,14 +348,15 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
if (this._state.state === ReadState.PeekHeader) {
// peek to see if we can read the entire header
const peekHeader = this._incomingData.peek(this._state.readLen);
// const firstByte = peekHeader.readUInt8(0);
// const finBit = (firstByte & 0b10000000) >>> 7;
const firstByte = peekHeader.readUInt8(0);
const finBit = (firstByte & 0b10000000) >>> 7;
const secondByte = peekHeader.readUInt8(1);
const hasMask = (secondByte & 0b10000000) >>> 7;
const len = (secondByte & 0b01111111);
this._state.state = ReadState.ReadHeader;
this._state.readLen = Constants.MinHeaderByteSize + (hasMask ? 4 : 0) + (len === 126 ? 2 : 0) + (len === 127 ? 8 : 0);
this._state.fin = finBit;
this._state.mask = 0;
} else if (this._state.state === ReadState.ReadHeader) {
@@ -263,13 +409,37 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
this._state.readLen = Constants.MinHeaderByteSize;
this._state.mask = 0;
this._onData.fire(body);
if (this._zlibInflate) {
// See https://tools.ietf.org/html/rfc7692#section-7.2.2
if (this._recordInflateBytes) {
this._recordedInflateBytes.push(Buffer.from(<Buffer>body.buffer));
}
this._zlibInflate.write(<Buffer>body.buffer);
if (this._state.fin) {
if (this._recordInflateBytes) {
this._recordedInflateBytes.push(Buffer.from([0x00, 0x00, 0xff, 0xff]));
}
this._zlibInflate.write(Buffer.from([0x00, 0x00, 0xff, 0xff]));
}
this._zlibInflate.flush(() => {
const data = Buffer.concat(this._pendingInflateData);
this._pendingInflateData.length = 0;
this._totalIncomingDataBytes += data.length;
this._onData.fire(VSBuffer.wrap(data));
});
} else {
this._totalIncomingDataBytes += body.byteLength;
this._onData.fire(body);
}
}
}
}
public drain(): Promise<void> {
return this.socket.drain();
public async drain(): Promise<void> {
if (this._zlibDeflateFlushWaitingCount > 0) {
await Event.toPromise(this._onDidZlibFlush.event);
}
await this.socket.drain();
}
}

View File

@@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Event } from 'vs/base/common/event';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Client as MessagePortClient } from 'vs/base/parts/ipc/browser/ipc.mp';
suite('IPC, MessagePorts', () => {
test('message passing', async () => {
const { port1, port2 } = new MessageChannel();
const client1 = new MessagePortClient(port1, 'client1');
const client2 = new MessagePortClient(port2, 'client2');
client1.registerChannel('client1', {
call(_: unknown, command: string, arg: any, cancellationToken: CancellationToken): Promise<any> {
switch (command) {
case 'testMethodClient1': return Promise.resolve('success1');
default: return Promise.reject(new Error('not implemented'));
}
},
listen(_: unknown, event: string, arg?: any): Event<any> {
switch (event) {
default: throw new Error('not implemented');
}
}
});
client2.registerChannel('client2', {
call(_: unknown, command: string, arg: any, cancellationToken: CancellationToken): Promise<any> {
switch (command) {
case 'testMethodClient2': return Promise.resolve('success2');
default: return Promise.reject(new Error('not implemented'));
}
},
listen(_: unknown, event: string, arg?: any): Event<any> {
switch (event) {
default: throw new Error('not implemented');
}
}
});
const channelClient1 = client2.getChannel('client1');
assert.strictEqual(await channelClient1.call('testMethodClient1'), 'success1');
const channelClient2 = client1.getChannel('client2');
assert.strictEqual(await channelClient2.call('testMethodClient2'), 'success2');
client1.dispose();
client2.dispose();
});
});

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Client as MessagePortClient } from 'vs/base/parts/ipc/browser/ipc.mp';
suite('IPC, MessagePorts', () => {
test('message port close event', async () => {
const { port1, port2 } = new MessageChannel();
new MessagePortClient(port1, 'client1');
const client2 = new MessagePortClient(port2, 'client2');
// This test ensures that Electron's API for the close event
// does not break because we rely on it to dispose client
// connections from the server.
//
// This event is not provided by browser MessagePort API though.
const whenClosed = new Promise<boolean>(resolve => port1.addEventListener('close', () => resolve(true)));
client2.dispose();
assert.ok(await whenClosed);
});
});

View File

@@ -11,7 +11,7 @@ import { getPathFromAmdModule } from 'vs/base/common/amd';
function createClient(): Client {
return new Client(getPathFromAmdModule(require, 'bootstrap-fork'), {
serverName: 'TestServer',
env: { AMD_ENTRYPOINT: 'vs/base/parts/ipc/test/node/testApp', verbose: true }
env: { VSCODE_AMD_ENTRYPOINT: 'vs/base/parts/ipc/test/node/testApp', verbose: true }
});
}

View File

@@ -135,13 +135,13 @@ suite('IPC, Socket Protocol', () => {
a.send(VSBuffer.fromString('foobarfarboo'));
const msg1 = await bMessages.waitForOne();
assert.equal(msg1.toString(), 'foobarfarboo');
assert.strictEqual(msg1.toString(), 'foobarfarboo');
const buffer = VSBuffer.alloc(1);
buffer.writeUInt8(123, 0);
a.send(buffer);
const msg2 = await bMessages.waitForOne();
assert.equal(msg2.readUInt8(0), 123);
assert.strictEqual(msg2.readUInt8(0), 123);
});
@@ -160,7 +160,7 @@ suite('IPC, Socket Protocol', () => {
a.send(VSBuffer.fromString(JSON.stringify(data)));
const msg = await bMessages.waitForOne();
assert.deepEqual(JSON.parse(msg.toString()), data);
assert.deepStrictEqual(JSON.parse(msg.toString()), data);
});
});
@@ -179,49 +179,49 @@ suite('PersistentProtocol reconnection', () => {
const bMessages = new MessageStream(b);
a.send(VSBuffer.fromString('a1'));
assert.equal(a.unacknowledgedCount, 1);
assert.equal(b.unacknowledgedCount, 0);
assert.strictEqual(a.unacknowledgedCount, 1);
assert.strictEqual(b.unacknowledgedCount, 0);
a.send(VSBuffer.fromString('a2'));
assert.equal(a.unacknowledgedCount, 2);
assert.equal(b.unacknowledgedCount, 0);
assert.strictEqual(a.unacknowledgedCount, 2);
assert.strictEqual(b.unacknowledgedCount, 0);
a.send(VSBuffer.fromString('a3'));
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 0);
assert.strictEqual(a.unacknowledgedCount, 3);
assert.strictEqual(b.unacknowledgedCount, 0);
const a1 = await bMessages.waitForOne();
assert.equal(a1.toString(), 'a1');
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 0);
assert.strictEqual(a1.toString(), 'a1');
assert.strictEqual(a.unacknowledgedCount, 3);
assert.strictEqual(b.unacknowledgedCount, 0);
const a2 = await bMessages.waitForOne();
assert.equal(a2.toString(), 'a2');
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 0);
assert.strictEqual(a2.toString(), 'a2');
assert.strictEqual(a.unacknowledgedCount, 3);
assert.strictEqual(b.unacknowledgedCount, 0);
const a3 = await bMessages.waitForOne();
assert.equal(a3.toString(), 'a3');
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 0);
assert.strictEqual(a3.toString(), 'a3');
assert.strictEqual(a.unacknowledgedCount, 3);
assert.strictEqual(b.unacknowledgedCount, 0);
b.send(VSBuffer.fromString('b1'));
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 1);
assert.strictEqual(a.unacknowledgedCount, 3);
assert.strictEqual(b.unacknowledgedCount, 1);
const b1 = await aMessages.waitForOne();
assert.equal(b1.toString(), 'b1');
assert.equal(a.unacknowledgedCount, 0);
assert.equal(b.unacknowledgedCount, 1);
assert.strictEqual(b1.toString(), 'b1');
assert.strictEqual(a.unacknowledgedCount, 0);
assert.strictEqual(b.unacknowledgedCount, 1);
a.send(VSBuffer.fromString('a4'));
assert.equal(a.unacknowledgedCount, 1);
assert.equal(b.unacknowledgedCount, 1);
assert.strictEqual(a.unacknowledgedCount, 1);
assert.strictEqual(b.unacknowledgedCount, 1);
const b2 = await bMessages.waitForOne();
assert.equal(b2.toString(), 'a4');
assert.equal(a.unacknowledgedCount, 1);
assert.equal(b.unacknowledgedCount, 0);
assert.strictEqual(b2.toString(), 'a4');
assert.strictEqual(a.unacknowledgedCount, 1);
assert.strictEqual(b.unacknowledgedCount, 0);
});
});