Skip to content

Commit 60b6f6e

Browse files
smerschjohannyiliang114
authored andcommitted
Allow {{host}} and {{port}} in domain proxy (coder#6225)
1 parent e1c1cf6 commit 60b6f6e

File tree

6 files changed

+68
-27
lines changed

6 files changed

+68
-27
lines changed

patches/proxy-uri.diff

+6-2
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
113113

114114
interface ICredential {
115115
service: string;
116-
@@ -511,6 +512,38 @@ function doCreateUri(path: string, query
116+
@@ -511,6 +512,42 @@ function doCreateUri(path: string, query
117117
} : undefined,
118118
workspaceProvider: WorkspaceProvider.create(config),
119119
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
@@ -125,7 +125,11 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
125125
+
126126
+ if (localhostMatch && resolvedUri.authority !== location.host) {
127127
+ if (config.productConfiguration && config.productConfiguration.proxyEndpointTemplate) {
128-
+ resolvedUri = URI.parse(new URL(config.productConfiguration.proxyEndpointTemplate.replace('{{port}}', localhostMatch.port.toString()), window.location.href).toString())
128+
+ const renderedTemplate = config.productConfiguration.proxyEndpointTemplate
129+
+ .replace('{{port}}', localhostMatch.port.toString())
130+
+ .replace('{{host}}', window.location.host)
131+
+
132+
+ resolvedUri = URI.parse(new URL(renderedTemplate, window.location.href).toString())
129133
+ } else {
130134
+ throw new Error(`Failed to resolve external URI: ${uri.toString()}. Could not determine base url because productConfiguration missing.`)
131135
+ }

src/node/cli.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -574,10 +574,22 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
574574

575575
// Filter duplicate proxy domains and remove any leading `*.`.
576576
const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, "")))
577-
args["proxy-domain"] = Array.from(proxyDomains)
578-
if (args["proxy-domain"].length > 0 && !process.env.VSCODE_PROXY_URI) {
579-
process.env.VSCODE_PROXY_URI = `{{port}}.${args["proxy-domain"][0]}`
577+
const finalProxies = []
578+
579+
for (const proxyDomain of proxyDomains) {
580+
if (!proxyDomain.includes("{{port}}")) {
581+
finalProxies.push("{{port}}." + proxyDomain)
582+
} else {
583+
finalProxies.push(proxyDomain)
584+
}
585+
}
586+
587+
// all proxies are of format anyprefix-{{port}}-anysuffix.{{host}}, where {{host}} is optional
588+
// e.g. code-8080.domain.tld would match for code-{{port}}.domain.tld and code-{{port}}.{{host}}
589+
if (finalProxies.length > 0 && !process.env.VSCODE_PROXY_URI) {
590+
process.env.VSCODE_PROXY_URI = `//${finalProxies[0]}`
580591
}
592+
args["proxy-domain"] = finalProxies
581593

582594
if (typeof args._ === "undefined") {
583595
args._ = []

src/node/http.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ export function authenticateOrigin(req: express.Request): void {
373373
/**
374374
* Get the host from headers. It will be trimmed and lowercased.
375375
*/
376-
function getHost(req: express.Request): string | undefined {
376+
export function getHost(req: express.Request): string | undefined {
377377
// Honor Forwarded if present.
378378
const forwardedRaw = getFirstHeader(req, "forwarded")
379379
if (forwardedRaw) {

src/node/main.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,10 @@ export const runCodeServer = async (
149149

150150
if (args["proxy-domain"].length > 0) {
151151
logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`)
152-
args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`))
152+
args["proxy-domain"].forEach((domain) => logger.info(` - ${domain}`))
153+
}
154+
if (process.env.VSCODE_PROXY_URI) {
155+
logger.info(`Using proxy URI in PORTS tab: ${process.env.VSCODE_PROXY_URI}`)
153156
}
154157

155158
if (args.enable && args.enable.length > 0) {

src/node/routes/domainProxy.ts

+39-17
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,56 @@
11
import { Request, Router } from "express"
22
import { HttpCode, HttpError } from "../../common/http"
3-
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
3+
import { getHost, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
44
import { proxy } from "../proxy"
55
import { Router as WsRouter } from "../wsRouter"
66

77
export const router = Router()
88

9+
const proxyDomainToRegex = (matchString: string): RegExp => {
10+
const escapedMatchString = matchString.replace(/[.*+?^$()|[\]\\]/g, "\\$&")
11+
12+
// Replace {{port}} with a regex group to capture the port
13+
// Replace {{host}} with .+ to allow any host match (so rely on DNS record here)
14+
let regexString = escapedMatchString.replace("{{port}}", "(\\d+)")
15+
regexString = regexString.replace("{{host}}", ".+")
16+
17+
regexString = regexString.replace(/[{}]/g, "\\$&") //replace any '{}' that might be left
18+
19+
return new RegExp("^" + regexString + "$")
20+
}
21+
22+
let proxyRegexes: RegExp[] = []
23+
const proxyDomainsToRegex = (proxyDomains: string[]): RegExp[] => {
24+
if (proxyDomains.length !== proxyRegexes.length) {
25+
proxyRegexes = proxyDomains.map(proxyDomainToRegex)
26+
}
27+
return proxyRegexes
28+
}
29+
930
/**
10-
* Return the port if the request should be proxied. Anything that ends in a
11-
* proxy domain and has a *single* subdomain should be proxied. Anything else
12-
* should return `undefined` and will be handled as normal.
31+
* Return the port if the request should be proxied.
32+
*
33+
* The proxy-domain should be of format anyprefix-{{port}}-anysuffix.{{host}}, where {{host}} is optional
34+
* e.g. code-8080.domain.tld would match for code-{{port}}.domain.tld and code-{{port}}.{{host}}.
1335
*
14-
* For example if `coder.com` is specified `8080.coder.com` will be proxied
15-
* but `8080.test.coder.com` and `test.8080.coder.com` will not.
1636
*/
1737
const maybeProxy = (req: Request): string | undefined => {
18-
// Split into parts.
19-
const host = req.headers.host || ""
20-
const idx = host.indexOf(":")
21-
const domain = idx !== -1 ? host.substring(0, idx) : host
22-
const parts = domain.split(".")
23-
24-
// There must be an exact match.
25-
const port = parts.shift()
26-
const proxyDomain = parts.join(".")
27-
if (!port || !req.args["proxy-domain"].includes(proxyDomain)) {
38+
const reqDomain = getHost(req)
39+
if (reqDomain === undefined) {
2840
return undefined
2941
}
3042

31-
return port
43+
const regexs = proxyDomainsToRegex(req.args["proxy-domain"])
44+
45+
for (const regex of regexs) {
46+
const match = reqDomain.match(regex)
47+
48+
if (match) {
49+
return match[1] // match[1] contains the port
50+
}
51+
}
52+
53+
return undefined
3254
}
3355

3456
router.all("*", async (req, res, next) => {

test/unit/node/cli.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ describe("parser", () => {
413413
const defaultArgs = await setDefaults(args)
414414
expect(defaultArgs).toEqual({
415415
...defaults,
416-
"proxy-domain": ["coder.com", "coder.org"],
416+
"proxy-domain": ["{{port}}.coder.com", "{{port}}.coder.org"],
417417
})
418418
})
419419
it("should allow '=,$/' in strings", async () => {
@@ -466,14 +466,14 @@ describe("parser", () => {
466466

467467
it("should set proxy uri", async () => {
468468
await setDefaults(parse(["--proxy-domain", "coder.org"]))
469-
expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.org")
469+
expect(process.env.VSCODE_PROXY_URI).toEqual("//{{port}}.coder.org")
470470
})
471471

472472
it("should set proxy uri to first domain", async () => {
473473
await setDefaults(
474474
parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]),
475475
)
476-
expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.com")
476+
expect(process.env.VSCODE_PROXY_URI).toEqual("//{{port}}.coder.com")
477477
})
478478

479479
it("should not override existing proxy uri", async () => {

0 commit comments

Comments
 (0)