app.ts 2.1 KB
Newer Older
A
Asher 已提交
1
import { logger } from "@coder/logger"
2
import compression from "compression"
A
Asher 已提交
3 4 5 6
import express, { Express } from "express"
import { promises as fs } from "fs"
import http from "http"
import * as httpolyglot from "httpolyglot"
7
import * as util from "../common/util"
A
Asher 已提交
8
import { DefaultedArgs } from "./cli"
A
Asher 已提交
9
import { handleUpgrade } from "./wsRouter"
A
Asher 已提交
10 11 12 13

/**
 * Create an Express app and an HTTP/S server to serve it.
 */
A
Asher 已提交
14
export const createApp = async (args: DefaultedArgs): Promise<[Express, Express, http.Server]> => {
A
Asher 已提交
15 16
  const app = express()

17 18
  app.use(compression())

A
Asher 已提交
19 20 21 22 23 24 25 26 27 28
  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)

29
  let resolved = false
30
  await new Promise<void>(async (resolve2, reject) => {
31 32 33 34 35 36 37 38 39 40 41 42 43
    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)
      }
    })

A
Asher 已提交
44 45 46 47 48 49 50 51
    if (args.socket) {
      try {
        await fs.unlink(args.socket)
      } catch (error) {
        if (error.code !== "ENOENT") {
          logger.error(error.message)
        }
      }
52
      server.listen(args.socket, resolve)
A
Asher 已提交
53 54
    } else {
      // [] is the correct format when using :: but Node errors with them.
55
      server.listen(args.port, args.host.replace(/^\[|\]$/g, ""), resolve)
A
Asher 已提交
56 57 58
    }
  })

A
Asher 已提交
59 60
  const wsApp = express()
  handleUpgrade(wsApp, server)
A
Asher 已提交
61

A
Asher 已提交
62
  return [app, wsApp, server]
A
Asher 已提交
63 64 65
}

/**
A
Asher 已提交
66
 * Get the address of a server as a string (protocol *is* included) while
A
Asher 已提交
67 68 69 70 71 72 73 74
 * 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") {
A
Asher 已提交
75
    return `http://${addr.address}:${addr.port}`
A
Asher 已提交
76 77 78
  }
  return addr
}