Repository: coder/code-server (TypeScript)
Task type: Feature

Issue:
[Feat]: make VSCODE_PROXY_URI use the subdomain proxy when it is enabled
## What is your suggestion?

When `VSCODE_PROXY_URI` is enabled, use the subdomain proxy. 

## Why do you want this feature?

Popular extensions like Tabnine can't use relative paths and need to be able to talk to code-server on specific ports/paths in order to work correctly.

## Are there any workarounds to get this functionality today?

Port forwarding but this isn't always possible.

## Are you interested in submitting a PR for this?

Yes, with more context. 



Discussion:
We might also want a way to override this for cases like Coder where we already provide a subdomain proxy outside of code-server.  For this we can probably just check if that variable is already set and if so avoid overriding.

To implement we need to check the `proxy-domain` flag and use that in the environment variable.  It can be defined multiple times so maybe we just use the first one.  So more or less I think it would be `{{port}}.${args["proxy-domain"][0]}`.  If the flag is not set we just keep using the path-based proxy.

I also think we should go ahead and patch `asExternalUri` to use this same environment variable although we should use the other ticket for that (and a separate PR).

Solution patch:
diff --git a/src/node/cli.ts b/src/node/cli.ts
--- a/src/node/cli.ts
+++ b/src/node/cli.ts
@@ -571,6 +571,9 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
   // Filter duplicate proxy domains and remove any leading `*.`.
   const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, "")))
   args["proxy-domain"] = Array.from(proxyDomains)
+  if (args["proxy-domain"].length > 0 && !process.env.VSCODE_PROXY_URI) {
+    process.env.VSCODE_PROXY_URI = `{{port}}.${args["proxy-domain"][0]}`
+  }
 
   if (typeof args._ === "undefined") {
     args._ = []


Test patch:
diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts
--- a/test/unit/node/cli.test.ts
+++ b/test/unit/node/cli.test.ts
@@ -43,6 +43,7 @@ describe("parser", () => {
     delete process.env.PASSWORD
     delete process.env.CS_DISABLE_FILE_DOWNLOADS
     delete process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE
+    delete process.env.VSCODE_PROXY_URI
     console.log = jest.fn()
   })
 
@@ -457,6 +458,31 @@ describe("parser", () => {
       port: 8082,
     })
   })
+
+  it("should not set proxy uri", async () => {
+    await setDefaults(parse([]))
+    expect(process.env.VSCODE_PROXY_URI).toBeUndefined()
+  })
+
+  it("should set proxy uri", async () => {
+    await setDefaults(parse(["--proxy-domain", "coder.org"]))
+    expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.org")
+  })
+
+  it("should set proxy uri to first domain", async () => {
+    await setDefaults(
+      parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]),
+    )
+    expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.com")
+  })
+
+  it("should not override existing proxy uri", async () => {
+    process.env.VSCODE_PROXY_URI = "foo"
+    await setDefaults(
+      parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]),
+    )
+    expect(process.env.VSCODE_PROXY_URI).toEqual("foo")
+  })
 })
 
 describe("cli", () => {