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

support https #462

Merged
merged 6 commits into from
Apr 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/blue-squids-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Add --https flag to dev and start
1 change: 1 addition & 0 deletions packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"port-authority": "^1.1.2",
"rimraf": "^3.0.2",
"rollup": "^2.41.1",
"selfsigned": "^1.10.8",
"sirv": "^1.0.11",
"svelte": "^3.35.0",
"tiny-glob": "^0.2.8",
Expand Down
22 changes: 13 additions & 9 deletions packages/kit/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ prog
.describe('Start a development server')
.option('-p, --port', 'Port', 3000)
.option('-h, --host', 'Host (only use this on trusted networks)', 'localhost')
.option('--https', 'use self-signed HTTPS certificate', false)
.option('-o, --open', 'Open a browser tab', false)
.action(async ({ port, host, open }) => {
.action(async ({ port, host, https, open }) => {
await check_port(port);

process.env.NODE_ENV = 'development';
Expand All @@ -80,7 +81,7 @@ prog
const { dev } = await import('./core/dev/index.js');

try {
const watcher = await dev({ port, host, config });
const watcher = await dev({ port, host, https, config });

watcher.on('stdout', (data) => {
process.stdout.write(data);
Expand All @@ -90,7 +91,7 @@ prog
process.stderr.write(data);
});

welcome({ port, host, open });
welcome({ port, host, https, open });
} catch (error) {
handle_error(error);
}
Expand Down Expand Up @@ -131,8 +132,9 @@ prog
.describe('Serve an already-built app')
.option('-p, --port', 'Port', 3000)
.option('-h, --host', 'Host (only use this on trusted networks)', 'localhost')
.option('--https', 'use self-signed HTTPS certificate', false)
.option('-o, --open', 'Open a browser tab', false)
.action(async ({ port, host, open }) => {
.action(async ({ port, host, https, open }) => {
await check_port(port);

process.env.NODE_ENV = 'production';
Expand All @@ -141,9 +143,9 @@ prog
const { start } = await import('./core/start/index.js');

try {
await start({ port, host, config });
await start({ port, host, config, https });

welcome({ port, host, open });
welcome({ port, host, https, open });
} catch (error) {
handle_error(error);
}
Expand Down Expand Up @@ -180,14 +182,16 @@ async function check_port(port) {
* @param {{
* open: boolean;
* host: string;
* https: boolean;
* port: number;
* }} param0
*/
function welcome({ port, host, open }) {
function welcome({ port, host, https, open }) {
if (open) launch(port);

console.log(colors.bold().cyan(`\n SvelteKit v${'__VERSION__'}\n`));

const protocol = https ? 'https:' : 'http:';
const exposed = host !== 'localhost' && host !== '127.0.0.1';

Object.values(networkInterfaces()).forEach((interfaces) => {
Expand All @@ -196,12 +200,12 @@ function welcome({ port, host, open }) {

// prettier-ignore
if (details.internal) {
console.log(` ${colors.gray('local: ')} http://${colors.bold(`localhost:${port}`)}`);
console.log(` ${colors.gray('local: ')} ${protocol}//${colors.bold(`localhost:${port}`)}`);
} else {
if (details.mac === '00:00:00:00:00:00') return;

if (exposed) {
console.log(` ${colors.gray('network:')} http://${colors.bold(`${details.address}:${port}`)}`);
console.log(` ${colors.gray('network:')} ${protocol}//${colors.bold(`${details.address}:${port}`)}`);
} else {
console.log(` ${colors.gray('network: not exposed')}`);
}
Expand Down
34 changes: 16 additions & 18 deletions packages/kit/src/core/dev/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import http from 'http';
import fs from 'fs';
import path from 'path';
import { parse, URLSearchParams } from 'url';
Expand All @@ -13,8 +12,9 @@ import { ssr } from '../../runtime/server/index.js';
import { get_body } from '../http/index.js';
import { copy_assets } from '../utils.js';
import svelte from '@sveltejs/vite-plugin-svelte';
import { get_server } from '../server/index.js';

/** @typedef {{ cwd?: string, port: number, host: string, config: import('../../../types.internal').ValidatedConfig }} Options */
/** @typedef {{ cwd?: string, port: number, host: string, https: boolean | import('https').ServerOptions, config: import('../../../types.internal').ValidatedConfig }} Options */
/** @typedef {import('../../../types.internal').SSRComponent} SSRComponent */

/** @param {Options} opts */
Expand All @@ -24,14 +24,15 @@ export function dev(opts) {

class Watcher extends EventEmitter {
/** @param {Options} opts */
constructor({ cwd = process.cwd(), port, host, config }) {
constructor({ cwd = process.cwd(), port, host, https, config }) {
super();

this.cwd = cwd;
this.dir = path.resolve(cwd, '.svelte/dev');

this.port = port;
this.host = host;
this.https = https;
this.config = config;

process.env.NODE_ENV = 'development';
Expand Down Expand Up @@ -80,7 +81,7 @@ class Watcher extends EventEmitter {
/**
* @type {vite.ViteDevServer}
*/
this.viteDevServer = await vite.createServer({
this.vite = await vite.createServer({
...user_config,
configFile: false,
root: this.cwd,
Expand Down Expand Up @@ -113,8 +114,8 @@ class Watcher extends EventEmitter {

const validator = this.config.kit.amp && (await amp_validator.getInstance());

this.server = http.createServer((req, res) => {
this.viteDevServer.middlewares(req, res, async () => {
this.server = await get_server(this.port, this.host, this.https, (req, res) => {
this.vite.middlewares(req, res, async () => {
try {
const parsed = parse(req.url);

Expand All @@ -123,15 +124,14 @@ class Watcher extends EventEmitter {
// handle dynamic requests - i.e. pages and endpoints
const template = fs.readFileSync(this.config.kit.files.template, 'utf-8');

const hooks = /** @type {import('../../../types.internal').Hooks} */ (await this.viteDevServer
const hooks = /** @type {import('../../../types.internal').Hooks} */ (await this.vite
.ssrLoadModule(`/${this.config.kit.files.hooks}`)
.catch(() => ({})));

let root;

try {
root = (await this.viteDevServer.ssrLoadModule(`/${this.dir}/generated/root.svelte`))
.default;
root = (await this.vite.ssrLoadModule(`/${this.dir}/generated/root.svelte`)).default;
} catch (e) {
res.statusCode = 500;
res.end(e.stack);
Expand Down Expand Up @@ -219,7 +219,7 @@ class Watcher extends EventEmitter {
only_render_prerenderable_pages: false,
get_component_path: (id) => `/${id}?import`,
get_stack: (error) => {
this.viteDevServer.ssrFixStacktrace(error);
this.vite.ssrFixStacktrace(error);
return error.stack;
},
get_static_file: (file) =>
Expand All @@ -239,13 +239,11 @@ class Watcher extends EventEmitter {
res.end('Not found');
}
} catch (e) {
this.viteDevServer.ssrFixStacktrace(e);
this.vite.ssrFixStacktrace(e);
res.end(e.stack);
}
});
});

this.server.listen(this.port, this.host || '0.0.0.0');
}

update() {
Expand Down Expand Up @@ -273,8 +271,8 @@ class Watcher extends EventEmitter {
const load = async (file) => {
const url = path.resolve(this.cwd, file);

const mod = /** @type {SSRComponent} */ (await this.viteDevServer.ssrLoadModule(url));
const node = await this.viteDevServer.moduleGraph.getModuleByUrl(url);
const mod = /** @type {SSRComponent} */ (await this.vite.ssrLoadModule(url));
const node = await this.vite.moduleGraph.getModuleByUrl(url);

const deps = new Set();
find_deps(node, deps);
Expand All @@ -284,7 +282,7 @@ class Watcher extends EventEmitter {
// TODO what about .scss files, etc?
if (dep.file.endsWith('.css')) {
try {
const mod = await this.viteDevServer.ssrLoadModule(dep.url);
const mod = await this.vite.ssrLoadModule(dep.url);
css.add(mod.default);
} catch {
// this can happen with dynamically imported modules, I think
Expand Down Expand Up @@ -367,7 +365,7 @@ class Watcher extends EventEmitter {
params: get_params(route.params),
load: async () => {
const url = path.resolve(this.cwd, route.file);
return await this.viteDevServer.ssrLoadModule(url);
return await this.vite.ssrLoadModule(url);
}
};
})
Expand All @@ -378,7 +376,7 @@ class Watcher extends EventEmitter {
if (this.closed) return;
this.closed = true;

this.viteDevServer.close();
this.vite.close();
this.server.close();
this.cheapwatch.close();
}
Expand Down
72 changes: 72 additions & 0 deletions packages/kit/src/core/server/cert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// @ts-ignore
import { generate } from 'selfsigned';

/**
* https://github.com/webpack/webpack-dev-server/blob/master/lib/utils/createCertificate.js
*
* Copyright JS Foundation and other contributors
* This source code is licensed under the MIT license found in the
* LICENSE file at
* https://github.com/webpack/webpack-dev-server/blob/master/LICENSE
*/
export function createCertificate() {
const pems = generate(null, {
algorithm: 'sha256',
days: 30,
keySize: 2048,
extensions: [
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
timeStamping: true
},
{
name: 'subjectAltName',
altNames: [
{
// type 2 is DNS
type: 2,
value: 'localhost'
},
{
type: 2,
value: 'localhost.localdomain'
},
{
type: 2,
value: 'lvh.me'
},
{
type: 2,
value: '*.lvh.me'
},
{
type: 2,
value: '[::1]'
},
{
// type 7 is IP
type: 7,
ip: '127.0.0.1'
},
{
type: 7,
ip: 'fe80::1'
}
]
}
]
});

return pems.private + pems.cert;
}
31 changes: 31 additions & 0 deletions packages/kit/src/core/server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import http from 'http';
import https from 'https';

/**
*
* @param {number} port
* @param {string} host
* @param {boolean | https.ServerOptions} https_options
* @param {(req: http.IncomingMessage, res: http.ServerResponse) => void} handler
* @returns {Promise<http.Server | https.Server>}
*/
export async function get_server(port, host, https_options, handler) {
if (https_options) {
https_options =
typeof https_options === 'boolean' ? /** @type {https.ServerOptions} */ ({}) : https_options;

if (!https_options.key || !https_options.cert) {
https_options.key = https_options.cert = (await import('./cert')).createCertificate();
}
}

return new Promise((fulfil) => {
const server = https_options
? https.createServer(/** @type {https.ServerOptions} */ (https_options), handler)
: http.createServer(handler);

server.listen(port, host || '0.0.0.0', () => {
fulfil(server);
});
});
}
Loading