vite icon indicating copy to clipboard operation
vite copied to clipboard

Support Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy on hmr dev server

Open ngbrown opened this issue 1 year ago • 5 comments

Description

Like #3909 but for the HMR dev server. During development I have the COEP header with a value of require-corp to access certain features like SharedArrayBuffer objects or Performance.now() with unthrottled timers. (cross-origin isolated features).

From MDN:

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

The problem is that when the server is restarting, the HMR WebSocket connection switches to polling on the HMR port ("[vite] server connection lost. polling for restart..."). However, the browser is giving this error:

The resource at “http://localhost:8002/” was blocked due to its Cross-Origin-Resource-Policy header (or lack thereof). See https://developer.mozilla.org/docs/Web/HTTP/Cross-Origin_Resource_Policy_(CORP)

The resource never connects and the page doesn't automatically reload. I have to manually reload the page so it can establish the WebSocket connection. This also happens when vite.config.ts changes.

I tried setting the server.headers option, but it doesn't affect the HMR server endpoint. (This is an Remix app hosted in Express, so not actually using the Vite dev server, just the HMR server).

The server.hmr.server option seems to take a class instance and there is no server.hmr.headers option.

Suggested solution

Add a server.hmr.headers option like was implemented in #5580.

The headers I need to set would look like this:

{
  server: {
    hmr: {
      headers: {
        "cross-origin-resource-policy": "cross-origin",
      },
    },
  },
}

Alternative

I could remove the Cross-Origin-Embedder-Policy and Cross-Origin-Opener-Policy headers while in development, but the cross-origin isolated browser features would be disabled.

Additional context

No response

Validations

ngbrown avatar Apr 26 '24 16:04 ngbrown

This is an Remix app hosted in Express, so not actually using the Vite dev server, just the HMR server

Can you provide a reproduction to show how the issue look like? It's not entirely clear that how you setup coop/coep headers on Remix express, so providing that would probably help.

You might be able to avoid this issue by passing your own node server to server.hmr.server (for example, something like I suggested for Remix Vite https setup long time ago https://github.com/hi-ogawa/test-remix-vite-express-https/blob/06da66d41950b8b0b3014f4f841d07e6fcffc7bf/server.mjs#L34), but I might be totally misunderstanding the issue.

Starting from their express template https://github.com/remix-run/remix/tree/main/templates/express, this is what I imagined would help https://stackblitz.com/edit/github-1jwsyy?file=server.js

hi-ogawa avatar Apr 27 '24 02:04 hi-ogawa

Here is a demonstration of the way I setup my headers and how the problem manifests: https://github.com/ngbrown/remix-vite-16536

ngbrown avatar Apr 27 '24 16:04 ngbrown

@hi-ogawa - I tried to change the server.js file with the way you passed server into the Vite hmr property, it does work by re-using the same server instance as the rest of the application, but I had a few questions:

  • ~Wouldn't it be better to have a separate port like HMR is expected to have?~ Sharing the same port is how the default Remix template works.
  • Since this is hosted at the same port as the rest of the application. How would it work if the application had it's own WebSocket connection that it wanted to maintain? Wouldn't they conflict?

I've updated my suggested solution with the headers I would like to set, assuming the HMR server is on a different port.

ngbrown avatar Apr 27 '24 16:04 ngbrown

Since this is hosted at the same port as the rest of the application. How would it work if the application had it's own WebSocket connection that it wanted to maintain? Wouldn't they conflict?

Yeah, that could be a concern, but maybe it's still possible to get around by setting server.hmr.path to not conflict with your app's websocket path?

https://github.com/vitejs/vite/blob/37190afc150fea4ec4586db2cef69d1f084fa224/packages/vite/src/node/server/ws.ts#L116


I tested your reproduction and I can confirm the issue, but it feels like a minor paper cut with very specific conditions. Also I wonder if this is more like a problem of Vite's client-side websocket failure robustness. See for example:

  • https://github.com/vitejs/vite/issues/5675#issuecomment-1345588419

hi-ogawa avatar Apr 28 '24 01:04 hi-ogawa

Hello! My users are running into this problem unfortunately when using Triplex (a visual editor, see: https://triplex.dev). Vite is used for the local development backend.

Triplex is available as a standalone Electron app as well as an extension inside VS Code. I've gotten feedback around the editor becoming unresponsive after their OS sleeps, which e.g. happens immediately when closing a macOS lapto. When this happens they're forced to close the editor and re-open.

In the Electron app I've been able to work around the problem by forcing headers onto all web server responses:

 window.webContents.session.webRequest.onHeadersReceived(
        (details, callback) => {
          callback({
            responseHeaders: {
              ...details.responseHeaders,
              "Cross-Origin-Embedder-Policy": "require-corp",
              "Cross-Origin-Opener-Policy": "same-origin",
              "Cross-Origin-Resource-Policy": "cross-origin",
            },
          });
        },
      );

When the OS goes to sleep and is woken up they can continue their session right where they left off. Unfortunately I don't have this flexibility in the VS Code extension.

Having the ability to set the headers on the HMR response would be great! There does exist the hmr.server option which I'm going to see if I can use and set the headers for the response myself though, will report back.

itsdouges avatar Sep 26 '24 10:09 itsdouges