From 42bb30dac64f48c9d4cf88d00f428416bdd9174e Mon Sep 17 00:00:00 2001 From: Stefan Baramov Date: Tue, 13 Sep 2016 16:08:26 -0400 Subject: [PATCH 1/7] Added support for running the webpack dev server in ssl mode through ng serve command. The command options --ssl, --ssl-key, and --ssl-cert are used to configure the server. This patch is related to issue #1874 and #1576. --- packages/angular-cli/tasks/serve-webpack.ts | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/angular-cli/tasks/serve-webpack.ts b/packages/angular-cli/tasks/serve-webpack.ts index 4dfb1dffff1f..6c01c8746205 100644 --- a/packages/angular-cli/tasks/serve-webpack.ts +++ b/packages/angular-cli/tasks/serve-webpack.ts @@ -46,6 +46,19 @@ export default Task.extend({ } } + let sslKey = null; + let sslCert = null; + if (commandOptions.ssl) { + const keyPath = path.resolve(this.project.root, commandOptions.sslKey); + if (fs.existsSync(keyPath)) { + sslKey = fs.readFileSync(keyPath, 'utf-8'); + } + const certPath = path.resolve(this.project.root, commandOptions.sslCert); + if (fs.existsSync(certPath)) { + sslCert = fs.readFileSync(certPath, 'utf-8'); + } + } + const webpackDevServerConfiguration: IWebpackDevServerConfigurationOptions = { contentBase: path.resolve( this.project.root, @@ -54,13 +67,19 @@ export default Task.extend({ historyApiFallback: true, stats: webpackDevServerOutputOptions, inline: true, - proxy: proxyConfig + proxy: proxyConfig, + https: commandOptions.ssl }; + if (sslKey != null && sslCert != null) { + webpackDevServerConfiguration.key = sslKey; + webpackDevServerConfiguration.cert = sslCert; + } + ui.writeLine(chalk.green(oneLine` ** NG Live Development Server is running on - http://${commandOptions.host}:${commandOptions.port}. + http${commandOptions.ssl?'s':''}://${commandOptions.host}:${commandOptions.port}. ** `)); From a2b739b96cb6137c8dcd87b31169be754563dee0 Mon Sep 17 00:00:00 2001 From: Stefan Baramov Date: Tue, 13 Sep 2016 19:53:18 -0400 Subject: [PATCH 2/7] Corrected previous commit related to adding support for running the webpack dev server in ssl mode through ng serve command. --- packages/angular-cli/custom-typings.d.ts | 3 +++ packages/angular-cli/tasks/serve-webpack.ts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/angular-cli/custom-typings.d.ts b/packages/angular-cli/custom-typings.d.ts index 1fbb264de74a..e4017dc5aa8b 100644 --- a/packages/angular-cli/custom-typings.d.ts +++ b/packages/angular-cli/custom-typings.d.ts @@ -17,6 +17,9 @@ interface IWebpackDevServerConfigurationOptions { headers?: { [key: string]: string }; stats?: { [key: string]: boolean }; inline: boolean; + https: boolean; + key?: string; + cert?: string; } interface WebpackProgressPluginOutputOptions { diff --git a/packages/angular-cli/tasks/serve-webpack.ts b/packages/angular-cli/tasks/serve-webpack.ts index 6c01c8746205..d919e2791515 100644 --- a/packages/angular-cli/tasks/serve-webpack.ts +++ b/packages/angular-cli/tasks/serve-webpack.ts @@ -46,8 +46,8 @@ export default Task.extend({ } } - let sslKey = null; - let sslCert = null; + let sslKey: string = null; + let sslCert: string = null; if (commandOptions.ssl) { const keyPath = path.resolve(this.project.root, commandOptions.sslKey); if (fs.existsSync(keyPath)) { From aae5053cfb0b2ee0f1104884a1ffbcd55dffc7de Mon Sep 17 00:00:00 2001 From: Stefan Baramov Date: Wed, 14 Sep 2016 08:35:12 -0400 Subject: [PATCH 3/7] added white spaces to make tslint happy --- packages/angular-cli/tasks/serve-webpack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular-cli/tasks/serve-webpack.ts b/packages/angular-cli/tasks/serve-webpack.ts index d919e2791515..6b7d00fe2dcd 100644 --- a/packages/angular-cli/tasks/serve-webpack.ts +++ b/packages/angular-cli/tasks/serve-webpack.ts @@ -79,7 +79,7 @@ export default Task.extend({ ui.writeLine(chalk.green(oneLine` ** NG Live Development Server is running on - http${commandOptions.ssl?'s':''}://${commandOptions.host}:${commandOptions.port}. + http${commandOptions.ssl ? 's' : ''}://${commandOptions.host}:${commandOptions.port}. ** `)); From 22fe0107ec24b18568fd08964c6912504d03020f Mon Sep 17 00:00:00 2001 From: Stefan Baramov Date: Tue, 20 Sep 2016 21:24:08 -0400 Subject: [PATCH 4/7] made https field optional --- packages/angular-cli/custom-typings.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular-cli/custom-typings.d.ts b/packages/angular-cli/custom-typings.d.ts index e4017dc5aa8b..6e638b137537 100644 --- a/packages/angular-cli/custom-typings.d.ts +++ b/packages/angular-cli/custom-typings.d.ts @@ -17,7 +17,7 @@ interface IWebpackDevServerConfigurationOptions { headers?: { [key: string]: string }; stats?: { [key: string]: boolean }; inline: boolean; - https: boolean; + https?: boolean; key?: string; cert?: string; } From 52a1ec128ca7a396fc0af2ef4347ef57d67ce507 Mon Sep 17 00:00:00 2001 From: Stefan Baramov Date: Mon, 3 Oct 2016 22:24:22 -0400 Subject: [PATCH 5/7] chore(ssl): added e2e tests and requested in the code review --- tests/e2e/assets/ssl/server.crt | 23 +++++++++++++++++++++++ tests/e2e/assets/ssl/server.key | 27 +++++++++++++++++++++++++++ tests/e2e/tests/misc/ssl-default.ts | 18 ++++++++++++++++++ tests/e2e/tests/misc/ssl-with-cert.ts | 18 ++++++++++++++++++ tests/e2e/utils/http.ts | 4 ++-- 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/assets/ssl/server.crt create mode 100644 tests/e2e/assets/ssl/server.key create mode 100644 tests/e2e/tests/misc/ssl-default.ts create mode 100644 tests/e2e/tests/misc/ssl-with-cert.ts diff --git a/tests/e2e/assets/ssl/server.crt b/tests/e2e/assets/ssl/server.crt new file mode 100644 index 000000000000..6891c4c67573 --- /dev/null +++ b/tests/e2e/assets/ssl/server.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIJAJOebwfGCm61MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdHZW9yZ2lhMRAwDgYDVQQHEwdBdGxhbnRhMRAwDgYD +VQQKEwdBbmd1bGFyMRAwDgYDVQQLEwdBbmd1bGFyMB4XDTE2MTAwNDAxMDAyMVoX +DTI2MTAwMjAxMDAyMVowVTELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0dlb3JnaWEx +EDAOBgNVBAcTB0F0bGFudGExEDAOBgNVBAoTB0FuZ3VsYXIxEDAOBgNVBAsTB0Fu +Z3VsYXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDT6Q4d1+mw81SC +4K1qLbsMn4O459XDiDDU/cGBiE0byqi6RpaB0MujCPn35xdeCf1mdDw929leEIRB +w/fCN3VwE+4ZDM7sF6SgoSDN8YT/OOush4tDu0djH110I+i1Bfg4m7gVkUnJLUCv +vMMOlD19LDqqaxdY3ojXx8gZJW9sNtUH2vCICwsZ7aNZp2NcCNKpU7LppP4IomCd +GfG501kY/UtELVgNGX+zuJwIiH/2AQZ+fsaDBBD0Azanck2M/aq5yVKMG8y/S5WP +7LMvZs8ZHPSG73QINogRTYW0EKx7nT87vmrHRtCc9u4coPdqOzQN9BigCYVkYrTv +xkOX9VDHAgMBAAGjgbgwgbUwHQYDVR0OBBYEFG4VV6/aNLx/qFIS9MhAWuyeV5OX +MIGFBgNVHSMEfjB8gBRuFVev2jS8f6hSEvTIQFrsnleTl6FZpFcwVTELMAkGA1UE +BhMCVVMxEDAOBgNVBAgTB0dlb3JnaWExEDAOBgNVBAcTB0F0bGFudGExEDAOBgNV +BAoTB0FuZ3VsYXIxEDAOBgNVBAsTB0FuZ3VsYXKCCQCTnm8HxgputTAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQDO4jZT/oKVxaiWr+jV5TD+qwThl9zT +Uw/ZpFDkdbZdY/baCFaLCiJwkK9+puMOabLvm1VzcnHHWCoiUNbWpw8AOumLEnTv +ze/5OZXJ6XlA9kd9f3hDlN5zNB3S+Z2nKIrkPGfxQZ603QCbWaptip5dxgek6oDZ +YXVtnbOnPznRsG5jh07U49RO8CNebqZLzdRToLgObbqYlfRMcbUxCOHXjnB5wUlp +377Iivm4ldnCTvFOjEiDh+FByWL5xic7PjyJPZFMidiYTmsGilP9XTFC83CRZwz7 +vW+RCSlU6x8Uejz98BPmASoqCuCTUeOo+2pFelFhX9NwR/Sb6b7ybdPv +-----END CERTIFICATE----- diff --git a/tests/e2e/assets/ssl/server.key b/tests/e2e/assets/ssl/server.key new file mode 100644 index 000000000000..e0e0af0f8da8 --- /dev/null +++ b/tests/e2e/assets/ssl/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA0+kOHdfpsPNUguCtai27DJ+DuOfVw4gw1P3BgYhNG8qoukaW +gdDLowj59+cXXgn9ZnQ8PdvZXhCEQcP3wjd1cBPuGQzO7BekoKEgzfGE/zjrrIeL +Q7tHYx9ddCPotQX4OJu4FZFJyS1Ar7zDDpQ9fSw6qmsXWN6I18fIGSVvbDbVB9rw +iAsLGe2jWadjXAjSqVOy6aT+CKJgnRnxudNZGP1LRC1YDRl/s7icCIh/9gEGfn7G +gwQQ9AM2p3JNjP2quclSjBvMv0uVj+yzL2bPGRz0hu90CDaIEU2FtBCse50/O75q +x0bQnPbuHKD3ajs0DfQYoAmFZGK078ZDl/VQxwIDAQABAoIBAEl17kXcNo/4GqDw +QE2hoslCdwhfnhQVn1AG09ESriBnRcylccF4308aaoVM4CXicqzUuJl9IEJimWav +B7GVRinfTtfyP71KiPCCSvv5sPBFDDYYGugVAS9UjTIYzLAMbLs7CDq5zglmnZkO +Z9QjAZnl/kRbsZFGO8wJ3s0Q1Cp/ygZcvFU331K2jHXW7B4YXiFOH/lBQrjdz0Gy +WBjX4zIdNWnwarvxu46IS/0z1P1YOHM8+B1Uv54MG94A6szBdd/Vp0cQRs78t/Cu +BQ1Rnuk16Pi+ieC5K04yUgeuNusYW0PWLtPX1nKNp9z46bmD1NHKAxaoDFXr7qP3 +pZCaDMkCgYEA8mmTYrhXJTRIrOxoUwM1e3OZ0uOxVXJJ8HF6X8t+UO6dFxXB/JC9 +ZBc+94cZQapaKFOeMmd/j3L2CQIjChk5yKV/G3Io+raxIoAAKPCkMF4NQQVvvNkS +CAGl61Qa78DoF5Habumz0AC1R9P877kNTC0aPSt4lhPWgfotbZNNMlMCgYEA38nM +s4a0pZseXPkuOtPYX/3Ms3E+d70XKSFuIMCHCg79YGsQ8h/9apYcPyeYkpQ0a4gs +I3IUqMaXC2OyqWA5LU1BZv51mXb6zcb2pokZfpiSWk+7sy5XjkE9EmQxp3xHfV3c +EO/DxHfWNvtMjESMbhu0yVzM2O/Aa53Tl9lqAT0CgYEA1dXBuHyqCtyTG08zO78B +55Ny5rAJ1zkI9jvz2hr0o0nJcvqzcyruliNXXRxkcCNoglg4nXfk81JSrGGhLSBR +c6hhdoF+mqKboLZO7c5Q14WvpWK5TVoiaMOja/J2DHYbhecYS2yGPH7TargaUBDq +JP9IPRtitOhs+Z0Jg7ZDi5cCgYAMb7B6gY/kbBxh2k8hYchyfS41AqQQD2gMFxmB +pHFcs7yM8SY97l0s4S6sq8ykyKupFiYtyhcv0elu7pltJDXJOLPbv2RVpPEHInlu +g8vw5xWrAydRK9Adza5RKVRBFHz8kIy8PDbK4kX7RDfay6xqKgv/7LJNk/VDhb/O +fnyPmQKBgQDg/o8Ubf/gxA9Husnuld4DBu3wwFhkMlWqyO9QH3cKgojQ2JGSrfDz +xHhetmhionEyzg0JCaMSpzgIHY+8o/NAwc++OjNHEoYp3XWM9GTp81ROMz6b83jV +biVR9N0MhONdwF6vtzDCcJxNIUe2p4lTvLf/Xd9jaQDNXe35Gxsdyg== +-----END RSA PRIVATE KEY----- diff --git a/tests/e2e/tests/misc/ssl-default.ts b/tests/e2e/tests/misc/ssl-default.ts new file mode 100644 index 000000000000..20be8d671535 --- /dev/null +++ b/tests/e2e/tests/misc/ssl-default.ts @@ -0,0 +1,18 @@ +import { request } from "../../utils/http"; +import { assetDir } from "../../utils/assets"; +import { killAllProcesses } from "../../utils/process"; +import { ngServe } from "../../utils/project"; + + +export default function() { + return Promise.resolve() + .then(() => ngServe('--ssl', 'true')) + .then(() => request('https://localhost:4200/')) + .then(body => { + if (!body.match(/Loading...<\/app-root>/)) { + throw new Error('Response does not match expected value.'); + } + }) + .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) + +} diff --git a/tests/e2e/tests/misc/ssl-with-cert.ts b/tests/e2e/tests/misc/ssl-with-cert.ts new file mode 100644 index 000000000000..49acd4eb53ee --- /dev/null +++ b/tests/e2e/tests/misc/ssl-with-cert.ts @@ -0,0 +1,18 @@ +import { request } from "../../utils/http"; +import { assetDir } from "../../utils/assets"; +import { killAllProcesses } from "../../utils/process"; +import { ngServe } from "../../utils/project"; + + +export default function() { + return Promise.resolve() + .then(() => ngServe('--ssl', 'true', '--ssl-key', assetDir('ssl/server.key'), '--ssl-cert', assetDir('ssl/server.crt'))) + .then(() => request('https://localhost:4200/')) + .then(body => { + if (!body.match(/Loading...<\/app-root>/)) { + throw new Error('Response does not match expected value.'); + } + }) + .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) + +} diff --git a/tests/e2e/utils/http.ts b/tests/e2e/utils/http.ts index 565beaf4f3d5..f557239665a0 100644 --- a/tests/e2e/utils/http.ts +++ b/tests/e2e/utils/http.ts @@ -4,11 +4,11 @@ import * as _request from 'request'; export function request(url: string): Promise { return new Promise((resolve, reject) => { - _request(url, (error: any, response: IncomingMessage, body: string) => { + _request({ url: url, agentOptions: { rejectUnauthorized: false }}, (error: any, response: IncomingMessage, body: string) => { if (error) { reject(error); } else if (response.statusCode >= 400) { - reject(new Error(`Requesting "${url}" returned status code ${response.statusCode}.`); + reject(new Error(`Requesting "${url}" returned status code ${response.statusCode}.`)); } else { resolve(body); } From 0ed6fd79ae9942749145a8e1204536b5b419b4fd Mon Sep 17 00:00:00 2001 From: Stefan Baramov Date: Tue, 4 Oct 2016 08:32:26 -0400 Subject: [PATCH 6/7] chore(ssl): fix the ssl tests to pass lint --- tests/e2e/tests/misc/ssl-default.ts | 10 ++++------ tests/e2e/tests/misc/ssl-with-cert.ts | 20 +++++++++++++------- tests/e2e/utils/http.ts | 3 ++- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/tests/e2e/tests/misc/ssl-default.ts b/tests/e2e/tests/misc/ssl-default.ts index 20be8d671535..e16ec54291c9 100644 --- a/tests/e2e/tests/misc/ssl-default.ts +++ b/tests/e2e/tests/misc/ssl-default.ts @@ -1,7 +1,6 @@ -import { request } from "../../utils/http"; -import { assetDir } from "../../utils/assets"; -import { killAllProcesses } from "../../utils/process"; -import { ngServe } from "../../utils/project"; +import { request } from '../../utils/http'; +import { killAllProcesses } from '../../utils/process'; +import { ngServe } from '../../utils/project'; export default function() { @@ -13,6 +12,5 @@ export default function() { throw new Error('Response does not match expected value.'); } }) - .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) - + .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }); } diff --git a/tests/e2e/tests/misc/ssl-with-cert.ts b/tests/e2e/tests/misc/ssl-with-cert.ts index 49acd4eb53ee..8dbae0014940 100644 --- a/tests/e2e/tests/misc/ssl-with-cert.ts +++ b/tests/e2e/tests/misc/ssl-with-cert.ts @@ -1,18 +1,24 @@ -import { request } from "../../utils/http"; -import { assetDir } from "../../utils/assets"; -import { killAllProcesses } from "../../utils/process"; -import { ngServe } from "../../utils/project"; +import { request } from '../../utils/http'; +import { assetDir } from '../../utils/assets'; +import { killAllProcesses } from '../../utils/process'; +import { ngServe } from '../../utils/project'; export default function() { return Promise.resolve() - .then(() => ngServe('--ssl', 'true', '--ssl-key', assetDir('ssl/server.key'), '--ssl-cert', assetDir('ssl/server.crt'))) - .then(() => request('https://localhost:4200/')) + .then(() => { + ngServe( + '--ssl', 'true', + '--ssl-key', assetDir('ssl/server.key'), + '--ssl-cert', assetDir('ssl/server.crt') + ); + }) + .then(() => request('https://localhost:4202/')) .then(body => { if (!body.match(/Loading...<\/app-root>/)) { throw new Error('Response does not match expected value.'); } }) - .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) + .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }); } diff --git a/tests/e2e/utils/http.ts b/tests/e2e/utils/http.ts index f557239665a0..fba900c7e6e0 100644 --- a/tests/e2e/utils/http.ts +++ b/tests/e2e/utils/http.ts @@ -4,7 +4,8 @@ import * as _request from 'request'; export function request(url: string): Promise { return new Promise((resolve, reject) => { - _request({ url: url, agentOptions: { rejectUnauthorized: false }}, (error: any, response: IncomingMessage, body: string) => { + let options = { url: url, agentOptions: { rejectUnauthorized: false }}; + _request(options, (error: any, response: IncomingMessage, body: string) => { if (error) { reject(error); } else if (response.statusCode >= 400) { From ddcc5db9a92cb1feae83a2f93366108a34e552c0 Mon Sep 17 00:00:00 2001 From: Stefan Baramov Date: Tue, 4 Oct 2016 09:29:03 -0400 Subject: [PATCH 7/7] chore(ssl): fix a small configuration error --- tests/e2e/tests/misc/ssl-with-cert.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/e2e/tests/misc/ssl-with-cert.ts b/tests/e2e/tests/misc/ssl-with-cert.ts index 8dbae0014940..3930c91625fb 100644 --- a/tests/e2e/tests/misc/ssl-with-cert.ts +++ b/tests/e2e/tests/misc/ssl-with-cert.ts @@ -6,14 +6,12 @@ import { ngServe } from '../../utils/project'; export default function() { return Promise.resolve() - .then(() => { - ngServe( - '--ssl', 'true', - '--ssl-key', assetDir('ssl/server.key'), - '--ssl-cert', assetDir('ssl/server.crt') - ); - }) - .then(() => request('https://localhost:4202/')) + .then(() => ngServe( + '--ssl', 'true', + '--ssl-key', assetDir('ssl/server.key'), + '--ssl-cert', assetDir('ssl/server.crt') + )) + .then(() => request('https://localhost:4200/')) .then(body => { if (!body.match(/Loading...<\/app-root>/)) { throw new Error('Response does not match expected value.');