439f27c568
* Disable BROWSER env var Right now the browser helper script does not actually work. It seems safer to skip this until we can fix it. * Bump to 4.0.2 * Update changelog for 4.0.2
135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
import { logger } from "@coder/logger"
|
|
import * as express from "express"
|
|
import { WebsocketRequest } from "../../../typings/pluginapi"
|
|
import { logError } from "../../common/util"
|
|
import { toVsCodeArgs } from "../cli"
|
|
import { isDevMode } from "../constants"
|
|
import { authenticated, ensureAuthenticated, redirect, self } from "../http"
|
|
import { loadAMDModule } from "../util"
|
|
import { Router as WsRouter } from "../wsRouter"
|
|
import { errorHandler } from "./errors"
|
|
|
|
export class CodeServerRouteWrapper {
|
|
/** Assigned in `ensureCodeServerLoaded` */
|
|
private _codeServerMain!: CodeServerLib.IServerAPI
|
|
private _wsRouterWrapper = WsRouter()
|
|
public router = express.Router()
|
|
|
|
public get wsRouter() {
|
|
return this._wsRouterWrapper.router
|
|
}
|
|
|
|
//#region Route Handlers
|
|
|
|
private $root: express.Handler = async (req, res, next) => {
|
|
const isAuthenticated = await authenticated(req)
|
|
|
|
if (!isAuthenticated) {
|
|
const to = self(req)
|
|
return redirect(req, res, "login", {
|
|
to: to !== "/" ? to : undefined,
|
|
})
|
|
}
|
|
|
|
const { query } = await req.settings.read()
|
|
if (query) {
|
|
// Ew means the workspace was closed so clear the last folder/workspace.
|
|
if (req.query.ew) {
|
|
delete query.folder
|
|
delete query.workspace
|
|
}
|
|
|
|
// Redirect to the last folder/workspace if nothing else is opened.
|
|
if (
|
|
!req.query.folder &&
|
|
!req.query.workspace &&
|
|
(query.folder || query.workspace) &&
|
|
!req.args["ignore-last-opened"] // This flag disables this behavior.
|
|
) {
|
|
const to = self(req)
|
|
return redirect(req, res, to, {
|
|
folder: query.folder,
|
|
workspace: query.workspace,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Store the query parameters so we can use them on the next load. This
|
|
// also allows users to create functionality around query parameters.
|
|
await req.settings.write({ query: req.query })
|
|
|
|
next()
|
|
}
|
|
|
|
private $proxyRequest: express.Handler = async (req, res, next) => {
|
|
// We allow certain errors to propagate so that other routers may handle requests
|
|
// outside VS Code
|
|
const requestErrorHandler = (error: any) => {
|
|
if (error instanceof Error && ["EntryNotFound", "FileNotFound", "HttpError"].includes(error.message)) {
|
|
next()
|
|
}
|
|
errorHandler(error, req, res, next)
|
|
}
|
|
|
|
req.once("error", requestErrorHandler)
|
|
|
|
this._codeServerMain.handleRequest(req, res)
|
|
}
|
|
|
|
private $proxyWebsocket = async (req: WebsocketRequest) => {
|
|
this._codeServerMain.handleUpgrade(req, req.socket)
|
|
|
|
req.socket.resume()
|
|
}
|
|
|
|
//#endregion
|
|
|
|
/**
|
|
* Fetches a code server instance asynchronously to avoid an initial memory overhead.
|
|
*/
|
|
private ensureCodeServerLoaded: express.Handler = async (req, _res, next) => {
|
|
if (this._codeServerMain) {
|
|
// Already loaded...
|
|
return next()
|
|
}
|
|
|
|
// Create the server...
|
|
|
|
const { args } = req
|
|
|
|
/**
|
|
* @file ../../../vendor/modules/code-oss-dev/src/vs/server/main.js
|
|
*/
|
|
const createVSServer = await loadAMDModule<CodeServerLib.CreateServer>(
|
|
"vs/server/remoteExtensionHostAgent",
|
|
"createServer",
|
|
)
|
|
|
|
try {
|
|
this._codeServerMain = await createVSServer(null, {
|
|
...(await toVsCodeArgs(args)),
|
|
// TODO: Make the browser helper script work.
|
|
"without-browser-env-var": true,
|
|
})
|
|
} catch (error) {
|
|
logError(logger, "CodeServerRouteWrapper", error)
|
|
if (isDevMode) {
|
|
return next(new Error((error instanceof Error ? error.message : error) + " (VS Code may still be compiling)"))
|
|
}
|
|
return next(error)
|
|
}
|
|
|
|
return next()
|
|
}
|
|
|
|
constructor() {
|
|
this.router.get("/", this.ensureCodeServerLoaded, this.$root)
|
|
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
|
|
this._wsRouterWrapper.ws("/", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
|
|
}
|
|
|
|
dispose() {
|
|
this._codeServerMain?.dispose()
|
|
}
|
|
}
|