Skip to content

Commit cf30b53

Browse files
authored
Merge pull request #2674 from nhooyr/absproxy
Add /absproxy to remove --proxy-path-passthrough
2 parents 4bace1a + 05a0f21 commit cf30b53

File tree

6 files changed

+73
-46
lines changed

6 files changed

+73
-46
lines changed

docs/FAQ.md

+16-12
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [Sub-paths](#sub-paths)
1717
- [Sub-domains](#sub-domains)
1818
- [Why does the code-server proxy strip `/proxy/<port>` from the request path?](#why-does-the-code-server-proxy-strip-proxyport-from-the-request-path)
19+
- [Proxying to Create React App](#proxying-to-create-react-app)
1920
- [Multi-tenancy](#multi-tenancy)
2021
- [Docker in code-server container?](#docker-in-code-server-container)
2122
- [How can I disable telemetry?](#how-can-i-disable-telemetry)
@@ -226,25 +227,28 @@ However many people prefer the cleaner aesthetic of no trailing slashes. This co
226227
to the base path as you cannot use relative redirects correctly anymore. See the above
227228
link.
228229
229-
For users who are ok with this tradeoff, pass `--proxy-path-passthrough` to code-server
230-
and the path will be passed as is.
230+
For users who are ok with this tradeoff, use `/absproxy` instead and the path will be
231+
passed as is. e.g. `/absproxy/3000/my-app-path`
231232
232-
This is particularly a problem with the `start` script in create-react-app. See
233+
### Proxying to Create React App
234+
235+
You must use `/absproxy/<port>` with create-react-app.
236+
See [#2565](https://github.com/cdr/code-server/issues/2565) and
233237
[#2222](https://github.com/cdr/code-server/issues/2222). You will need to inform
234-
create-react-app of the path at which you are serving via `homepage` field in your
235-
`package.json`. e.g. you'd add the following for the default CRA port:
238+
create-react-app of the path at which you are serving via `$PUBLIC_URL` and webpack
239+
via `$WDS_SOCKET_PATH`.
236240
237-
```json
238-
"homepage": "/proxy/3000",
241+
e.g.
242+
243+
```sh
244+
PUBLIC_URL=/absproxy/3000 \
245+
WDS_SOCKET_PATH=$PUBLIC_URL/sockjs-node \
246+
BROWSER=none yarn start
239247
```
240248

241-
Then visit `https://my-code-server-address.io/proxy/3000` to see your app exposed through
249+
Then visit `https://my-code-server-address.io/absproxy/3000` to see your app exposed through
242250
code-server!
243251

244-
Unfortunately `webpack-dev-server`'s websocket connections will not go through as it
245-
always uses `/sockjs-node`. So hot reloading will not work until the `create-react-app`
246-
team fix this bug.
247-
248252
Highly recommend using the subdomain approach instead to avoid this class of issue.
249253

250254
## Multi-tenancy

src/node/cli.ts

-5
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ export interface Args extends VsArgs {
5050
"show-versions"?: boolean
5151
"uninstall-extension"?: string[]
5252
"proxy-domain"?: string[]
53-
"proxy-path-passthrough"?: boolean
5453
locale?: string
5554
_: string[]
5655
"reuse-window"?: boolean
@@ -173,10 +172,6 @@ const options: Options<Required<Args>> = {
173172
"uninstall-extension": { type: "string[]", description: "Uninstall a VS Code extension by id." },
174173
"show-versions": { type: "boolean", description: "Show VS Code extension versions." },
175174
"proxy-domain": { type: "string[]", description: "Domain used for proxying ports." },
176-
"proxy-path-passthrough": {
177-
type: "boolean",
178-
description: "Whether the path proxy should leave the /proxy/<port> in the request path when proxying.",
179-
},
180175
"ignore-last-opened": {
181176
type: "boolean",
182177
short: "e",

src/node/proxy.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ proxy.on("error", (error, _, res) => {
99
})
1010

1111
// Intercept the response to rewrite absolute redirects against the base path.
12-
// Is disabled when the request has no base path which means --proxy-path-passthrough has
13-
// been enabled.
12+
// Is disabled when the request has no base path which means /absproxy is in use.
1413
proxy.on("proxyRes", (res, req) => {
1514
if (res.headers.location && res.headers.location.startsWith("/") && (req as any).base) {
1615
res.headers.location = (req as any).base + res.headers.location

src/node/routes/index.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,25 @@ export const register = async (
103103
app.use("/", domainProxy.router)
104104
wsApp.use("/", domainProxy.wsRouter.router)
105105

106-
app.use("/proxy", proxy.router)
107-
wsApp.use("/proxy", proxy.wsRouter.router)
106+
app.all("/proxy/(:port)(/*)?", (req, res) => {
107+
proxy.proxy(req, res)
108+
})
109+
wsApp.get("/proxy/(:port)(/*)?", (req, res) => {
110+
proxy.wsProxy(req as WebsocketRequest)
111+
})
112+
// These two routes pass through the path directly.
113+
// So the proxied app must be aware it is running
114+
// under /absproxy/<someport>/
115+
app.all("/absproxy/(:port)(/*)?", (req, res) => {
116+
proxy.proxy(req, res, {
117+
passthroughPath: true,
118+
})
119+
})
120+
wsApp.get("/absproxy/(:port)(/*)?", (req, res) => {
121+
proxy.wsProxy(req as WebsocketRequest, {
122+
passthroughPath: true,
123+
})
124+
})
108125

109126
app.use(bodyParser.json())
110127
app.use(bodyParser.urlencoded({ extended: true }))

src/node/routes/pathProxy.ts

+28-18
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1-
import { Request, Router } from "express"
1+
import { Request, Response } from "express"
2+
import * as path from "path"
23
import qs from "qs"
34
import { HttpCode, HttpError } from "../../common/http"
45
import { normalize } from "../../common/util"
56
import { authenticated, ensureAuthenticated, redirect } from "../http"
6-
import { proxy } from "../proxy"
7-
import { Router as WsRouter } from "../wsRouter"
7+
import { proxy as _proxy } from "../proxy"
8+
import { WebsocketRequest } from "../wsRouter"
89

9-
export const router = Router()
10-
11-
const getProxyTarget = (req: Request, passthroughPath: boolean): string => {
10+
const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
1211
if (passthroughPath) {
1312
return `http://0.0.0.0:${req.params.port}/${req.originalUrl}`
1413
}
1514
const query = qs.stringify(req.query)
1615
return `http://0.0.0.0:${req.params.port}/${req.params[0] || ""}${query ? `?${query}` : ""}`
1716
}
1817

19-
router.all("/(:port)(/*)?", (req, res) => {
18+
export function proxy(
19+
req: Request,
20+
res: Response,
21+
opts?: {
22+
passthroughPath?: boolean
23+
},
24+
): void {
2025
if (!authenticated(req)) {
2126
// If visiting the root (/:port only) redirect to the login page.
2227
if (!req.params[0] || req.params[0] === "/") {
@@ -28,22 +33,27 @@ router.all("/(:port)(/*)?", (req, res) => {
2833
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
2934
}
3035

31-
if (!req.args["proxy-path-passthrough"]) {
36+
if (!opts?.passthroughPath) {
3237
// Absolute redirects need to be based on the subpath when rewriting.
33-
;(req as any).base = `${req.baseUrl}/${req.params.port}`
38+
// See proxy.ts.
39+
;(req as any).base = req.path.split(path.sep).slice(0, 3).join(path.sep)
3440
}
3541

36-
proxy.web(req, res, {
42+
_proxy.web(req, res, {
3743
ignorePath: true,
38-
target: getProxyTarget(req, req.args["proxy-path-passthrough"] || false),
44+
target: getProxyTarget(req, opts?.passthroughPath),
3945
})
40-
})
41-
42-
export const wsRouter = WsRouter()
46+
}
4347

44-
wsRouter.ws("/(:port)(/*)?", ensureAuthenticated, (req) => {
45-
proxy.ws(req, req.ws, req.head, {
48+
export function wsProxy(
49+
req: WebsocketRequest,
50+
opts?: {
51+
passthroughPath?: boolean
52+
},
53+
): void {
54+
ensureAuthenticated(req)
55+
_proxy.ws(req, req.ws, req.head, {
4656
ignorePath: true,
47-
target: getProxyTarget(req, req.args["proxy-path-passthrough"] || false),
57+
target: getProxyTarget(req, opts?.passthroughPath),
4858
})
49-
})
59+
}

test/proxy.test.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ describe("proxy", () => {
77
const nhooyrDevServer = new httpserver.HttpServer()
88
let codeServer: httpserver.HttpServer | undefined
99
let proxyPath: string
10+
let absProxyPath: string
1011
let e: express.Express
1112

1213
beforeAll(async () => {
1314
await nhooyrDevServer.listen((req, res) => {
1415
e(req, res)
1516
})
1617
proxyPath = `/proxy/${nhooyrDevServer.port()}/wsup`
18+
absProxyPath = proxyPath.replace("/proxy/", "/absproxy/")
1719
})
1820

1921
afterAll(async () => {
@@ -43,11 +45,11 @@ describe("proxy", () => {
4345
})
4446

4547
it("should not rewrite the base path", async () => {
46-
e.get(proxyPath, (req, res) => {
48+
e.get(absProxyPath, (req, res) => {
4749
res.json("joe is the best")
4850
})
49-
;[, , codeServer] = await integration.setup(["--auth=none", "--proxy-path-passthrough=true"], "")
50-
const resp = await codeServer.fetch(proxyPath)
51+
;[, , codeServer] = await integration.setup(["--auth=none"], "")
52+
const resp = await codeServer.fetch(absProxyPath)
5153
expect(resp.status).toBe(200)
5254
const json = await resp.json()
5355
expect(json).toBe("joe is the best")
@@ -69,15 +71,15 @@ describe("proxy", () => {
6971
})
7072

7173
it("should not rewrite redirects", async () => {
72-
const finalePath = proxyPath.replace("/wsup", "/finale")
73-
e.post(proxyPath, (req, res) => {
74+
const finalePath = absProxyPath.replace("/wsup", "/finale")
75+
e.post(absProxyPath, (req, res) => {
7476
res.redirect(307, finalePath)
7577
})
7678
e.post(finalePath, (req, res) => {
7779
res.json("redirect success")
7880
})
79-
;[, , codeServer] = await integration.setup(["--auth=none", "--proxy-path-passthrough=true"], "")
80-
const resp = await codeServer.fetch(proxyPath, {
81+
;[, , codeServer] = await integration.setup(["--auth=none"], "")
82+
const resp = await codeServer.fetch(absProxyPath, {
8183
method: "POST",
8284
})
8385
expect(resp.status).toBe(200)

0 commit comments

Comments
 (0)