Skip to content

Commit 676c7bf

Browse files
authored
Merge pull request #2250 from cdr/disconnects
Experimental initial connection fix
2 parents 860c99e + 9ad7d0b commit 676c7bf

File tree

4 files changed

+87
-44
lines changed

4 files changed

+87
-44
lines changed

ci/build/build-release.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ EOF
6767
bundle_vscode() {
6868
mkdir -p "$VSCODE_OUT_PATH"
6969
rsync "$VSCODE_SRC_PATH/yarn.lock" "$VSCODE_OUT_PATH"
70-
rsync "$VSCODE_SRC_PATH/out-vscode${MINIFY+-min}/" "$VSCODE_OUT_PATH/out"
70+
rsync "$VSCODE_SRC_PATH/out-vscode${MINIFY:+-min}/" "$VSCODE_OUT_PATH/out"
7171

7272
rsync "$VSCODE_SRC_PATH/.build/extensions/" "$VSCODE_OUT_PATH/extensions"
7373
if [ "$KEEP_MODULES" = 0 ]; then

ci/dev/vscode.patch

+74-36
Original file line numberDiff line numberDiff line change
@@ -1225,10 +1225,10 @@ index 0000000000000000000000000000000000000000..4ea6d95d36aaac07dbd4d0e16ab3c1bb
12251225
+}
12261226
diff --git a/src/vs/server/entry.ts b/src/vs/server/entry.ts
12271227
new file mode 100644
1228-
index 0000000000000000000000000000000000000000..ab020fbb4e4ab3748cc807765ff9c362389faafa
1228+
index 0000000000000000000000000000000000000000..8482c48bae007ed6b39183001ae2cc6d140fcd50
12291229
--- /dev/null
12301230
+++ b/src/vs/server/entry.ts
1231-
@@ -0,0 +1,78 @@
1231+
@@ -0,0 +1,79 @@
12321232
+import { field } from '@coder/logger';
12331233
+import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
12341234
+import { CodeServerMessage, VscodeMessage } from 'vs/server/ipc';
@@ -1273,7 +1273,8 @@ index 0000000000000000000000000000000000000000..ab020fbb4e4ab3748cc807765ff9c362
12731273
+// Wait for the init message then start up VS Code. Subsequent messages will
12741274
+// return new workbench options without starting a new instance.
12751275
+process.on('message', async (message: CodeServerMessage, socket) => {
1276-
+ logger.debug('got message from code-server', field('message', message));
1276+
+ logger.debug('got message from code-server', field('type', message.type));
1277+
+ logger.trace('code-server message content', field('message', message));
12771278
+ switch (message.type) {
12781279
+ case 'init':
12791280
+ try {
@@ -1821,21 +1822,20 @@ index 0000000000000000000000000000000000000000..609c4d1cb43f52f92906b901c14c790f
18211822
+}
18221823
diff --git a/src/vs/server/node/connection.ts b/src/vs/server/node/connection.ts
18231824
new file mode 100644
1824-
index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759f396be18
1825+
index 0000000000000000000000000000000000000000..93062cadc627c61e0829c27a72894b81e6a0e039
18251826
--- /dev/null
18261827
+++ b/src/vs/server/node/connection.ts
1827-
@@ -0,0 +1,157 @@
1828+
@@ -0,0 +1,171 @@
1829+
+import { field, Logger, logger } from '@coder/logger';
18281830
+import * as cp from 'child_process';
18291831
+import { VSBuffer } from 'vs/base/common/buffer';
18301832
+import { Emitter } from 'vs/base/common/event';
18311833
+import { FileAccess } from 'vs/base/common/network';
18321834
+import { ISocket } from 'vs/base/parts/ipc/common/ipc.net';
18331835
+import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
18341836
+import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
1835-
+import { ILogService } from 'vs/platform/log/common/log';
18361837
+import { getNlsConfiguration } from 'vs/server/node/nls';
18371838
+import { Protocol } from 'vs/server/node/protocol';
1838-
+import { IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
18391839
+
18401840
+export abstract class Connection {
18411841
+ private readonly _onClose = new Emitter<void>();
@@ -1899,13 +1899,14 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759
18991899
+
19001900
+export class ExtensionHostConnection extends Connection {
19011901
+ private process?: cp.ChildProcess;
1902+
+ private readonly logger: Logger;
19021903
+
19031904
+ public constructor(
19041905
+ locale:string, protocol: Protocol, buffer: VSBuffer, token: string,
1905-
+ private readonly log: ILogService,
19061906
+ private readonly environment: INativeEnvironmentService,
19071907
+ ) {
19081908
+ super(protocol, token);
1909+
+ this.logger = logger.named("exthost", field("token", token));
19091910
+ this.protocol.dispose();
19101911
+ this.spawn(locale, buffer).then((p) => this.process = p);
19111912
+ this.protocol.getUnderlyingSocket().pause();
@@ -1928,6 +1929,7 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759
19281929
+ private sendInitMessage(buffer: VSBuffer): void {
19291930
+ const socket = this.protocol.getUnderlyingSocket();
19301931
+ socket.pause();
1932+
+ this.logger.trace('Sending socket');
19311933
+ this.process!.send({ // Process must be set at this point.
19321934
+ type: 'VSCODE_EXTHOST_IPC_SOCKET',
19331935
+ initialDataChunk: (buffer.buffer as Buffer).toString('base64'),
@@ -1936,7 +1938,9 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759
19361938
+ }
19371939
+
19381940
+ private async spawn(locale: string, buffer: VSBuffer): Promise<cp.ChildProcess> {
1941+
+ this.logger.trace('Getting NLS configuration...');
19391942
+ const config = await getNlsConfiguration(locale, this.environment.userDataPath);
1943+
+ this.logger.trace('Spawning extension host...');
19401944
+ const proc = cp.fork(
19411945
+ FileAccess.asFileUri('bootstrap-fork', require).fsPath,
19421946
+ [ '--type=extensionHost' ],
@@ -1956,30 +1960,41 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759
19561960
+ },
19571961
+ );
19581962
+
1959-
+ proc.on('error', () => this.dispose());
1960-
+ proc.on('exit', () => this.dispose());
1963+
+ proc.on('error', (error) => {
1964+
+ this.logger.error('Exited unexpectedly', field('error', error));
1965+
+ this.dispose();
1966+
+ });
1967+
+ proc.on('exit', (code) => {
1968+
+ this.logger.trace('Exited', field('code', code));
1969+
+ this.dispose();
1970+
+ });
19611971
+ if (proc.stdout && proc.stderr) {
1962-
+ proc.stdout.setEncoding('utf8').on('data', (d) => this.log.info('Extension host stdout', d));
1963-
+ proc.stderr.setEncoding('utf8').on('data', (d) => this.log.error('Extension host stderr', d));
1972+
+ proc.stdout.setEncoding('utf8').on('data', (d) => this.logger.info(d));
1973+
+ proc.stderr.setEncoding('utf8').on('data', (d) => this.logger.error(d));
19641974
+ }
1975+
+
19651976
+ proc.on('message', (event) => {
1966-
+ if (event && event.type === '__$console') {
1967-
+ const severity = (<any>this.log)[event.severity] ? event.severity : 'info';
1968-
+ (<any>this.log)[severity]('Extension host', event.arguments);
1969-
+ }
1970-
+ if (event && event.type === 'VSCODE_EXTHOST_DISCONNECTED') {
1971-
+ this.setOffline();
1977+
+ switch (event && event.type) {
1978+
+ case '__$console':
1979+
+ const severity = (<any>this.logger)[event.severity] || 'info';
1980+
+ (<any>this.logger)[severity]('console', field('arguments', event.arguments));
1981+
+ break;
1982+
+ case 'VSCODE_EXTHOST_DISCONNECTED':
1983+
+ this.logger.trace('Going offline');
1984+
+ this.setOffline();
1985+
+ break;
1986+
+ case 'VSCODE_EXTHOST_IPC_READY':
1987+
+ this.logger.trace('Got ready message');
1988+
+ this.sendInitMessage(buffer);
1989+
+ break;
1990+
+ default:
1991+
+ this.logger.error('Unexpected message', field("event", event));
1992+
+ break;
19721993
+ }
19731994
+ });
19741995
+
1975-
+ const listen = (message: IExtHostReadyMessage) => {
1976-
+ if (message.type === 'VSCODE_EXTHOST_IPC_READY') {
1977-
+ proc.removeListener('message', listen);
1978-
+ this.sendInitMessage(buffer);
1979-
+ }
1980-
+ };
1981-
+
1982-
+ return proc.on('message', listen);
1996+
+ this.logger.trace('Waiting for handshake...');
1997+
+ return proc;
19831998
+ }
19841999
+}
19852000
diff --git a/src/vs/server/node/insights.ts b/src/vs/server/node/insights.ts
@@ -2463,15 +2478,17 @@ index 0000000000000000000000000000000000000000..3d428a57d31f29c40f9c3ce45f715b44
24632478
+};
24642479
diff --git a/src/vs/server/node/protocol.ts b/src/vs/server/node/protocol.ts
24652480
new file mode 100644
2466-
index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b9cfd5fbf
2481+
index 0000000000000000000000000000000000000000..0d9310038c0ca378579652d89bc8ac84924213db
24672482
--- /dev/null
24682483
+++ b/src/vs/server/node/protocol.ts
2469-
@@ -0,0 +1,73 @@
2484+
@@ -0,0 +1,91 @@
2485+
+import { field } from '@coder/logger';
24702486
+import * as net from 'net';
24712487
+import { VSBuffer } from 'vs/base/common/buffer';
24722488
+import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
24732489
+import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
24742490
+import { AuthRequest, ConnectionTypeRequest, HandshakeMessage } from 'vs/platform/remote/common/remoteAgentConnection';
2491+
+import { logger } from 'vs/server/node/logger';
24752492
+
24762493
+export interface SocketOptions {
24772494
+ readonly reconnectionToken: string;
@@ -2499,29 +2516,45 @@ index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b
24992516
+ * Perform a handshake to get a connection request.
25002517
+ */
25012518
+ public handshake(): Promise<ConnectionTypeRequest> {
2519+
+ logger.trace('Protocol handshake', field('token', this.options.reconnectionToken));
25022520
+ return new Promise((resolve, reject) => {
2521+
+ const timeout = setTimeout(() => {
2522+
+ logger.error('Handshake timed out', field('token', this.options.reconnectionToken));
2523+
+ reject(new Error("timed out"));
2524+
+ }, 10000); // Matches the client timeout.
2525+
+
25032526
+ const handler = this.onControlMessage((rawMessage) => {
25042527
+ try {
2505-
+ const message = JSON.parse(rawMessage.toString());
2528+
+ const raw = rawMessage.toString();
2529+
+ logger.trace('Protocol message', field('token', this.options.reconnectionToken), field('message', raw));
2530+
+ const message = JSON.parse(raw);
25062531
+ switch (message.type) {
2507-
+ case 'auth': return this.authenticate(message);
2532+
+ case 'auth':
2533+
+ return this.authenticate(message);
25082534
+ case 'connectionType':
25092535
+ handler.dispose();
2536+
+ clearTimeout(timeout);
25102537
+ return resolve(message);
2511-
+ default: throw new Error('Unrecognized message type');
2538+
+ default:
2539+
+ throw new Error('Unrecognized message type');
25122540
+ }
25132541
+ } catch (error) {
25142542
+ handler.dispose();
2543+
+ clearTimeout(timeout);
25152544
+ reject(error);
25162545
+ }
25172546
+ });
2547+
+
2548+
+ // Kick off the handshake in case we missed the client's opening shot.
2549+
+ // TODO: Investigate why that message seems to get lost.
2550+
+ this.authenticate();
25182551
+ });
25192552
+ }
25202553
+
25212554
+ /**
25222555
+ * TODO: This ignores the authentication process entirely for now.
25232556
+ */
2524-
+ private authenticate(_message: AuthRequest): void {
2557+
+ private authenticate(_?: AuthRequest): void {
25252558
+ this.sendMessage({ type: 'sign', data: '' });
25262559
+ }
25272560
+
@@ -2542,10 +2575,11 @@ index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b
25422575
+}
25432576
diff --git a/src/vs/server/node/server.ts b/src/vs/server/node/server.ts
25442577
new file mode 100644
2545-
index 0000000000000000000000000000000000000000..a1289865858f405f93d3d396f41c6a0aadffd5e5
2578+
index 0000000000000000000000000000000000000000..45a7bf62a6c07d8771b0257e7c98fae095109eb1
25462579
--- /dev/null
25472580
+++ b/src/vs/server/node/server.ts
2548-
@@ -0,0 +1,286 @@
2581+
@@ -0,0 +1,291 @@
2582+
+import { field } from '@coder/logger';
25492583
+import * as fs from 'fs';
25502584
+import * as net from 'net';
25512585
+import * as path from 'path';
@@ -2709,6 +2743,7 @@ index 0000000000000000000000000000000000000000..a1289865858f405f93d3d396f41c6a0a
27092743
+ );
27102744
+ }
27112745
+
2746+
+ logger.debug('New connection', field('token', token));
27122747
+ protocol.sendMessage(await ok());
27132748
+
27142749
+ let connection: Connection;
@@ -2727,12 +2762,14 @@ index 0000000000000000000000000000000000000000..a1289865858f405f93d3d396f41c6a0a
27272762
+ connection = new ExtensionHostConnection(
27282763
+ message.args ? message.args.language : 'en',
27292764
+ protocol, buffer, token,
2730-
+ this.services.get(ILogService) as ILogService,
27312765
+ this.services.get(IEnvironmentService) as INativeEnvironmentService,
27322766
+ );
27332767
+ }
27342768
+ connections.set(token, connection);
2735-
+ connection.onClose(() => connections.delete(token));
2769+
+ connection.onClose(() => {
2770+
+ logger.debug('Connection closed', field('token', token));
2771+
+ connections.delete(token);
2772+
+ });
27362773
+ this.disposeOldOfflineConnections(connections);
27372774
+ break;
27382775
+ case ConnectionType.Tunnel: return protocol.tunnel();
@@ -2744,6 +2781,7 @@ index 0000000000000000000000000000000000000000..a1289865858f405f93d3d396f41c6a0a
27442781
+ const offline = Array.from(connections.values())
27452782
+ .filter((connection) => typeof connection.offline !== 'undefined');
27462783
+ for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) {
2784+
+ logger.debug('Disposing offline connection', field("token", offline[i].token));
27472785
+ offline[i].dispose();
27482786
+ }
27492787
+ }

src/node/app/vscode.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,15 @@ export class VscodeHttpProvider extends HttpProvider {
5050

5151
logger.debug("setting up vs code...")
5252
return new Promise<WorkbenchOptions>((resolve, reject) => {
53-
vscode.once("message", (message: VscodeMessage) => {
54-
logger.debug("got message from vs code", field("message", message))
55-
return message.type === "options" && message.id === id
56-
? resolve(message.options)
57-
: reject(new Error("Unexpected response during initialization"))
58-
})
53+
const onMessage = (message: VscodeMessage) => {
54+
// There can be parallel initializations so wait for the right ID.
55+
if (message.type === "options" && message.id === id) {
56+
logger.trace("got message from vs code", field("message", message))
57+
vscode.off("message", onMessage)
58+
resolve(message.options)
59+
}
60+
}
61+
vscode.on("message", onMessage)
5962
vscode.once("error", reject)
6063
vscode.once("exit", (code) => reject(new Error(`VS Code exited unexpectedly with code ${code}`)))
6164
this.send({ type: "init", id, options }, vscode)
@@ -77,7 +80,7 @@ export class VscodeHttpProvider extends HttpProvider {
7780

7881
this._vscode = new Promise((resolve, reject) => {
7982
vscode.once("message", (message: VscodeMessage) => {
80-
logger.debug("got message from vs code", field("message", message))
83+
logger.trace("got message from vs code", field("message", message))
8184
return message.type === "ready"
8285
? resolve(vscode)
8386
: reject(new Error("Unexpected response waiting for ready response"))

src/node/http.ts

+2
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,8 @@ export class HttpServer {
738738
}
739739

740740
private onUpgrade = async (request: http.IncomingMessage, socket: net.Socket, head: Buffer): Promise<void> => {
741+
socket.pause()
742+
741743
try {
742744
this.heart.beat()
743745
socket.on("error", () => socket.destroy())

0 commit comments

Comments
 (0)