2020-02-05 02:27:46 +07:00
|
|
|
import * as crypto from "crypto"
|
2020-10-21 06:05:58 +07:00
|
|
|
import { Router } from "express"
|
|
|
|
import { promises as fs } from "fs"
|
2020-02-05 02:27:46 +07:00
|
|
|
import * as path from "path"
|
2020-10-21 06:05:58 +07:00
|
|
|
import { commit, rootPath, version } from "../constants"
|
|
|
|
import { authenticated, ensureAuthenticated, redirect, replaceTemplates } from "../http"
|
|
|
|
import { getMediaMime, pathToFsPath } from "../util"
|
|
|
|
import { VscodeProvider } from "../vscode"
|
2020-11-06 01:58:37 +07:00
|
|
|
import { Router as WsRouter } from "../wsRouter"
|
2020-02-07 01:29:19 +07:00
|
|
|
|
2020-10-21 06:05:58 +07:00
|
|
|
export const router = Router()
|
2020-02-05 02:27:46 +07:00
|
|
|
|
2020-10-21 06:05:58 +07:00
|
|
|
const vscode = new VscodeProvider()
|
2020-02-05 02:27:46 +07:00
|
|
|
|
2020-10-21 06:05:58 +07:00
|
|
|
router.get("/", async (req, res) => {
|
|
|
|
if (!authenticated(req)) {
|
|
|
|
return redirect(req, res, "login", {
|
2020-11-11 07:14:18 +07:00
|
|
|
// req.baseUrl can be blank if already at the root.
|
|
|
|
to: req.baseUrl && req.baseUrl !== "/" ? req.baseUrl : undefined,
|
2020-02-05 02:27:46 +07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-10-21 06:05:58 +07:00
|
|
|
const [content, options] = await Promise.all([
|
|
|
|
await fs.readFile(path.join(rootPath, "src/browser/pages/vscode.html"), "utf8"),
|
2020-11-11 07:20:44 +07:00
|
|
|
(async () => {
|
|
|
|
try {
|
|
|
|
return await vscode.initialize({ args: req.args, remoteAuthority: req.headers.host || "" }, req.query)
|
|
|
|
} catch (error) {
|
2020-10-21 06:05:58 +07:00
|
|
|
const devMessage = commit === "development" ? "It might not have finished compiling." : ""
|
|
|
|
throw new Error(`VS Code failed to load. ${devMessage} ${error.message}`)
|
2020-11-11 07:20:44 +07:00
|
|
|
}
|
|
|
|
})(),
|
2020-10-21 06:05:58 +07:00
|
|
|
])
|
|
|
|
|
|
|
|
options.productConfiguration.codeServerVersion = version
|
|
|
|
|
|
|
|
res.send(
|
|
|
|
replaceTemplates(
|
|
|
|
req,
|
|
|
|
// Uncomment prod blocks if not in development. TODO: Would this be
|
|
|
|
// better as a build step? Or maintain two HTML files again?
|
|
|
|
commit !== "development" ? content.replace(/<!-- PROD_ONLY/g, "").replace(/END_PROD_ONLY -->/g, "") : content,
|
|
|
|
{
|
|
|
|
disableTelemetry: !!req.args["disable-telemetry"],
|
|
|
|
},
|
|
|
|
)
|
2020-02-28 03:56:14 +07:00
|
|
|
.replace(`"{{REMOTE_USER_DATA_URI}}"`, `'${JSON.stringify(options.remoteUserDataUri)}'`)
|
|
|
|
.replace(`"{{PRODUCT_CONFIGURATION}}"`, `'${JSON.stringify(options.productConfiguration)}'`)
|
|
|
|
.replace(`"{{WORKBENCH_WEB_CONFIGURATION}}"`, `'${JSON.stringify(options.workbenchWebConfiguration)}'`)
|
2020-10-21 06:05:58 +07:00
|
|
|
.replace(`"{{NLS_CONFIGURATION}}"`, `'${JSON.stringify(options.nlsConfiguration)}'`),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-11-04 05:30:45 +07:00
|
|
|
/**
|
|
|
|
* TODO: Might currently be unused.
|
|
|
|
*/
|
2020-11-04 05:45:03 +07:00
|
|
|
router.get("/resource(/*)?", ensureAuthenticated, async (req, res) => {
|
2020-10-21 06:05:58 +07:00
|
|
|
if (typeof req.query.path === "string") {
|
|
|
|
res.set("Content-Type", getMediaMime(req.query.path))
|
|
|
|
res.send(await fs.readFile(pathToFsPath(req.query.path)))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-11-04 05:30:45 +07:00
|
|
|
/**
|
|
|
|
* Used by VS Code to load files.
|
|
|
|
*/
|
2020-11-04 05:45:03 +07:00
|
|
|
router.get("/vscode-remote-resource(/*)?", ensureAuthenticated, async (req, res) => {
|
2020-10-21 06:05:58 +07:00
|
|
|
if (typeof req.query.path === "string") {
|
|
|
|
res.set("Content-Type", getMediaMime(req.query.path))
|
|
|
|
res.send(await fs.readFile(pathToFsPath(req.query.path)))
|
2020-02-05 02:27:46 +07:00
|
|
|
}
|
2020-10-21 06:05:58 +07:00
|
|
|
})
|
2020-02-06 03:21:59 +07:00
|
|
|
|
2020-11-04 05:30:45 +07:00
|
|
|
/**
|
|
|
|
* VS Code webviews use these paths to load files and to load webview assets
|
|
|
|
* like HTML and JavaScript.
|
|
|
|
*/
|
2020-11-04 05:45:03 +07:00
|
|
|
router.get("/webview/*", ensureAuthenticated, async (req, res) => {
|
2020-10-21 06:05:58 +07:00
|
|
|
res.set("Content-Type", getMediaMime(req.path))
|
2020-10-28 05:17:05 +07:00
|
|
|
if (/^vscode-resource/.test(req.params[0])) {
|
|
|
|
return res.send(await fs.readFile(req.params[0].replace(/^vscode-resource(\/file)?/, "")))
|
2020-02-07 01:29:19 +07:00
|
|
|
}
|
2020-10-21 06:05:58 +07:00
|
|
|
return res.send(
|
|
|
|
await fs.readFile(path.join(vscode.vsRootPath, "out/vs/workbench/contrib/webview/browser/pre", req.params[0])),
|
|
|
|
)
|
|
|
|
})
|
2020-11-06 01:58:37 +07:00
|
|
|
|
|
|
|
export const wsRouter = WsRouter()
|
|
|
|
|
|
|
|
wsRouter.ws("/", ensureAuthenticated, async (req) => {
|
|
|
|
const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
|
|
const reply = crypto
|
|
|
|
.createHash("sha1")
|
|
|
|
.update(req.headers["sec-websocket-key"] + magic)
|
|
|
|
.digest("base64")
|
|
|
|
req.ws.write(
|
|
|
|
[
|
|
|
|
"HTTP/1.1 101 Switching Protocols",
|
|
|
|
"Upgrade: websocket",
|
|
|
|
"Connection: Upgrade",
|
|
|
|
`Sec-WebSocket-Accept: ${reply}`,
|
|
|
|
].join("\r\n") + "\r\n\r\n",
|
|
|
|
)
|
|
|
|
await vscode.sendWebsocket(req.ws, req.query)
|
|
|
|
})
|