Skip to content

Commit 90b9684

Browse files
mbaranovskidevongovett
authored andcommitted
Added https development server support. (#161)
* Added https development server support. * From let to const cachedKey & cachedCert variable. * Updated logger string to display correct url while serving with --https option.
1 parent a2aa64a commit 90b9684

File tree

10 files changed

+155
-12
lines changed

10 files changed

+155
-12
lines changed

.eslintrc.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"extends": "eslint:recommended",
33
"parserOptions": {
4-
"ecmaVersion": 8
4+
"ecmaVersion": 8
55
},
66
"env": {
77
"node": true,
88
"es6": true
99
}
10-
}
10+
}

bin/.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
"rules": {
44
"no-console": 0
55
}
6-
}
6+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"json5": "^0.5.1",
2828
"micromatch": "^3.0.4",
2929
"mkdirp": "^0.5.1",
30+
"node-forge": "^0.7.1",
3031
"node-libs-browser": "^2.0.0",
3132
"opn": "^5.1.0",
3233
"physical-cpu-count": "^2.0.0",

src/Bundler.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,8 +475,8 @@ class Bundler extends EventEmitter {
475475
return Server.middleware(this);
476476
}
477477

478-
async serve(port = 1234) {
479-
let server = await Server.serve(this, port);
478+
async serve(port = 1234, https) {
479+
let server = await Server.serve(this, port, https);
480480
this.bundle();
481481
return server;
482482
}

src/Server.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
const http = require('http');
2+
const https = require('https');
23
const serveStatic = require('serve-static');
34
const getPort = require('get-port');
45
const serverErrors = require('./utils/customErrors').serverErrors;
56

7+
const generateCertificate = require('./utils/generateCertificate');
8+
69
function middleware(bundler) {
710
const serve = serveStatic(bundler.options.outDir, {index: false});
811

@@ -54,9 +57,11 @@ function middleware(bundler) {
5457
};
5558
}
5659

57-
async function serve(bundler, port) {
60+
async function serve(bundler, port, https) {
5861
let freePort = await getPort({port});
59-
let server = http.createServer(middleware(bundler)).listen(freePort);
62+
let server = https
63+
? serveHttps(bundler, freePort)
64+
: http.createServer(middleware(bundler)).listen(freePort);
6065

6166
return new Promise((resolve, reject) => {
6267
server.on('error', err => {
@@ -73,7 +78,7 @@ async function serve(bundler, port) {
7378
: '';
7479
bundler.logger.persistent(
7580
`Server running at ${bundler.logger.chalk.cyan(
76-
`http://localhost:${server.address().port}`
81+
`${https ? 'https' : 'http'}://localhost:${server.address().port}`
7782
)} ${addon}`
7883
);
7984

@@ -82,5 +87,10 @@ async function serve(bundler, port) {
8287
});
8388
}
8489

90+
function serveHttps(bundler, freePort) {
91+
const {key, cert} = generateCertificate();
92+
return https.createServer({key, cert}, middleware(bundler)).listen(freePort);
93+
}
94+
8595
exports.middleware = middleware;
8696
exports.serve = serve;

src/assets/LESSAsset.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function urlPlugin(asset) {
3232
return {
3333
install: (less, pluginManager) => {
3434
let visitor = new less.visitors.Visitor({
35-
visitUrl: (node) => {
35+
visitUrl: node => {
3636
node.value.value = asset.addURLDependency(
3737
node.value.value,
3838
node.currentFileInfo.filename

src/cli.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ program
1313
'set the port to serve on. defaults to 1234',
1414
parseInt
1515
)
16+
.option('--https', 'serves files over HTTPS')
1617
.option('-o, --open', 'automatically open in default browser')
1718
.option(
1819
'-d, --out-dir <path>',
@@ -96,9 +97,13 @@ async function bundle(main, command) {
9697
const bundler = new Bundler(main, command);
9798

9899
if (command.name() === 'serve') {
99-
const server = await bundler.serve(command.port || 1234);
100+
const server = await bundler.serve(command.port || 1234, command.https);
100101
if (command.open) {
101-
require('opn')(`http://localhost:${server.address().port}`);
102+
require('opn')(
103+
`${command.https ? 'https' : 'http'}://localhost:${
104+
server.address().port
105+
}`
106+
);
102107
}
103108
} else {
104109
bundler.bundle();

src/utils/generateCertificate.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
const forge = require('node-forge');
2+
const fs = require('fs');
3+
4+
const PRIVATE_KEY_PATH = './.cache/private.pem';
5+
const PRIMARY_KEY_PATH = './.cache/primary.crt';
6+
7+
function generateCertificate() {
8+
const cachedKey =
9+
fs.existsSync(PRIVATE_KEY_PATH) && fs.readFileSync(PRIVATE_KEY_PATH);
10+
const cachedCert =
11+
fs.existsSync(PRIMARY_KEY_PATH) && fs.readFileSync(PRIMARY_KEY_PATH);
12+
13+
if (cachedKey && cachedCert)
14+
return {
15+
key: cachedKey,
16+
cert: cachedCert
17+
};
18+
19+
console.log('Generating SSL Certificate...');
20+
21+
const pki = forge.pki;
22+
const keys = pki.rsa.generateKeyPair(2048);
23+
const cert = pki.createCertificate();
24+
25+
cert.publicKey = keys.publicKey;
26+
cert.serialNumber = '01';
27+
cert.validity.notBefore = new Date();
28+
cert.validity.notAfter = new Date();
29+
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
30+
31+
const attrs = [
32+
{
33+
name: 'commonName',
34+
value: 'parceljs.org'
35+
},
36+
{
37+
name: 'countryName',
38+
value: 'US'
39+
},
40+
{
41+
shortName: 'ST',
42+
value: 'Virginia'
43+
},
44+
{
45+
name: 'localityName',
46+
value: 'Blacksburg'
47+
},
48+
{
49+
name: 'organizationName',
50+
value: 'parcelBundler'
51+
},
52+
{
53+
shortName: 'OU',
54+
value: 'Test'
55+
}
56+
];
57+
58+
cert.setSubject(attrs);
59+
cert.setIssuer(attrs);
60+
cert.setExtensions([
61+
{
62+
name: 'basicConstraints',
63+
cA: true
64+
},
65+
{
66+
name: 'keyUsage',
67+
keyCertSign: true,
68+
digitalSignature: true,
69+
nonRepudiation: true,
70+
keyEncipherment: true,
71+
dataEncipherment: true
72+
},
73+
{
74+
name: 'extKeyUsage',
75+
serverAuth: true,
76+
clientAuth: true,
77+
codeSigning: true,
78+
emailProtection: true,
79+
timeStamping: true
80+
},
81+
{
82+
name: 'nsCertType',
83+
client: true,
84+
server: true,
85+
email: true,
86+
objsign: true,
87+
sslCA: true,
88+
emailCA: true,
89+
objCA: true
90+
},
91+
{
92+
name: 'subjectAltName',
93+
altNames: [
94+
{
95+
type: 6, // URI
96+
value: 'http://example.org/webid#me'
97+
},
98+
{
99+
type: 7, // IP
100+
ip: '127.0.0.1'
101+
}
102+
]
103+
},
104+
{
105+
name: 'subjectKeyIdentifier'
106+
}
107+
]);
108+
109+
cert.sign(keys.privateKey, forge.md.sha256.create());
110+
111+
const privPem = pki.privateKeyToPem(keys.privateKey);
112+
const certPem = pki.certificateToPem(cert);
113+
114+
fs.writeFileSync(PRIVATE_KEY_PATH, privPem);
115+
fs.writeFileSync(PRIMARY_KEY_PATH, certPem);
116+
117+
return {
118+
key: privPem,
119+
cert: certPem
120+
};
121+
}
122+
123+
module.exports = generateCertificate;

test/.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
"env": {
44
"mocha": true
55
}
6-
}
6+
}

yarn.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3380,6 +3380,10 @@ nib@^1.1.2:
33803380
dependencies:
33813381
stylus "0.54.5"
33823382

3383+
node-forge@^0.7.1:
3384+
version "0.7.1"
3385+
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
3386+
33833387
node-gyp@^3.3.1:
33843388
version "3.6.2"
33853389
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60"

0 commit comments

Comments
 (0)