Skip to content

Commit

Permalink
Add HTTPS/HTTP2 server (#422)
Browse files Browse the repository at this point in the history
  • Loading branch information
stramel authored Jun 5, 2020
1 parent c489384 commit 3266d9b
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 10 deletions.
21 changes: 20 additions & 1 deletion docs/04-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ For your safety, Snowpack only supports environment variables that begin with `S
Add the `@snowpack/plugin-dotenv` plugin to your dev environment to automatically load environment variables from your project `.env` files. Visit the [plugin README](https://github.com/pikapkg/create-snowpack-app/tree/master/packages/plugin-dotenv) to learn more.
### Dev Request Proxy
```js
Expand All @@ -239,6 +238,26 @@ Snowpack can proxy requests from the dev server to external URLs and APIs. Makin
See the [Proxy Options](#proxy-options) section for more information and full set of configuration options.
### HTTPS/HTTP2
Snowpack provides an easy way to use a local HTTPS server during development throught the use of the `--secure` flag. When enabled, Snowpack will look for a `snowpack.key` and `snowpack.crt` file in the root directory.
Usage:
```
npm start -- --secure
```
#### Generating SSL Certificates
You can automatically generate credentials for your project via either:
- [devcert (no install required)](https://github.com/davewasmer/devcert-cli): `npx devcert-cli generate localhost`
- [mkcert (install required)](https://github.com/FiloSottile/mkcert): `mkcert -install && mkcert localhost`
#### HTTP2
Not sure what to write here...
### Import Maps
Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
"@types/cacache": "^12.0.1",
"@types/compressible": "^2.0.0",
"@types/es-module-lexer": "^0.3.0",
"@types/etag": "^1.8.0",
"@types/http-proxy": "^1.17.4",
"@types/mkdirp": "^1.0.0",
"@types/tar": "^4.0.3",
Expand Down
45 changes: 41 additions & 4 deletions src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {EventEmitter} from 'events';
import execa from 'execa';
import {existsSync, promises as fs, readFileSync} from 'fs';
import http from 'http';
import http2 from 'http2';
import HttpProxy from 'http-proxy';
import mime from 'mime-types';
import npmRunPath from 'npm-run-path';
Expand Down Expand Up @@ -103,7 +104,7 @@ const sendFile = (
body: string | Buffer,
ext = '.html',
) => {
const ETag = etag(body);
const ETag = etag(body, { weak: true });
const headers: Record<string, string> = {
'Content-Type': mime.contentType(ext) || 'application/octet-stream',
'Access-Control-Allow-Origin': '*',
Expand Down Expand Up @@ -418,8 +419,41 @@ export async function command(commandOptions: CommandOptions) {
}
});

const server = http
.createServer(async (req, res) => {

const readCredentials = async (cwd: string) => {
const [cert, key] = await Promise.all([
fs.readFile(path.join(cwd, 'snowpack.crt')),
fs.readFile(path.join(cwd, 'snowpack.key')),
]);

return {
cert,
key,
}
};

let credentials: { cert: Buffer, key: Buffer } | undefined
if (config.devOptions.secure) {
try {
credentials = await readCredentials(cwd);
} catch (e) {
console.error(chalk.red(`✘ No HTTPS credentials found! Missing Files: ${chalk.bold('snowpack.crt')}, ${chalk.bold('snowpack.key')}`))
console.log()
console.log('You can automatically generate credentials for your project via either:')
console.log()
console.log(` - ${chalk.cyan('devcert')}: ${chalk.yellow('npx devcert-cli generate localhost')}`)
console.log(' https://github.com/davewasmer/devcert-cli (no install required)')
console.log()
console.log(` - ${chalk.cyan('mkcert')}: ${chalk.yellow('mkcert -install && mkcert localhost')}`)
console.log(' https://github.com/FiloSottile/mkcert (install required)')
console.log()
process.exit(1);
}
}

const createServer = credentials ? (requestHandler) => http2.createSecureServer(credentials!, requestHandler) : (requestHandler) => http.createServer(requestHandler)

const server = createServer(async (req, res) => {
const reqUrl = req.url!;
let reqPath = decodeURI(url.parse(reqUrl).pathname!);
const originalReqPath = reqPath;
Expand Down Expand Up @@ -830,7 +864,10 @@ export async function command(commandOptions: CommandOptions) {
.filter((i) => i.family === 'IPv4' && i.internal === false)
.map((i) => i.address);

const protocol = config.devOptions.secure ? 'https:' : 'http:'

paint(messageBus, config.scripts, undefined, {
protocol,
port,
ips,
startTimeMs: Date.now() - serverStart,
Expand Down Expand Up @@ -867,6 +904,6 @@ export async function command(commandOptions: CommandOptions) {
},
});

if (open !== 'none') await openInBrowser(port, open);
if (open !== 'none') await openInBrowser(protocol, port, open);
return new Promise(() => {});
}
5 changes: 3 additions & 2 deletions src/commands/paint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function paint(
buildMode: {dest: string} | undefined,
devMode:
| {
protocol: string;
port: number;
ips: string[];
startTimeMs: number;
Expand All @@ -55,10 +56,10 @@ export function paint(
process.stdout.write(`${chalk.bold('Snowpack')}\n\n`);
// Dashboard
if (devMode) {
process.stdout.write(` ${chalk.bold.cyan(`http://localhost:${devMode.port}`)}`);
process.stdout.write(` ${chalk.bold.cyan(`${devMode.protocol}//localhost:${devMode.port}`)}`);
for (const ip of devMode.ips) {
process.stdout.write(
`${chalk.cyan(` > `)}${chalk.bold.cyan(`http://${ip}:${devMode.port}`)}`,
`${chalk.cyan(` > `)}${chalk.bold.cyan(`${devMode.protocol}//${ip}:${devMode.port}`)}`,
);
}
process.stdout.write('\n' + chalk.dim(` Server started in ${devMode.startTimeMs}ms.\n\n`));
Expand Down
4 changes: 4 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export interface SnowpackConfig {
plugins: SnowpackPlugin[];
homepage?: string;
devOptions: {
secure: boolean;
port: number;
out: string;
fallback: string;
Expand Down Expand Up @@ -122,6 +123,7 @@ export interface CLIFlags extends Omit<Partial<SnowpackConfig['installOptions']>
config?: string; // manual path to config file
env?: string[]; // env vars
open?: string[];
secure?: boolean;
}

// default settings
Expand All @@ -140,6 +142,7 @@ const DEFAULT_CONFIG: Partial<SnowpackConfig> = {
},
},
devOptions: {
secure: false,
port: 8080,
open: 'default',
out: 'build',
Expand Down Expand Up @@ -167,6 +170,7 @@ const configSchema = {
devOptions: {
type: 'object',
properties: {
secure: { type: 'boolean'},
port: {type: 'number'},
out: {type: 'string'},
fallback: {type: 'string'},
Expand Down
3 changes: 2 additions & 1 deletion src/hmr-server-engine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import WebSocket from 'ws';
import type http from 'http';
import type http2 from 'http2';

interface Dependency {
dependents: Set<string>;
Expand All @@ -13,7 +14,7 @@ export class EsmHmrEngine {
clients: Set<WebSocket> = new Set();
dependencyTree = new Map<string, Dependency>();

constructor(options: {server?: http.Server} = {}) {
constructor(options: {server?: http.Server | http2.Http2Server} = {}) {
const wss = options.server
? new WebSocket.Server({noServer: true})
: new WebSocket.Server({port: 12321});
Expand Down
4 changes: 2 additions & 2 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ const appNames = {
},
};

export async function openInBrowser(port: number, browser: string) {
const url = `http://localhost:${port}`;
export async function openInBrowser(protocol: string, port: number, browser: string) {
const url = `${protocol}//localhost:${port}`;
browser = /chrome/i.test(browser)
? appNames[process.platform]['chrome']
: /brave/i.test(browser)
Expand Down

0 comments on commit 3266d9b

Please sign in to comment.