Skip to content

Commit

Permalink
Deploy beta docs
Browse files Browse the repository at this point in the history
  • Loading branch information
lydell committed Jan 7, 2024
1 parent 096f235 commit fe3f386
Show file tree
Hide file tree
Showing 3 changed files with 287 additions and 16 deletions.
4 changes: 4 additions & 0 deletions docs/elm-watch.json.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type NonEmptyArray<T> = [T, ...Array<T>];
type ElmWatchJson = {
postprocess?: NonEmptyArray<string>;
port?: number;
webSocketUrl?: string; // ⚠ elm-watch@beta only
serve?: string; // ⚠ elm-watch@beta only
targets: {
[name: string]: {
inputs: NonEmptyArray<string>;
Expand Down Expand Up @@ -56,6 +58,8 @@ Example:
| [targets](#targets) | `Record<string, object>` | _Required_ | The input Elm files to compile and the output JavaScript files to write to. At least one target is required. |
| [postprocess](../postprocess/) | `NonEmptyArray<string>` | No postprocessing. | A command to run after each `elm make` to transform Elm’s JavaScript output. |
| port | `number` | An arbitrary available port. Tries to re-use the same port as last time you ran elm-watch. | The port for elm-watch’s HTTP and WebSocket server, used for hot reloading and as a simple file server. In case you _have_ to have the exact same port every time. Note that [some ports cannot be used][port-blocking]. |
| ⚠️ webSocketUrl | `string` | `` `ws://${currentHostname}:${port}/elm-watch` `` (sort of) | **Only available in `elm-watch@beta`.** This lets you customize how the elm-watch client connects its WebSocket for advanced use cases. You can also use the `ELM_WATCH_WEBSOCKET_URL` environment variable for dynamically setting it (the environment variable takes precedence). The value must be a valid URL starting with `ws:` or `wss:`. |
| ⚠ serve | `string` | unset | **Only available in `elm-watch@beta`.** A directory of static files to [serve](../server/). |

## targets

Expand Down
55 changes: 51 additions & 4 deletions docs/https.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ nav_order: 11
# HTTPS

{: .info }
**TL;DR:** I recommend using `http://` for local development. If you really want `https://`, accept elm-watch’s “unsafe” self-signed SSL certificate.
**TL;DR:** I recommend using `http://` for local development. If you really want `https://`, there are ways to set that up yourself.

I’d say it’s the most common to use plain old `http://` when working on `localhost`. One could argue that `https://` would be better even for local development since it’s closer to your production environment (which most likely uses `https://`). To be honest, I’ve tried using `https://` for local development and can’t remember a single time it saved me from a bug. Instead it just complicates things with certificates.
I’d say it’s the most common to use plain old `http://` when working on `localhost`. One could argue that `https://` would be better even for local development since it’s closer to your production environment (which most likely uses `https://`). To be honest, I’ve tried using `https://` for local development and can’t remember a single time it saved me from a bug. Instead it just complicates things with certificates. But there are some niche web features that are only available HTTPS, even on `localhost`.

With elm-watch HTTPS causes a new complexity: elm-watch uses WebSockets for hot reloading, which results in the question of `ws://` vs `wss://`.

Expand All @@ -17,16 +17,63 @@ elm-watch uses:
- `ws://` on `http://` pages.
- `wss://` on `https://` pages.

elm-watch runs an HTTP server, because WebSockets connect over HTTP before switching to the WebSocket protocol. Now, things differ a little bit depending on the elm-watch version:

- elm-watch 1.0.2 and older only runs an HTTP server.
- elm-watch 1.1.0 added some support for HTTPS: It runs both and HTTP server and an HTTPS server.
- elm-watch beta removes the HTTPS server, but lets you set that up yourself.

## elm-watch 1.1

If you use `https://`, then the first time you visit your page you’ll see how elm-watch’s WebSocket gets stuck in the 🔌 connecting state. In the browser console you might see messages about connection errors due to an invalid certificate. You need to accept the certificate to make it work.

Click elm-watch’s [browser UI](../browser-ui/) to expand it. There’s a link there that goes to the WebSocket server. When you click it, your browser will show a scary-looking security screen. That’s because elm-watch uses a self-signed certificate, which isn’t secure. However, there’s no security to worry about here – elm-watch just needs a certificate to be able to use `wss://` (which is basically required on `https://` pages – more on that below). Click a few buttons to proceed to the page anyway. Once you’ve done that once, the browser remembers your choice. Go back to your page (and possibly refresh the page) and now the WebSocket should connect! If you’ve ever created a self-signed certificate yourself for development – that’s exactly what’s happening here. elm-watch ships with a generic self-signed certificate created with `openssl`.

If you’d like to be able to configure the certificate used by elm-watch, let me know!
Using a self-signed certificate isn’t ideal, and cannot be used by everyone. Also, running both HTTP and HTTPS in elm-watch is pretty complicated. This is why `elm-watch@beta` switched to a new approach, where you are in full control over HTTPS.

## elm-watch beta

`elm-watch@beta` puts its HTTP server to more use than just connecting WebSockets: It also optionally [serves static files](../server/). That static file server is HTTP, not HTTPS, but the code for choosing between `ws://` and `wss://` based on if you’re on an `https://` page is still there. How can it be `https://` then? That’s if you serve the files yourself on your own HTTPS server (or if you run elm-watch in a certain way – which I’ll get back to).

Like in elm-watch 1.1, if you use `https://` then you might see how elm-watch’s WebSocket gets stuck in the 🔌 connecting state. That’s because it tries to connect with `wss://` over HTTPS, but elm-watch only runs an HTTP server. In the [browser UI](../browser-ui/), instead of showing a link to a page where you can accept a self-signed certificate, `elm-watch@beta` now just links to this page instead, where you can read up on how to get HTTPS going.

If you use your own HTTPS server, you can set the `"webSocketUrl"` option in [elm-watch.json](../elm-watch.json/) or the `ELM_WATCH_WEBSOCKET_URL` environment variable to make elm-watch connect to your HTTPS server instead of directly to elm-watch’s HTTP server. In your HTTPS server you need to proxy the WebSocket to elm-watch. Alternatively, you can set up a separate HTTPS proxy server just for elm-watch’s WebSocket if you prefer.

You can also run elm-watch in an alternate way with a [custom server](../server/#custom-server) to set up HTTPS:

```js
import * as fs from "node:fs";
import * as https from "node:https";
import * as path from "node:path";
import * as url from "node:url";
import elmWatch from "elm-watch";

const DIRNAME = path.dirname(url.fileURLToPath(import.meta.url));

// Deal with certificates and HTTPS options in whatever way you’d like:
const CERTIFICATE = {
key: fs.readFileSync(path.join(DIRNAME, "certificate", "dev.key")),
cert: fs.readFileSync(path.join(DIRNAME, "certificate", "dev.crt")),
};

elmWatch(process.argv.slice(2), {
createServer: ({ onRequest, onUpgrade }) =>
https.createServer(CERTIFICATE, onRequest).on("upgrade", onUpgrade),
})
.then((exitCode) => {
process.exit(exitCode);
})
.catch((error) => {
console.error("Unexpected elm-watch error:", error);
});
```
## Research
Here are my findings from testing different combinations of http/s, ws/s, localhost vs not-localhost, and self-signed vs valid certificates:
✅ = Works.
🤕 = Works with workaround: If elm-watch is using port 12345, you need to visit for example https://localhost:12345 once and accept the self-signed certificate.
🤕 = Works with workaround: If the WebSocket connects to port 12345, you need to visit for example https://localhost:12345 once and accept the self-signed certificate.
💥 = `new WebSocket("ws://...")` immediately throws an error (that can be caught using `try-catch`).
❌ = `new WebSocket("ws://...")` throws no error, but the WebSocket never connects.
📢 = A warning is logged to the browser console. It cannot be turned off.
Expand Down
Loading

0 comments on commit fe3f386

Please sign in to comment.