2021-01-20 20:11:08 +00:00
|
|
|
import * as express from "express"
|
2021-01-14 14:34:51 +00:00
|
|
|
import * as http from "http"
|
|
|
|
import * as nodeFetch from "node-fetch"
|
2021-01-20 20:11:08 +00:00
|
|
|
import Websocket from "ws"
|
2021-01-14 14:49:23 +00:00
|
|
|
import * as util from "../src/common/util"
|
|
|
|
import { ensureAddress } from "../src/node/app"
|
2021-01-20 20:11:08 +00:00
|
|
|
import { handleUpgrade } from "../src/node/wsRouter"
|
2021-01-14 14:34:51 +00:00
|
|
|
|
2021-01-14 15:53:58 +00:00
|
|
|
// Perhaps an abstraction similar to this should be used in app.ts as well.
|
2021-01-14 14:34:51 +00:00
|
|
|
export class HttpServer {
|
|
|
|
private hs = http.createServer()
|
|
|
|
|
2021-01-14 15:53:58 +00:00
|
|
|
public constructor(hs?: http.Server) {
|
|
|
|
// See usage in test/integration.ts
|
|
|
|
if (hs) {
|
|
|
|
this.hs = hs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-14 14:34:51 +00:00
|
|
|
/**
|
|
|
|
* listen starts the server on a random localhost port.
|
|
|
|
* Use close to cleanup when done.
|
|
|
|
*/
|
|
|
|
public listen(fn: http.RequestListener): Promise<void> {
|
|
|
|
this.hs.on("request", fn)
|
|
|
|
|
|
|
|
let resolved = false
|
|
|
|
return new Promise((res, rej) => {
|
|
|
|
this.hs.listen(0, "localhost", () => {
|
|
|
|
res()
|
|
|
|
resolved = true
|
|
|
|
})
|
|
|
|
|
|
|
|
this.hs.on("error", (err) => {
|
|
|
|
if (!resolved) {
|
|
|
|
rej(err)
|
|
|
|
} else {
|
|
|
|
// Promise resolved earlier so this is some other error.
|
2021-01-14 14:49:23 +00:00
|
|
|
util.logError("http server error", err)
|
2021-01-14 14:34:51 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-01-20 20:11:08 +00:00
|
|
|
/**
|
|
|
|
* Send upgrade requests to an Express app.
|
|
|
|
*/
|
|
|
|
public listenUpgrade(app: express.Express): void {
|
|
|
|
handleUpgrade(app, this.hs)
|
|
|
|
}
|
|
|
|
|
2021-01-14 14:34:51 +00:00
|
|
|
/**
|
|
|
|
* close cleans up the server.
|
|
|
|
*/
|
|
|
|
public close(): Promise<void> {
|
|
|
|
return new Promise((res, rej) => {
|
|
|
|
this.hs.close((err) => {
|
|
|
|
if (err) {
|
|
|
|
rej(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
res()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fetch fetches the request path.
|
|
|
|
* The request path must be rooted!
|
|
|
|
*/
|
|
|
|
public fetch(requestPath: string, opts?: nodeFetch.RequestInit): Promise<nodeFetch.Response> {
|
|
|
|
return nodeFetch.default(`${ensureAddress(this.hs)}${requestPath}`, opts)
|
|
|
|
}
|
2021-01-14 15:53:58 +00:00
|
|
|
|
2021-01-20 20:11:08 +00:00
|
|
|
/**
|
|
|
|
* Open a websocket against the requset path.
|
|
|
|
*/
|
|
|
|
public ws(requestPath: string): Websocket {
|
|
|
|
return new Websocket(`${ensureAddress(this.hs).replace("http:", "ws:")}${requestPath}`)
|
|
|
|
}
|
|
|
|
|
2021-01-14 15:53:58 +00:00
|
|
|
public port(): number {
|
|
|
|
const addr = this.hs.address()
|
2021-01-14 15:55:19 +00:00
|
|
|
if (addr && typeof addr === "object") {
|
2021-01-14 15:53:58 +00:00
|
|
|
return addr.port
|
|
|
|
}
|
|
|
|
throw new Error("server not listening or listening on unix socket")
|
|
|
|
}
|
2021-01-14 14:34:51 +00:00
|
|
|
}
|