From 114a589081c06ffd7003eb0820a2d5eedd67e308 Mon Sep 17 00:00:00 2001 From: Arad Alvand Date: Sun, 18 Jun 2023 18:44:37 +0330 Subject: [PATCH 1/3] Add HTTPS and HTTP/2 support for `adapter-node` --- .changeset/mighty-wasps-perform.md | 5 +++ .../25-build-and-deploy/40-adapter-node.md | 12 +++++++ packages/adapter-node/src/env.js | 7 +++- packages/adapter-node/src/index.js | 33 ++++++++++++++++--- 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 .changeset/mighty-wasps-perform.md diff --git a/.changeset/mighty-wasps-perform.md b/.changeset/mighty-wasps-perform.md new file mode 100644 index 000000000000..96c13ed481c2 --- /dev/null +++ b/.changeset/mighty-wasps-perform.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': minor +--- + +feat: add opt-in HTTPS and HTTP/2 support diff --git a/documentation/docs/25-build-and-deploy/40-adapter-node.md b/documentation/docs/25-build-and-deploy/40-adapter-node.md index 575c6b5296df..1e4bf8490ed7 100644 --- a/documentation/docs/25-build-and-deploy/40-adapter-node.md +++ b/documentation/docs/25-build-and-deploy/40-adapter-node.md @@ -110,6 +110,18 @@ We instead read from the _right_, accounting for the number of trusted proxies. The maximum request body size to accept in bytes including while streaming. Defaults to 512kb. You can disable this option with a value of 0 and implement a custom check in [`handle`](hooks#server-hooks-handle) if you need something more advanced. +### `CERT_PATH`, `CERT_KEY_PATH`, and `HTTPS_PORT`: +By default, the server only listens for plain HTTP requests. To enable an additional HTTPS endpoint, you must provide the `CERT_PATH` and `CERT_KEY_PATH` environment variables pointing to a file containing a valid certificate and a file containing its associated private key respectively, both in the PEM format. +The files are read once on startup, so if the contents of the files change, the app will need to be restarted. + +The default port that will be used for the HTTPS endpoint is 3001. You can customize this via the `HTTPS_PORT` environment variable. + +### `NO_HTTP2`: +By default, the HTTPS endpoint will support HTTP/2 as well as HTTP/1.1. If, for some reason, supporting HTTP/2 isn't desirable, you can disable it by setting the `NO_HTTP2` environment variable to a non-empty value (e.g. `1`). + +### `ONLY_HTTPS`: +If you only need an HTTPS endpoint and want to opt out of the default plain HTTP one, you can do so by setting the `ONLY_HTTPS` environment variable to a non-empty value (e.g. `1`). + ## Options The adapter can be configured with various options: diff --git a/packages/adapter-node/src/env.js b/packages/adapter-node/src/env.js index 841bddf2c237..ec791b3cdc29 100644 --- a/packages/adapter-node/src/env.js +++ b/packages/adapter-node/src/env.js @@ -9,7 +9,12 @@ const expected = new Set([ 'ADDRESS_HEADER', 'PROTOCOL_HEADER', 'HOST_HEADER', - 'BODY_SIZE_LIMIT' + 'BODY_SIZE_LIMIT', + 'CERT_PATH', + 'CERT_KEY_PATH', + 'HTTPS_PORT', + 'ONLY_HTTPS', + 'NO_HTTP2' ]); if (ENV_PREFIX) { diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index ea3feee05860..5b48daa2f47a 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -1,15 +1,38 @@ import { handler } from 'HANDLER'; import { env } from 'ENV'; import polka from 'polka'; +import http from 'node:http'; +import https from 'node:https'; +import http2 from 'node:http2'; +import fs from 'node:fs'; export const path = env('SOCKET_PATH', false); export const host = env('HOST', '0.0.0.0'); export const port = env('PORT', !path && '3000'); +export const cert_path = env('CERT_PATH', false); +export const cert_key_path = env('CERT_KEY_PATH', false); +export const https_port = env('HTTPS_PORT', !path && '3001'); +export const only_https = env('ONLY_HTTPS', false); +export const no_http2 = env('NO_HTTP2', false); -const server = polka().use(handler); +const app = polka().use(handler); -server.listen({ path, host, port }, () => { - console.log(`Listening on ${path ? path : host + ':' + port}`); -}); +if (!only_https) { + http.createServer(app.handler).listen({ path, host, port }, () => { + console.log(`Listening on http://${path ? path : host + ':' + port}`); + }); +} -export { server }; +if (cert_path && cert_key_path) { + const cert = fs.readFileSync(cert_path); + const key = fs.readFileSync(cert_key_path); + const https_server = no_http2 + ? https.createServer({ cert, key }, app.handler) + : http2.createSecureServer({ allowHTTP1: true, cert, key }, app.handler); + + https_server.listen({ path, host, https_port }, () => { + console.log(`Listening on https://${path ? path : host + ':' + https_port}`); + }); +} + +export { app as server }; From 43c5b897a448df95b7d1a735c24e81819ff843d1 Mon Sep 17 00:00:00 2001 From: Arad Alvand Date: Sun, 18 Jun 2023 21:01:56 +0330 Subject: [PATCH 2/3] Suppress Polka's `app.handler` type errors --- packages/adapter-node/src/index.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index 5b48daa2f47a..9a2ccc205876 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -18,6 +18,8 @@ export const no_http2 = env('NO_HTTP2', false); const app = polka().use(handler); if (!only_https) { + // TODO Remove the `@ts-expect-error`s below once https://github.com/lukeed/polka/issues/194 is fixed + // @ts-expect-error http.createServer(app.handler).listen({ path, host, port }, () => { console.log(`Listening on http://${path ? path : host + ':' + port}`); }); @@ -27,10 +29,12 @@ if (cert_path && cert_key_path) { const cert = fs.readFileSync(cert_path); const key = fs.readFileSync(cert_key_path); const https_server = no_http2 - ? https.createServer({ cert, key }, app.handler) - : http2.createSecureServer({ allowHTTP1: true, cert, key }, app.handler); + ? // @ts-expect-error + https.createServer({ cert, key }, app.handler) + : // @ts-expect-error + http2.createSecureServer({ allowHTTP1: true, cert, key }, app.handler); - https_server.listen({ path, host, https_port }, () => { + https_server.listen({ path, host, port: https_port }, () => { console.log(`Listening on https://${path ? path : host + ':' + https_port}`); }); } From 083af9a69237bb6d8a4df00a6d2b1c6c8767961a Mon Sep 17 00:00:00 2001 From: Arad Alvand Date: Fri, 23 Jun 2023 13:33:26 +0330 Subject: [PATCH 3/3] Add a note in the docs about using a reverse proxy --- .../25-build-and-deploy/40-adapter-node.md | 2 ++ packages/adapter-node/src/index.js | 24 +++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/documentation/docs/25-build-and-deploy/40-adapter-node.md b/documentation/docs/25-build-and-deploy/40-adapter-node.md index 1e4bf8490ed7..fdb464d33d17 100644 --- a/documentation/docs/25-build-and-deploy/40-adapter-node.md +++ b/documentation/docs/25-build-and-deploy/40-adapter-node.md @@ -116,6 +116,8 @@ The files are read once on startup, so if the contents of the files change, the The default port that will be used for the HTTPS endpoint is 3001. You can customize this via the `HTTPS_PORT` environment variable. +> We generally recommend you use a reverse proxy in front of your application to enable HTTPS instead, and have your reverse proxy communicate with your SvelteKit application via plain HTTP; only use these options when you know what you're doing, or as a last resort. + ### `NO_HTTP2`: By default, the HTTPS endpoint will support HTTP/2 as well as HTTP/1.1. If, for some reason, supporting HTTP/2 isn't desirable, you can disable it by setting the `NO_HTTP2` environment variable to a non-empty value (e.g. `1`). diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index 9a2ccc205876..5122a59dc9f3 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -9,15 +9,15 @@ import fs from 'node:fs'; export const path = env('SOCKET_PATH', false); export const host = env('HOST', '0.0.0.0'); export const port = env('PORT', !path && '3000'); -export const cert_path = env('CERT_PATH', false); -export const cert_key_path = env('CERT_KEY_PATH', false); -export const https_port = env('HTTPS_PORT', !path && '3001'); -export const only_https = env('ONLY_HTTPS', false); -export const no_http2 = env('NO_HTTP2', false); +export const certPath = env('CERT_PATH', false); +export const certKeyPath = env('CERT_KEY_PATH', false); +export const httpsPort = env('HTTPS_PORT', !path && '3001'); +export const onlyHttps = env('ONLY_HTTPS', false); +export const noHttp2 = env('NO_HTTP2', false); const app = polka().use(handler); -if (!only_https) { +if (!onlyHttps) { // TODO Remove the `@ts-expect-error`s below once https://github.com/lukeed/polka/issues/194 is fixed // @ts-expect-error http.createServer(app.handler).listen({ path, host, port }, () => { @@ -25,17 +25,17 @@ if (!only_https) { }); } -if (cert_path && cert_key_path) { - const cert = fs.readFileSync(cert_path); - const key = fs.readFileSync(cert_key_path); - const https_server = no_http2 +if (certPath && certKeyPath) { + const cert = fs.readFileSync(certPath); + const key = fs.readFileSync(certKeyPath); + const https_server = noHttp2 ? // @ts-expect-error https.createServer({ cert, key }, app.handler) : // @ts-expect-error http2.createSecureServer({ allowHTTP1: true, cert, key }, app.handler); - https_server.listen({ path, host, port: https_port }, () => { - console.log(`Listening on https://${path ? path : host + ':' + https_port}`); + https_server.listen({ path, host, port: httpsPort }, () => { + console.log(`Listening on https://${path ? path : host + ':' + httpsPort}`); }); }