Skip to content

Commit 247c4ec

Browse files
committed
Move onMessage so it can be used in the wrappers
1 parent d55e069 commit 247c4ec

File tree

2 files changed

+81
-86
lines changed

2 files changed

+81
-86
lines changed

src/node/vscode.ts

+10-56
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { field, logger } from "@coder/logger"
1+
import { logger } from "@coder/logger"
22
import * as cp from "child_process"
33
import * as net from "net"
44
import * as path from "path"
@@ -8,13 +8,12 @@ import { rootPath } from "./constants"
88
import { settings } from "./settings"
99
import { SocketProxyProvider } from "./socket"
1010
import { isFile } from "./util"
11-
import { wrapper } from "./wrapper"
11+
import { onMessage, wrapper } from "./wrapper"
1212

1313
export class VscodeProvider {
1414
public readonly serverRootPath: string
1515
public readonly vsRootPath: string
1616
private _vscode?: Promise<cp.ChildProcess>
17-
private timeoutInterval = 10000 // 10s, matches VS Code's timeouts.
1817
private readonly socketProvider = new SocketProxyProvider()
1918

2019
public constructor() {
@@ -69,10 +68,13 @@ export class VscodeProvider {
6968
vscode,
7069
)
7170

72-
const message = await this.onMessage(vscode, (message): message is ipc.OptionsMessage => {
73-
// There can be parallel initializations so wait for the right ID.
74-
return message.type === "options" && message.id === id
75-
})
71+
const message = await onMessage<ipc.VscodeMessage, ipc.OptionsMessage>(
72+
vscode,
73+
(message): message is ipc.OptionsMessage => {
74+
// There can be parallel initializations so wait for the right ID.
75+
return message.type === "options" && message.id === id
76+
},
77+
)
7678

7779
return message.options
7880
}
@@ -104,61 +106,13 @@ export class VscodeProvider {
104106
dispose()
105107
})
106108

107-
this._vscode = this.onMessage(vscode, (message): message is ipc.ReadyMessage => {
109+
this._vscode = onMessage<ipc.VscodeMessage, ipc.ReadyMessage>(vscode, (message): message is ipc.ReadyMessage => {
108110
return message.type === "ready"
109111
}).then(() => vscode)
110112

111113
return this._vscode
112114
}
113115

114-
/**
115-
* Listen to a single message from a process. Reject if the process errors,
116-
* exits, or times out.
117-
*
118-
* `fn` is a function that determines whether the message is the one we're
119-
* waiting for.
120-
*/
121-
private onMessage<T extends ipc.VscodeMessage>(
122-
proc: cp.ChildProcess,
123-
fn: (message: ipc.VscodeMessage) => message is T,
124-
): Promise<T> {
125-
return new Promise((resolve, reject) => {
126-
const cleanup = () => {
127-
proc.off("error", onError)
128-
proc.off("exit", onExit)
129-
proc.off("message", onMessage)
130-
clearTimeout(timeout)
131-
}
132-
133-
const timeout = setTimeout(() => {
134-
cleanup()
135-
reject(new Error("timed out"))
136-
}, this.timeoutInterval)
137-
138-
const onError = (error: Error) => {
139-
cleanup()
140-
reject(error)
141-
}
142-
143-
const onExit = (code: number | null) => {
144-
cleanup()
145-
reject(new Error(`VS Code exited unexpectedly with code ${code}`))
146-
}
147-
148-
const onMessage = (message: ipc.VscodeMessage) => {
149-
logger.trace("got message from vscode", field("message", message))
150-
if (fn(message)) {
151-
cleanup()
152-
resolve(message)
153-
}
154-
}
155-
156-
proc.on("message", onMessage)
157-
proc.on("error", onError)
158-
proc.on("exit", onExit)
159-
})
160-
}
161-
162116
/**
163117
* VS Code expects a raw socket. It will handle all the web socket frames.
164118
*/

src/node/wrapper.ts

+71-30
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,59 @@ import * as rfs from "rotating-file-stream"
55
import { Emitter } from "../common/emitter"
66
import { paths } from "./util"
77

8+
const timeoutInterval = 10000 // 10s, matches VS Code's timeouts.
9+
10+
/**
11+
* Listen to a single message from a process. Reject if the process errors,
12+
* exits, or times out.
13+
*
14+
* `fn` is a function that determines whether the message is the one we're
15+
* waiting for.
16+
*/
17+
export function onMessage<M, T extends M>(
18+
proc: cp.ChildProcess | NodeJS.Process,
19+
fn: (message: M) => message is T,
20+
customLogger?: Logger,
21+
): Promise<T> {
22+
return new Promise((resolve, reject) => {
23+
const cleanup = () => {
24+
proc.off("error", onError)
25+
proc.off("exit", onExit)
26+
proc.off("message", onMessage)
27+
clearTimeout(timeout)
28+
}
29+
30+
const timeout = setTimeout(() => {
31+
cleanup()
32+
reject(new Error("timed out"))
33+
}, timeoutInterval)
34+
35+
const onError = (error: Error) => {
36+
cleanup()
37+
reject(error)
38+
}
39+
40+
const onExit = (code: number) => {
41+
cleanup()
42+
reject(new Error(`exited unexpectedly with code ${code}`))
43+
}
44+
45+
const onMessage = (message: M) => {
46+
;(customLogger || logger).trace("got message", field("message", message))
47+
if (fn(message)) {
48+
cleanup()
49+
resolve(message)
50+
}
51+
}
52+
53+
proc.on("message", onMessage)
54+
// NodeJS.Process doesn't have `error` but binding anyway shouldn't break
55+
// anything. It does have `exit` but the types aren't working.
56+
;(proc as cp.ChildProcess).on("error", onError)
57+
;(proc as cp.ChildProcess).on("exit", onExit)
58+
})
59+
}
60+
861
interface HandshakeMessage {
962
type: "handshake"
1063
}
@@ -111,19 +164,15 @@ class ChildProcess extends Process {
111164
/**
112165
* Initiate the handshake and wait for a response from the parent.
113166
*/
114-
public handshake(): Promise<void> {
115-
return new Promise((resolve) => {
116-
const onMessage = (message: Message): void => {
117-
logger.debug(`received message from ${this.parentPid}`, field("message", message))
118-
if (message.type === "handshake") {
119-
process.removeListener("message", onMessage)
120-
resolve()
121-
}
122-
}
123-
// Initiate the handshake and wait for the reply.
124-
process.on("message", onMessage)
125-
this.send({ type: "handshake" })
126-
})
167+
public async handshake(): Promise<void> {
168+
this.send({ type: "handshake" })
169+
await onMessage<Message, HandshakeMessage>(
170+
process,
171+
(message): message is HandshakeMessage => {
172+
return message.type === "handshake"
173+
},
174+
this.logger,
175+
)
127176
}
128177

129178
/**
@@ -270,23 +319,15 @@ export class ParentProcess extends Process {
270319
/**
271320
* Wait for a handshake from the child then reply.
272321
*/
273-
private handshake(child: cp.ChildProcess): Promise<void> {
274-
return new Promise((resolve, reject) => {
275-
const onMessage = (message: Message): void => {
276-
logger.debug(`received message from ${child.pid}`, field("message", message))
277-
if (message.type === "handshake") {
278-
child.removeListener("message", onMessage)
279-
child.on("message", (msg) => this._onChildMessage.emit(msg))
280-
child.send({ type: "handshake" })
281-
resolve()
282-
}
283-
}
284-
child.on("message", onMessage)
285-
child.once("error", reject)
286-
child.once("exit", (code) => {
287-
reject(new ProcessError(`Unexpected exit with code ${code}`, code !== null ? code : undefined))
288-
})
289-
})
322+
private async handshake(child: cp.ChildProcess): Promise<void> {
323+
await onMessage<Message, HandshakeMessage>(
324+
child,
325+
(message): message is HandshakeMessage => {
326+
return message.type === "handshake"
327+
},
328+
this.logger,
329+
)
330+
child.send({ type: "handshake" })
290331
}
291332
}
292333

0 commit comments

Comments
 (0)