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

--https mode causes issues in dev by trying to connect to wss://localhost:24678 #844

Closed
Conduitry opened this issue Apr 3, 2021 · 24 comments
Labels
bug Something isn't working
Milestone

Comments

@Conduitry
Copy link
Member

Conduitry commented Apr 3, 2021

Describe the bug
In --https mode, the site is served at https://localhost:3000 and so the Vite websocket connection that is attempted fails, because it tries to make it at wss://localhost:24678 when it exists at ws://localhost:24678. This results in the page being periodically reloaded.

Logs

Firefox can’t establish a connection to the server at wss://localhost:24678/.
[vite] server connection lost. polling for restart... client:340:13
[vite] connecting...

To Reproduce
npx svelte-kit dev --https

Open console and/or network logs and wait. Turn on timestamps on the console to help see what's happening. The page refresh rate seems to vary widely, and I haven't figured out what causes that to happen.

Expected behavior
I don't think we can force an HTTPS page to load a non-secured websocket, so we'll have to figure out how to serve a secure websocket over that port. This will also still probably cause issues because it will be a self-signed cert. I don't know how to resolve this.

Severity
High. HTTPS mode is not very usable if the browser refreshes periodically.

Additional context
I told you HTTPS mode was a mistake and you didn't believe me.

@Conduitry Conduitry added the bug Something isn't working label Apr 3, 2021
@Conduitry Conduitry changed the title --https mode causes issues with connecting to ws://localhost:24678 --https mode causes issues with connecting to wss://localhost:24678 Apr 3, 2021
@Conduitry Conduitry changed the title --https mode causes issues with connecting to wss://localhost:24678 --https mode causes issues in dev by trying to connect to wss://localhost:24678 Apr 3, 2021
@mindrones
Copy link
Member

I just got this, using npm run dev -- -H on SvelteKit v1.0.0-next.70. On Chrome it refreshes about every 4 secs, no logs apart from [vite] connecting...; in Firefox it refreshes about every sec and I got this log just once:

Details

sveltekit_https_periodic_refreshes

...then for some unknown reason it seems to start working (no refreshes).

I told you HTTPS mode was a mistake and you didn't believe me.

Well, in my case I'm testing the Web Bluetooth API in Chrome which requires HTTPS so I'm glad for the -H option! :)

@Conduitry
Copy link
Member Author

Conduitry commented Apr 3, 2021

This is just part of a larger issue with not being able to reach the hmr (?) server on that port. Rich had suggested trying to disable it when in https mode, but really it would be better if the port were configurable, if you could disable the feature separately from enabling https mode, and if its existence was documented for people who are e.g. running Kit inside Docker. Also ideally the whole page wouldn't reload when the server can't be reached.

@Conduitry
Copy link
Member Author

Vite lets this websocket server be configured with https://vitejs.dev/config/#server-hmr so we should be able to disable it and have a command line argument to control it.

@Conduitry
Copy link
Member Author

I've attempted to add an hmr: false to the server object at

server: {
...user_config.server,
middlewareMode: true
},
and the browser is still trying to connect to wss://localhost:24678/ so I'm not sure what's up with that or where else I might need to change something.

@bbuchanan
Copy link

This problem has stopped me dead in my tracks. I'm working on a new project w/ oauth and I need an https callback to receive my login token. I hope this gets addressed soon but I'm open to workarounds in the meantime. Thank you.

@aral
Copy link

aral commented Apr 11, 2021

In case it helps, in plain Vite + Svelte projects, simply setting the server works (with HMR). e.g., https://github.com/small-tech/site-vite-svelte/blob/main/vite.config.js#L17 (example taken from site-vite-svelte template for Site.js).

When I try to do the same thing in SvelteKit, the vite server settings are being ignored. With the following config, for example, the server starts at http://localhost:3000. (I’m assuming this falls under the vite-specific settings that SvelteKit overrides.)

const os = require('os')
const fs = require('fs')
const path = require('path')
const static = require('@sveltejs/adapter-static');
const pkg = require('./package.json');

// Note: the local TLS certificates will have been created at
// the post-install stage of npm install.
const certDirectory = path.join(os.homedir(), '.small-tech.org', 'auto-encrypt-localhost')
const cert = fs.readFileSync(path.join(certDirectory, 'localhost.pem'))
const key = fs.readFileSync(path.join(certDirectory, 'localhost-key.pem'))

/** @type {import('@sveltejs/kit').Config} */
module.exports = {
  kit: {
    // By default, `npm run build` will create a standard static app.
    // You can create optimized builds for different platforms by
    // specifying a different adapter
    adapter: static({pages: '.generated', assets: '.generated'}),

    // hydrate the <div id="svelte"> element in src/app.html
    target: '#svelte',

    files: {
      assets: '.kit/static',
      lib: '.kit/src/lib',
      routes: '.kit/src/routes',
      template: '.kit/src/app.html'
    },

    vite: {
      ssr: {
        noExternal: Object.keys(pkg.dependencies || {})
      },
      server: {
        port: 444,
        https: {key, cert}
      }
    }
  }
}

@mankins
Copy link

mankins commented Apr 28, 2021

#581 (comment) hinted at a way that seems to work for me:

Adding the following to svelte.config.cjs fixed the issue for me (so it uses ws instead of wss and port 3001)

	kit: {
		vite: {
			server: {
				hmr: {
					protocol: 'ws',
					port: 3001
				}
			}
		}
}

@Conduitry
Copy link
Member Author

Overriding the port doesn't appear to be necessary. Overriding the protocol only seems to be possible when accessing the site via https://localhost:.... I think there are special rules about localhost being considered a trusted origin, even unencrypted, so the browser lets an HTTPS site access an unencrypted websocket on localhost. If I navigate to https://127.0.0.1:..., I get the endless reloading, I believe because the browser refuses to connect to the unencrypted websocket on 127.0.0.1.

@moisesbites
Copy link

moisesbites commented Apr 30, 2021

server: {
hmr: {
protocol: 'ws',
port: 3001
}
}

@mankis thank you.

I used your code and it's working now. My Sveltekit app is behind a haproxy inside a docker container, because I have a API backend and a JWT cookie:

In haproxy:

backend frontend_server
    balance roundrobin
    server frontend_server_1 frontend_1:3000 cookie f1

IN docker-compose, for example, I open the port 3001:

  frontend_1:
    image: 'app/node'
    user: 'node'
    working_dir: '/home/node/app'
    volumes:
      - './frontend:/home/node/app'
    command: 'npm run dev'
    ports:
      - '3001:3001'
    networks:
      - frontend

And the result, using Firefox:
image

For development, I think this is the workaround solution, for while.

@JBusillo
Copy link
Contributor

Is there any reason to have a secure socket for HMR while testing? This socket is only used to notify the client to reload modules, to display an error overlay panel, etc. I believe that the web socket protocol can/should always be 'ws'.
If no protocol ('ws' or 'wss') is specified in vite.server.hmr.protocol, Vite will set the protocol according to its dev server protocol: 'http' --> 'ws', or 'https' --> 'wss'.
The current workaround is set the protocol manually in svelte.config.js, as described by @mankins:

kit: {
	vite: {
		server: {
			hmr: {
				protocol: 'ws',
			}
		}
	}
}

Would it be acceptable for SvelteKit to ensure that the protocol is always set to 'ws'?
I ran a little test with this change, which doesn't interfere with other HMR configurable settings, such as setting the port. It worked for me:

//  kit/src/core/dev/index.js  -- around line 105
			server: {
				...user_config.server,
				middlewareMode: true,
				hmr: { ...user_config.server?.hmr, protocol: 'ws' }
			},

Please advise if I'm stepping on anyone's toes or being redundant.

@Conduitry
Copy link
Member Author

That's better than nothing, but as I noted above, if you run afoul of the browser's mixed content rules, it will still cause infinite reloading.

@JBusillo
Copy link
Contributor

I believe I have a solution, while learning a lot about https and web sockets along the way.

In Vite's web socket implementation (vite/src/node/server/ws.ts), there is an undocumented option to receive the server reference that's created by the invoking application (in this case, SvelteKit). If a server is specified, it will attach the upgrade, connection and error handlers.

The challenge is to pass the reference of SvelteKit's server into Vite. The server reference needs to be passed in the config -- but the current code creates the config before the server is started. (i.e., 'vite.createServer' is called before 'get_server' in kit/src/core/dev/index.js)

I don't think that switching the order makes a difference -- at least it didn't in my testing. After making the switch, I also changed the following code:

// kit/src/core/dev/index.js
			server: {
				...user_config.server,
				middlewareMode: true,
				hmr: {
					...user_config.server?.hmr, server: this.https ? this.server : undefined
				}
			},

I also needed to change svelte.config.js to change the HMR port to 3000:

				hmr: {
					port: 3000
				},

and, I ran using svelte-kit dev -H --host <my-dev-server's-ip-address>

The two big questions are:

  • Will the Vite team give its blessing that 'server.hmr.server' is available for general use
  • Would switching the order of calling vite.createServer and get_server have any side-effects

I was able to access the server using my Android phone and a spare laptop.

@JBusillo
Copy link
Contributor

If interested, I have a proposal (with source changes and a README.md to explain them) to solve the issue, without reordering the 'mainline' dev process as described in my previous comment: Proposal

Let me know if I can help in any way.

@skoshx
Copy link

skoshx commented May 24, 2021

@Conduitry @JBusillo Might it just be a better use of time to settle with the workaround (running at localhost instead of 127.0.0.1), since the --https option is almost always used to test in localhost?

@JBusillo
Copy link
Contributor

PR #1517 addresses the web socket connection issues that allows using 'localhost', '127.0.0.1', or an exposed network IP address (using the --host option). There are users who want to test using external devices (smartphones, tablets) using SSL while running the dev server on their desktop/laptop. The workaround does not address these use cases.

The PR already has been coded and tested, and is now pending review, approval and deployment by the maintainers. I don't see much additional time spent on this issue/PR. However, I defer any further decisions to the maintainers.

@skoshx
Copy link

skoshx commented May 24, 2021

@JBusillo Ok, I didn't notice your PR! Great work, and I do agree with you, for instance with this PR i will be able to test Web Authentication API on my iOS phone, which I couldn't do before. Thanks a lot, hope the PR gets merged ASAP :)

@johnnysprinkles
Copy link
Contributor

johnnysprinkles commented May 28, 2021

I think I have a related issue -- I'm using SSL but of course not within SvelteKit itself, that seems crazy. SSL in front of it. So I spin up the dev server on 3000, put stunnel in front on 3001 or something, but now the call for insecure ws websocket on 24678 fails due to mixed security. I'm just looking for a way to make it serve the websocket on a different port than the client is looking for it on, so I can connect it together with stunnel or something similar.

@johnnysprinkles
Copy link
Contributor

I do have a jury rigged setup that works based on having two machines. I develop and run on my workstation and view in a web browser on my laptop, so I can ssh -L tunnel from my laptop 24678 to my workstation 24679, then stunnel on my workstation from 24679 to 24678. A zig and a zag. But I'd like to be able to do this on one machine.

@JBusillo
Copy link
Contributor

Here's a workaround that may work: PR 1517

It's messy, but hopefully a better solution will be implemented.

@johnnysprinkles
Copy link
Contributor

johnnysprinkles commented May 28, 2021

For my case, if they accept my PR in Vite it resolves. vitejs/vite#3578

benmccann added a commit that referenced this issue Jun 3, 2021
@Conduitry
Copy link
Member Author

This is fixed in @sveltejs/[email protected] - Thanks @JBusillo!

@skrenes
Copy link

skrenes commented Oct 12, 2021

I was having this issue due to using an Nginx reverse proxy with a HTTPS upgrade using a custom port (SvelteKit was using its default port 3000). Is there an easy way to have the client initiate a websocket connection using the same port as the browser @JBusillo?
For anyone in a similar situation to me, for now make sure the SvelteKit port matches the forwarded port. And note that at least for now, Apple doesn't support TLS 1.3 with websockets, so you need to make sure there's TLS 1.2 support.

@alexander-mart
Copy link
Contributor

I was having this issue due to using an Nginx reverse proxy with a HTTPS upgrade using a custom port (SvelteKit was using its default port 3000). Is there an easy way to have the client initiate a websocket connection using the same port as the browser @JBusillo? For anyone in a similar situation to me, for now make sure the SvelteKit port matches the forwarded port. And note that at least for now, Apple doesn't support TLS 1.3 with websockets, so you need to make sure there's TLS 1.2 support.

What version of SvelteKit do you use?
It should be fixed at @sveltejs/[email protected] (see comment)

@skrenes
Copy link

skrenes commented Oct 28, 2021

I was using the latest version at the time (18x). It works properly if the external port (for the reverse proxy) matches whatever is given to Sveltekit through the --port option. If the external port doesn't match, only the https connection is established, not wss. If I get some time this weekend, I'll retest to ensure this is strictly the case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests