2020-10-16 17:43:49 +00:00
|
|
|
import { logger } from "@coder/logger"
|
2021-02-12 20:27:33 +00:00
|
|
|
import compression from "compression"
|
2020-10-16 17:43:49 +00:00
|
|
|
import express, { Express } from "express"
|
|
|
|
import { promises as fs } from "fs"
|
|
|
|
import http from "http"
|
|
|
|
import * as httpolyglot from "httpolyglot"
|
2021-01-14 14:49:23 +00:00
|
|
|
import * as util from "../common/util"
|
2020-10-16 17:43:49 +00:00
|
|
|
import { DefaultedArgs } from "./cli"
|
2020-11-05 18:58:37 +00:00
|
|
|
import { handleUpgrade } from "./wsRouter"
|
2020-10-16 17:43:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an Express app and an HTTP/S server to serve it.
|
|
|
|
*/
|
2020-11-05 18:58:37 +00:00
|
|
|
export const createApp = async (args: DefaultedArgs): Promise<[Express, Express, http.Server]> => {
|
2020-10-16 17:43:49 +00:00
|
|
|
const app = express()
|
|
|
|
|
2021-02-12 20:27:33 +00:00
|
|
|
app.use(compression())
|
|
|
|
|
2020-10-16 17:43:49 +00:00
|
|
|
const server = args.cert
|
|
|
|
? httpolyglot.createServer(
|
|
|
|
{
|
|
|
|
cert: args.cert && (await fs.readFile(args.cert.value)),
|
|
|
|
key: args["cert-key"] && (await fs.readFile(args["cert-key"])),
|
|
|
|
},
|
|
|
|
app,
|
|
|
|
)
|
|
|
|
: http.createServer(app)
|
|
|
|
|
2021-01-14 14:49:23 +00:00
|
|
|
let resolved = false
|
2021-01-20 20:01:31 +00:00
|
|
|
await new Promise<void>(async (resolve2, reject) => {
|
2021-01-14 14:49:23 +00:00
|
|
|
const resolve = () => {
|
|
|
|
resolved = true
|
|
|
|
resolve2()
|
|
|
|
}
|
|
|
|
server.on("error", (err) => {
|
|
|
|
if (!resolved) {
|
|
|
|
reject(err)
|
|
|
|
} else {
|
|
|
|
// Promise resolved earlier so this is an unrelated error.
|
|
|
|
util.logError("http server error", err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-10-16 17:43:49 +00:00
|
|
|
if (args.socket) {
|
|
|
|
try {
|
|
|
|
await fs.unlink(args.socket)
|
|
|
|
} catch (error) {
|
|
|
|
if (error.code !== "ENOENT") {
|
|
|
|
logger.error(error.message)
|
|
|
|
}
|
|
|
|
}
|
2021-01-20 20:01:31 +00:00
|
|
|
server.listen(args.socket, resolve)
|
2020-10-16 17:43:49 +00:00
|
|
|
} else {
|
|
|
|
// [] is the correct format when using :: but Node errors with them.
|
2021-01-20 20:01:31 +00:00
|
|
|
server.listen(args.port, args.host.replace(/^\[|\]$/g, ""), resolve)
|
2020-10-16 17:43:49 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-11-05 18:58:37 +00:00
|
|
|
const wsApp = express()
|
|
|
|
handleUpgrade(wsApp, server)
|
2020-10-20 23:05:58 +00:00
|
|
|
|
2020-11-05 18:58:37 +00:00
|
|
|
return [app, wsApp, server]
|
2020-10-16 17:43:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-11-03 22:11:28 +00:00
|
|
|
* Get the address of a server as a string (protocol *is* included) while
|
2020-10-16 17:43:49 +00:00
|
|
|
* ensuring there is one (will throw if there isn't).
|
|
|
|
*/
|
|
|
|
export const ensureAddress = (server: http.Server): string => {
|
|
|
|
const addr = server.address()
|
|
|
|
if (!addr) {
|
|
|
|
throw new Error("server has no address")
|
|
|
|
}
|
|
|
|
if (typeof addr !== "string") {
|
2020-11-03 22:11:28 +00:00
|
|
|
return `http://${addr.address}:${addr.port}`
|
2020-10-16 17:43:49 +00:00
|
|
|
}
|
|
|
|
return addr
|
|
|
|
}
|