code-server/test/utils/httpserver.ts
Asher c4c480a068
Implement last opened functionality (#4633)
* Implement last opened functionality

Fixes https://github.com/cdr/code-server/issues/4619

* Fix test temp dirs not being cleaned up

* Mock logger everywhere

This suppresses all the error and debug output we generate which makes
it hard to actually find which test has failed.  It also gives us a
standard way to test logging for the few places we do that.

* Use separate data directories for unit test instances

Exactly as we do for the e2e tests.

* Add integration tests for vscode route

* Make settings use --user-data-dir

Without this test instances step on each other feet and they also
clobber your own non-test settings.

* Make redirects consistent

They will preserve the trailing slash if there is one.

* Remove compilation check

If you do a regular non-watch build there are no compilation stats so
this bricks VS Code in CI when running the unit tests.

I am not sure how best to fix this for the case where you have a build
that has not been packaged yet so I just removed it for now and added a
message to check if VS Code is compiling when in dev mode.

* Update code-server update endpoint name
2021-12-17 13:06:52 -06:00

97 lines
2.8 KiB
TypeScript

import { logger } from "@coder/logger"
import * as express from "express"
import * as http from "http"
import nodeFetch, { RequestInit, Response } from "node-fetch"
import Websocket from "ws"
import { Disposable } from "../../src/common/emitter"
import * as util from "../../src/common/util"
import { ensureAddress } from "../../src/node/app"
import { disposer } from "../../src/node/http"
import { handleUpgrade } from "../../src/node/wsRouter"
// Perhaps an abstraction similar to this should be used in app.ts as well.
export class HttpServer {
private hs: http.Server
public dispose: Disposable["dispose"]
/**
* Expects a server and a disposal that cleans up the server (and anything
* else that may need cleanup).
*
* Otherwise a new server is created.
*/
public constructor(server?: { server: http.Server; dispose: Disposable["dispose"] }) {
this.hs = server?.server || http.createServer()
this.dispose = server?.dispose || disposer(this.hs)
}
/**
* 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)
return new Promise((resolve, reject) => {
this.hs.on("error", reject)
this.hs.listen(0, "localhost", () => {
this.hs.off("error", reject)
resolve()
this.hs.on("error", (err) => {
// Promise resolved earlier so this is some other error.
util.logError(logger, "http server error", err)
})
})
})
}
/**
* Send upgrade requests to an Express app.
*/
public listenUpgrade(app: express.Express): void {
handleUpgrade(app, this.hs)
}
/**
* fetch fetches the request path.
* The request path must be rooted!
*/
public fetch(requestPath: string, opts?: RequestInit, query?: { [key: string]: string }): Promise<Response> {
const address = ensureAddress(this.hs, "http")
if (typeof address === "string") {
throw new Error("Cannot fetch socket path")
}
address.pathname = requestPath
if (query) {
Object.keys(query).forEach((key) => {
address.searchParams.append(key, query[key])
})
}
return nodeFetch(address.toString(), opts)
}
/**
* Open a websocket against the request path.
*/
public ws(requestPath: string): Websocket {
const address = ensureAddress(this.hs, "ws")
if (typeof address === "string") {
throw new Error("Cannot open websocket to socket path")
}
address.pathname = requestPath
return new Websocket(address.toString())
}
public port(): number {
const addr = this.hs.address()
if (addr && typeof addr === "object") {
return addr.port
}
throw new Error("server not listening or listening on unix socket")
}
}