Merge pull request #2563 from cdr/proxy-path-passthrough-0bb9
pathProxy.ts: Implement --proxy-path-passthrough
This commit is contained in:
@ -112,3 +112,11 @@ export const getFirstString = (value: string | string[] | object | undefined): s
|
||||
|
||||
return typeof value === "string" ? value : undefined
|
||||
}
|
||||
|
||||
export function logError(prefix: string, err: any): void {
|
||||
if (err instanceof Error) {
|
||||
logger.error(`${prefix}: ${err.message} ${err.stack}`)
|
||||
} else {
|
||||
logger.error(`${prefix}: ${err}`)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import express, { Express } from "express"
|
||||
import { promises as fs } from "fs"
|
||||
import http from "http"
|
||||
import * as httpolyglot from "httpolyglot"
|
||||
import * as util from "../common/util"
|
||||
import { DefaultedArgs } from "./cli"
|
||||
import { handleUpgrade } from "./wsRouter"
|
||||
|
||||
@ -22,8 +23,21 @@ export const createApp = async (args: DefaultedArgs): Promise<[Express, Express,
|
||||
)
|
||||
: http.createServer(app)
|
||||
|
||||
await new Promise<http.Server>(async (resolve, reject) => {
|
||||
server.on("error", reject)
|
||||
let resolved = false
|
||||
await new Promise<http.Server>(async (resolve2, reject) => {
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
if (args.socket) {
|
||||
try {
|
||||
await fs.unlink(args.socket)
|
||||
|
@ -50,6 +50,7 @@ export interface Args extends VsArgs {
|
||||
"show-versions"?: boolean
|
||||
"uninstall-extension"?: string[]
|
||||
"proxy-domain"?: string[]
|
||||
"proxy-path-passthrough"?: boolean
|
||||
locale?: string
|
||||
_: string[]
|
||||
"reuse-window"?: boolean
|
||||
@ -172,6 +173,10 @@ const options: Options<Required<Args>> = {
|
||||
"uninstall-extension": { type: "string[]", description: "Uninstall a VS Code extension by id." },
|
||||
"show-versions": { type: "boolean", description: "Show VS Code extension versions." },
|
||||
"proxy-domain": { type: "string[]", description: "Domain used for proxying ports." },
|
||||
"proxy-path-passthrough": {
|
||||
type: "boolean",
|
||||
description: "Whether the path proxy should leave the /proxy/<port> in the request path when proxying.",
|
||||
},
|
||||
"ignore-last-opened": {
|
||||
type: "boolean",
|
||||
short: "e",
|
||||
@ -239,7 +244,7 @@ export const optionDescriptions = (): string[] => {
|
||||
export const parse = (
|
||||
argv: string[],
|
||||
opts?: {
|
||||
configFile: string
|
||||
configFile?: string
|
||||
},
|
||||
): Args => {
|
||||
const error = (msg: string): Error => {
|
||||
@ -516,7 +521,19 @@ export async function readConfigFile(configPath?: string): Promise<ConfigArgs> {
|
||||
}
|
||||
|
||||
const configFile = await fs.readFile(configPath)
|
||||
const config = yaml.safeLoad(configFile.toString(), {
|
||||
return parseConfigFile(configFile.toString(), configPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* parseConfigFile parses configFile into ConfigArgs.
|
||||
* configPath is used as the filename in error messages
|
||||
*/
|
||||
export function parseConfigFile(configFile: string, configPath: string): ConfigArgs {
|
||||
if (!configFile) {
|
||||
return { _: [], config: configPath }
|
||||
}
|
||||
|
||||
const config = yaml.safeLoad(configFile, {
|
||||
filename: configPath,
|
||||
})
|
||||
if (!config || typeof config === "string") {
|
||||
|
@ -45,4 +45,13 @@ export class Heart {
|
||||
})
|
||||
}, this.heartbeatInterval)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to clear any heartbeatTimer for shutdown.
|
||||
*/
|
||||
public dispose(): void {
|
||||
if (typeof this.heartbeatTimer !== "undefined") {
|
||||
clearTimeout(this.heartbeatTimer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ export const register = async (
|
||||
})
|
||||
})
|
||||
})
|
||||
server.on("close", () => {
|
||||
heart.dispose()
|
||||
})
|
||||
|
||||
app.disable("x-powered-by")
|
||||
wsApp.disable("x-powered-by")
|
||||
@ -165,7 +168,7 @@ export const register = async (
|
||||
|
||||
app.use(errorHandler)
|
||||
|
||||
const wsErrorHandler: express.ErrorRequestHandler = async (err, req) => {
|
||||
const wsErrorHandler: express.ErrorRequestHandler = async (err, req, res, next) => {
|
||||
logger.error(`${err.message} ${err.stack}`)
|
||||
;(req as WebsocketRequest).ws.end()
|
||||
}
|
||||
|
@ -8,12 +8,12 @@ import { Router as WsRouter } from "../wsRouter"
|
||||
|
||||
export const router = Router()
|
||||
|
||||
const getProxyTarget = (req: Request, rewrite: boolean): string => {
|
||||
if (rewrite) {
|
||||
const query = qs.stringify(req.query)
|
||||
return `http://0.0.0.0:${req.params.port}/${req.params[0] || ""}${query ? `?${query}` : ""}`
|
||||
const getProxyTarget = (req: Request, passthroughPath: boolean): string => {
|
||||
if (passthroughPath) {
|
||||
return `http://0.0.0.0:${req.params.port}/${req.originalUrl}`
|
||||
}
|
||||
return `http://0.0.0.0:${req.params.port}/${req.originalUrl}`
|
||||
const query = qs.stringify(req.query)
|
||||
return `http://0.0.0.0:${req.params.port}/${req.params[0] || ""}${query ? `?${query}` : ""}`
|
||||
}
|
||||
|
||||
router.all("/(:port)(/*)?", (req, res) => {
|
||||
@ -33,7 +33,7 @@ router.all("/(:port)(/*)?", (req, res) => {
|
||||
|
||||
proxy.web(req, res, {
|
||||
ignorePath: true,
|
||||
target: getProxyTarget(req, true),
|
||||
target: getProxyTarget(req, req.args["proxy-path-passthrough"] || false),
|
||||
})
|
||||
})
|
||||
|
||||
@ -42,6 +42,6 @@ export const wsRouter = WsRouter()
|
||||
wsRouter.ws("/(:port)(/*)?", ensureAuthenticated, (req) => {
|
||||
proxy.ws(req, req.ws, req.head, {
|
||||
ignorePath: true,
|
||||
target: getProxyTarget(req, true),
|
||||
target: getProxyTarget(req, req.args["proxy-path-passthrough"] || false),
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user