Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unstable_dev in v1.16.0 is incompatible with GitHub Codespaces for Web (& other port forwarding) #6379

Closed
1 task done
huw opened this issue May 12, 2023 · 5 comments
Closed
1 task done

Comments

@huw
Copy link
Contributor

huw commented May 12, 2023

What version of Remix are you using?

v1.16.0

Are all your remix dependencies & dev-dependencies using the same version?

  • Yes

Background

GitHub Codespaces for Web, and other web-based containerised environments, solve the problem of securely accessing other ports by forwarding them to URLs on port 80 that include the original port in the URL.

For example, if I open the following 3 ports as part of my dev process:

  1. http://localhost:3000: My app (HTTP)
  2. http://localhost:3001: Remix dev server (HTTP)
  3. http://localhost:3002: Remix HMR server (WebSocket)

Codespaces will allow me to access them on the following URLs:

  1. https://<CODESPACE-NAME>-3000.preview.app.github.dev: My app (HTTP)
  2. https://<CODESPACE-NAME>-3001.preview.app.github.dev: Remix dev server (HTTP)
  3. https://<CODESPACE-NAME>-3002.preview.app.github.dev: Remix HMR server (WebSocket)

One of the issues with this format is that, codespace-to-codespace, these URLs will vary. Both the <CODESPACE-NAME> and preview.app.github.dev components of the URL can change. These can be accessed in the Codespaces environment using the CODESPACE_NAME and GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN environment variables, as well as the CODESPACES environment variable, which will be true if we’re in a Codespace environment. See more in GitHub’s documentation.

The other issue with this format is that you have to visit the URL first to authenticate with it and save the appropriate cookie. Unfortunately, Codespaces doesn’t appropriately forward the OPTIONS request for CORS assets (such as those linked via <link rel=modulepreload>). More on this later.

Behaviour in the stable dev server

Pre-HMR, there was no issue. All of my assets were hosted on the port-3000 server, and I would visit https://<CODESPACE-NAME>-3000.preview.app.github.dev to access them.

Behaviour in the pre-v1.16.0 unstable_dev server

In the last version of Remix, I was able to use HMR by hooking up my app’s server (port 3000) with the HMR WebSocket (in this example, port 3002). This doesn’t work under the default templates, because <LiveReload /> generates a component that looks for http://localhost:3002 instead of https://<CODESPACE-NAME>-3002.preview.app.github.dev. I was able to patch this by writing my own <LiveReload /> component, detailed here.

Behaviour in the v1.16.0 unstable_dev server

This version of the dev server serves JavaScript & CSS assets at a new url, http://localhost:3001, and rewrites the URLs pointing to the app server (port 3000) to the new URL.

Unfortunately, this has the same issue as the HMR above, albeit with a less obvious fix. Loading my app through the Codespaces URL (https://<CODESPACE-NAME>-3000.preview.app.github.dev) fails to load the CSS & JS, because it’s looking for http://localhost:3001, when it should be looking for https://<CODESPACE-NAME>-3001.preview.app.github.dev.

If I set --http-host <CODESPACE-NAME>-3001.preview.app.github.dev and --http-port 3001, then my client looks for https://<CODESPACE-NAME>-3001.preview.app.github.dev:3001, which is also incorrect. If I try to set --http-port 80, I get an EACCESS.

Issue A: The dev server writes the wrong port on the asset URL

Diagnosis

The issue is that the dev server hosts assets on the same port that it’s trying to rewrite to. The following line is how Remix creates the URLs for the clients to search from:

let stringifyOrigin = (o: Origin) => `${o.scheme}://${o.host}:${o.port}`;

Workaround

I can pnpm patch the package to make the line into:

let stringifyOrigin = o => process.env.CODESPACES === "true" ? `https://${process.env.CODESPACE_NAME}-${o.port}.${process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}` : `${o.scheme}://${o.host}:${o.port}`;

Issue B: Codespaces blocks the CORS OPTIONS request for the JS assets

Diagnosis

After patching Issue A, my browser is able to load the CSS assets, which are behind a <link rel=stylesheet>, which doesn’t use CORS. However, I get a tonne of blocked CORS requests for the JS assets, because they’re using <link rel=modulepreload>.

This is because, for a privately-forwarded Codespaces port (i.e. behind authentication), Codespaces 302s to a sign-in link. Unfortunately, this URL doesn’t respond with an Access-Control-Allow-Origin header, which causes the browser to block the request. This is discussed further in github/community#15351.

Workaround

The only workaround is to set the dev server port (3001) to ‘public visibility’ in Codespaces. This will prevent it from redirecting to the sign-in link, but it exposes all your assets to the clearweb while your server is running. (There might also be some browser flags but I am definitely not risking disabling those).

(If you’re a new Codespaces user seeing this issue for the first time, you’ll also want to implement the HMR workaround)

Why is this an issue?

There is no way to use the dev server, with or without HMR, without patching the package today and exposing your assets to the clearweb. Given that Codespaces for Web (or other similar non-standard setups, c.f. #2859) is fairly niche, I don’t expect this to be a high-priority issue. I’m raising it mainly to document that it exists & that there is a reasonable workaround that requires some user-side maintenance.

Proposed Solutions

I am happy to PR both if the maintainers agree. However, due to Issue B, Codespaces still won’t work out of the box without (a) architectural changes to the dev server or (b) Codespaces-specific code in the Remix codebase. Both seem like bad ideas. Perhaps novice Codespaces users should just revert to running their own build watcher and forgoing HMR?

Issue A (Asset URLs)

The best way to handle this would be to expose separate config options for the hosting & fetching ports. These could then be dynamically computed in remix.config.js for Codespaces users. Codespaces users would then be able to dynamically inject their config using the environment variables discussed above.

Issue B (CORS)

Ideally, Codespaces fix this upstream. I wonder if the host process could intercept requests on the default server port (ex. 3000) and split them between the managed subprocess and the asset server?

@kiliman
Copy link
Collaborator

kiliman commented May 12, 2023

If I set --http-host -3001.preview.app.github.dev and --http-port 3001, then my client looks for https://-3001.preview.app.github.dev:3001, which is also incorrect. If I try to set --http-port 80, I get an EACCESS.

What happens if you set --http-port 443, which is the HTTPS port?

@huw
Copy link
Contributor Author

huw commented May 12, 2023

Also EACCESS, unfortunately.

@huw
Copy link
Contributor Author

huw commented May 12, 2023

I’ve updated the issue with a second sub-issue I discovered after the fact. Once patching the initial problem, all of the JS assets also get blocked by CORS. There is no easy workaround for this that doesn’t involve exposing all your development assets to the clearweb.

@pcattori
Copy link
Contributor

Serving assets from dev server was reverted (#6306), which is available on nightly.

I believe that should solve both of these issues.

@huw
Copy link
Contributor Author

huw commented May 16, 2023

Thank you! That solved it.

@huw huw closed this as completed May 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants