Skip to content

Commit

Permalink
Add support for specifying SSL certificate & key (#966)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkrehm authored and devongovett committed Mar 18, 2018
1 parent b8a0f63 commit a057c90
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 9 deletions.
14 changes: 10 additions & 4 deletions packages/core/parcel/src/HMRServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ const https = require('https');
const WebSocket = require('ws');
const prettyError = require('./utils/prettyError');
const generateCertificate = require('./utils/generateCertificate');
const getCertificate = require('./utils/getCertificate');
const logger = require('./Logger');

class HMRServer {
async start(options = {}) {
await new Promise(resolve => {
let server = options.https
? https.createServer(generateCertificate(options))
: http.createServer();
await new Promise(async resolve => {
let server;
if (!options.https) {
server = http.createServer();
} else if (typeof options.https === 'boolean') {
server = https.createServer(generateCertificate(options));
} else {
server = https.createServer(await getCertificate(options.https));
}

this.wss = new WebSocket.Server({server});
server.listen(options.hmrPort, resolve);
Expand Down
12 changes: 9 additions & 3 deletions packages/core/parcel/src/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const serveStatic = require('serve-static');
const getPort = require('get-port');
const serverErrors = require('./utils/customErrors').serverErrors;
const generateCertificate = require('./utils/generateCertificate');
const getCertificate = require('./utils/getCertificate');
const logger = require('./Logger');

serveStatic.mime.define({
Expand Down Expand Up @@ -82,9 +83,14 @@ function middleware(bundler) {

async function serve(bundler, port, useHTTPS = false) {
let handler = middleware(bundler);
let server = useHTTPS
? https.createServer(generateCertificate(bundler.options), handler)
: http.createServer(handler);
let server;
if (!useHTTPS) {
server = http.createServer(handler);
} else if (typeof useHTTPS === 'boolean') {
server = https.createServer(generateCertificate(bundler.options), handler);
} else {
server = https.createServer(await getCertificate(useHTTPS), handler);
}

let freePort = await getPort({port});
server.listen(freePort);
Expand Down
9 changes: 9 additions & 0 deletions packages/core/parcel/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ program
'set the hostname of HMR websockets, defaults to location.hostname of current window'
)
.option('--https', 'serves files over HTTPS')
.option('--cert <path>', 'path to certificate to use with HTTPS')
.option('--key <path>', 'path to private key to use with HTTPS')
.option('--open', 'automatically open in default browser')
.option(
'-d, --out-dir <path>',
Expand Down Expand Up @@ -148,6 +150,13 @@ async function bundle(main, command) {
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
}

if (command.cert && command.key) {
command.https = {
cert: command.cert,
key: command.key
};
}

const bundler = new Bundler(main, command);

if (command.name() === 'serve') {
Expand Down
13 changes: 13 additions & 0 deletions packages/core/parcel/src/utils/getCertificate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const fs = require('./fs');

async function getCertificate(options) {
try {
let cert = await fs.readFile(options.cert);
let key = await fs.readFile(options.key);
return {key, cert};
} catch (err) {
throw new Error('Certificate and/or key not found');
}
}

module.exports = getCertificate;
25 changes: 25 additions & 0 deletions packages/core/parcel/test/getCertificate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const assert = require('assert');
const path = require('path');
const fs = require('../src/utils/fs');
const getCertificate = require('../src/utils/getCertificate');

const https = {
key: path.join(__dirname, '/integration/https', 'private.pem'),
cert: path.join(__dirname, '/integration/https', 'primary.crt')
};

describe('getCertificate', () => {
it('should support custom certificate', async () => {
const key = await fs.readFile(
path.join(__dirname, '/integration/https', 'private.pem')
);
const cert = await fs.readFile(
path.join(__dirname, '/integration/https', 'primary.crt')
);

const retrieved = await getCertificate(https);

assert.equal(retrieved.cert.toString(), cert.toString());
assert.equal(retrieved.key.toString(), key.toString());
});
});
63 changes: 63 additions & 0 deletions packages/core/parcel/test/hmr.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,67 @@ describe('hmr', function() {
assert(logs[0].trim().startsWith('[parcel] 🚨'));
assert(logs[1].trim().startsWith('[parcel] ✨'));
});

it('should make a secure connection', async function() {
await ncp(__dirname + '/integration/commonjs', __dirname + '/input');

b = bundler(__dirname + '/input/index.js', {
watch: true,
hmr: true,
https: true
});
await b.bundle();

ws = new WebSocket('wss://localhost:' + b.options.hmrPort, {
rejectUnauthorized: false
});

const buildEnd = nextEvent(b, 'buildEnd');

fs.writeFileSync(
__dirname + '/input/local.js',
'exports.a = 5;\nexports.b = 5;'
);

let msg = json5.parse(await nextEvent(ws, 'message'));
assert.equal(msg.type, 'update');
assert.equal(msg.assets.length, 1);
assert.equal(msg.assets[0].generated.js, 'exports.a = 5;\nexports.b = 5;');
assert.deepEqual(msg.assets[0].deps, {});

await buildEnd;
});

it('should make a secure connection with custom certificate', async function() {
await ncp(__dirname + '/integration/commonjs', __dirname + '/input');

b = bundler(__dirname + '/input/index.js', {
watch: true,
hmr: true,
https: {
key: __dirname + '/integration/https/private.pem',
cert: __dirname + '/integration/https/primary.crt'
}
});
await b.bundle();

ws = new WebSocket('wss://localhost:' + b.options.hmrPort, {
rejectUnauthorized: false
});

const buildEnd = nextEvent(b, 'buildEnd');

fs.writeFileSync(
__dirname + '/input/local.js',
'exports.a = 5;\nexports.b = 5;'
);

let msg = json5.parse(await nextEvent(ws, 'message'));
assert.equal(msg.type, 'update');
assert.equal(msg.assets.length, 1);
assert.equal(msg.assets[0].generated.js, 'exports.a = 5;\nexports.b = 5;');
assert.deepEqual(msg.assets[0].deps, {});

await buildEnd;
});
});
30 changes: 29 additions & 1 deletion packages/core/parcel/test/integration/https/primary.crt
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
hello cert
-----BEGIN CERTIFICATE-----
MIIE/zCCAuegAwIBAgIJAITHmIqRap3uMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV
BAMMC2V4YW1wbGUuY29tMB4XDTE4MDMwODE5NDIxOFoXDTI4MDMwNTE5NDIxOFow
FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQDqi17CnmpA5jfFqcvfB5OZz59lRQpNO098OOeUIbQB/pP868Y5qmL/
ylCQmj3liIdul6oflNFPmu23numpUTHOSwSxfY/QZwzsYytnbQG6gr1DhrvNDzeN
Arf1cNJ9CqfJdEmNyikfXGOJCk1xewCWYcZ34kugo2kuLGO83ft3Q5J47iqvgTPX
PbTc4qfUgXavprVSUzpWOGbbK2GHeenTGCarsfu7YkP3VVjMpeKcxS94Bnd0wHZF
PJKuTNDMrANBP/7RKh4XBgeuRPAdUjbcHWnlMWBvj0YuIL/Yhw976tIWAlyp0ZR6
HKF46QOEnPrR5sQGIC7wM6qyB3xCVX7eMEKcV97YYtD52eskBHT8knQhNfYn6OI4
zin13++en3Kg8I/lDA3uf/Zr2Uj2gwu8v4RqINOQoWrgAHWeActi+5FB9xwebnwX
yax+7eoCPtfe1zDo0GlzNbDKFnd3JfMC4e4gvwnAMnS1Wg5bBNXZJy8ggpniY04b
34HBWBOonscaQLMWrZQpNDfHoszUzGla0nKjru3Cwk8MJqz9qlruPJN0stdqY2C/
3Q2rB1YgvMFTr43C+KALk7hQ4w1ShJ0KYde1UPgWKooYGJRMKtpSuwu4QcgsxPIB
fH1aXEuHefxQ5EwhZm8FW79Psit+oPMvrBw1esLQnBQA3JhDG2tG3wIDAQABo1Aw
TjAdBgNVHQ4EFgQUkO1iAxx9YovR6zdzp9WTigA0OX4wHwYDVR0jBBgwFoAUkO1i
Axx9YovR6zdzp9WTigA0OX4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AgEAGk378ptJZMSmxgypj8z2pldHfbRURDL+rI+5opf7JBeVQkyqqDWkdSM1dCMy
mN7QqwCQbQFxKiKM+wbZfS8/JO5Pj0kQwuiaXVx7gPDezXT4aDw+TFH/K3ZNI2oB
S4JWCix5rHpwE6Xdnady0IYloT08t9lMdME0728B7j+XcJgH5exzLIwWdzs9JWYj
hip87tx6ORw1s74HCmcnMshr7Ow1J8DTmWcL5+MLPokFm14jGWiW826odzucU3jf
bh1T8+DbM+ZkxXN0BTh6jFx0qmYg8wyVSKqVSncAo1hdsb+/aA+BpU0eofWInyzs
45c3/wIE2LVC3EsoHAlT3O6ZVtI2va48E4oDMJSf7IinjGZ+VJzn46gXvmZl6KDQ
Get1dsrxxlPqwOLM/WgHkvGBwfw9OyK3igdYtudsT5OcW4Parok9s1ea4szDteHF
5opBcQG91i9/fAeCMSgjAZx0HMlFQi7OxsappUgc0d77JeR04hiVPxOxttu8LuKw
GgmuoZ0HgCso9CaPlOQXfQxOFljcdQkUxrIf/7HOo/D7Lzhc2RIgcDQiZk5o8chv
JX+QSmpQTRl//iBDNlNcUy5WJtSjpPOxvKryVypTw+Xwkbb4HgAmNY5x87K7WucB
j5hfjsLAwrciIsIcKu5qLzheU6vRhPwZE5vW3lWZnf6D/s4=
-----END CERTIFICATE-----
53 changes: 52 additions & 1 deletion packages/core/parcel/test/integration/https/private.pem
Original file line number Diff line number Diff line change
@@ -1 +1,52 @@
hello private key
-----BEGIN PRIVATE KEY-----
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
I0ZrfUn75MYvsljzUOiaIQRgK9m1/74=
-----END PRIVATE KEY-----
11 changes: 11 additions & 0 deletions packages/core/parcel/test/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,15 @@ describe('server', function() {
let data = await get('/dist/index.js', https);
assert.equal(data, fs.readFileSync(__dirname + '/dist/index.js', 'utf8'));
});

it('should support HTTPS via custom certificate', async function() {
let b = bundler(__dirname + '/integration/commonjs/index.js');
server = await b.serve(0, {
key: __dirname + '/integration/https/private.pem',
cert: __dirname + '/integration/https/primary.crt'
});

let data = await get('/dist/index.js', https);
assert.equal(data, fs.readFileSync(__dirname + '/dist/index.js', 'utf8'));
});
});

0 comments on commit a057c90

Please sign in to comment.