Skip to content

Commit 02f9af1

Browse files
authored
Merge pull request #2737 from cdr/health-test
Fix healthz socket
2 parents 7b28284 + 2d8b785 commit 02f9af1

File tree

4 files changed

+75
-9
lines changed

4 files changed

+75
-9
lines changed

src/node/routes/health.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ router.get("/", (req, res) => {
1313
export const wsRouter = WsRouter()
1414

1515
wsRouter.ws("/", async (req) => {
16-
wss.handleUpgrade(req, req.socket, req.head, (ws) => {
17-
ws.on("message", () => {
16+
wss.handleUpgrade(req, req.ws, req.head, (ws) => {
17+
ws.addEventListener("message", () => {
1818
ws.send(
1919
JSON.stringify({
2020
event: "health",
@@ -23,5 +23,6 @@ wsRouter.ws("/", async (req) => {
2323
}),
2424
)
2525
})
26+
req.ws.resume()
2627
})
2728
})

test/health.test.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as httpserver from "./httpserver"
2+
import * as integration from "./integration"
3+
4+
describe("health", () => {
5+
let codeServer: httpserver.HttpServer | undefined
6+
7+
afterEach(async () => {
8+
if (codeServer) {
9+
await codeServer.close()
10+
codeServer = undefined
11+
}
12+
})
13+
14+
it("/healthz", async () => {
15+
;[, , codeServer] = await integration.setup(["--auth=none"], "")
16+
const resp = await codeServer.fetch("/healthz")
17+
expect(resp.status).toBe(200)
18+
const json = await resp.json()
19+
expect(json).toStrictEqual({ lastHeartbeat: 0, status: "expired" })
20+
})
21+
22+
it("/healthz (websocket)", async () => {
23+
;[, , codeServer] = await integration.setup(["--auth=none"], "")
24+
const ws = codeServer.ws("/healthz")
25+
const message = await new Promise((resolve, reject) => {
26+
ws.on("error", console.error)
27+
ws.on("message", (message) => {
28+
try {
29+
const j = JSON.parse(message.toString())
30+
resolve(j)
31+
} catch (error) {
32+
reject(error)
33+
}
34+
})
35+
ws.on("open", () => ws.send(JSON.stringify({ event: "health" })))
36+
})
37+
ws.terminate()
38+
expect(message).toStrictEqual({ event: "health", status: "expired", lastHeartbeat: 0 })
39+
})
40+
})

test/httpserver.ts

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as express from "express"
22
import * as http from "http"
3+
import * as net from "net"
34
import * as nodeFetch from "node-fetch"
45
import Websocket from "ws"
56
import * as util from "../src/common/util"
@@ -8,13 +9,21 @@ import { handleUpgrade } from "../src/node/wsRouter"
89

910
// Perhaps an abstraction similar to this should be used in app.ts as well.
1011
export class HttpServer {
11-
private hs = http.createServer()
12+
private readonly sockets = new Set<net.Socket>()
13+
private cleanupTimeout?: NodeJS.Timeout
1214

13-
public constructor(hs?: http.Server) {
14-
// See usage in test/integration.ts
15-
if (hs) {
16-
this.hs = hs
17-
}
15+
// See usage in test/integration.ts
16+
public constructor(private readonly hs = http.createServer()) {
17+
this.hs.on("connection", (socket) => {
18+
this.sockets.add(socket)
19+
socket.on("close", () => {
20+
this.sockets.delete(socket)
21+
if (this.cleanupTimeout && this.sockets.size === 0) {
22+
clearTimeout(this.cleanupTimeout)
23+
this.cleanupTimeout = undefined
24+
}
25+
})
26+
})
1827
}
1928

2029
/**
@@ -54,13 +63,28 @@ export class HttpServer {
5463
*/
5564
public close(): Promise<void> {
5665
return new Promise((res, rej) => {
66+
// Close will not actually close anything; it just waits until everything
67+
// is closed.
5768
this.hs.close((err) => {
5869
if (err) {
5970
rej(err)
6071
return
6172
}
6273
res()
6374
})
75+
76+
// If there are sockets remaining we might need to force close them or
77+
// this promise might never resolve.
78+
if (this.sockets.size > 0) {
79+
// Give sockets a chance to close up shop.
80+
this.cleanupTimeout = setTimeout(() => {
81+
this.cleanupTimeout = undefined
82+
for (const socket of this.sockets.values()) {
83+
console.warn("a socket was left hanging")
84+
socket.destroy()
85+
}
86+
}, 1000)
87+
}
6488
})
6589
}
6690

test/test-plugin/src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ export const plugin: cs.Plugin = {
2828
wsRouter() {
2929
const wr = cs.WsRouter()
3030
wr.ws("/test-app", (req) => {
31-
cs.wss.handleUpgrade(req, req.socket, req.head, (ws) => {
31+
cs.wss.handleUpgrade(req, req.ws, req.head, (ws) => {
32+
req.ws.resume()
3233
ws.send("hello")
3334
})
3435
})

0 commit comments

Comments
 (0)