From 5acb069904ccf2dbeda61140fd90c71fae35912f Mon Sep 17 00:00:00 2001 From: Stefan Baramov Date: Thu, 20 Oct 2016 09:53:02 -0400 Subject: [PATCH] feat(ssl): add support for the ssl options of the ng serve task: --ssl, --ssl-cert, and --ssl-key (#2792) --- packages/angular-cli/custom-typings.d.ts | 3 +++ packages/angular-cli/tasks/serve-webpack.ts | 23 ++++++++++++++++-- tests/e2e/assets/ssl/server.crt | 23 ++++++++++++++++++ tests/e2e/assets/ssl/server.key | 27 +++++++++++++++++++++ tests/e2e/tests/misc/ssl-default.ts | 16 ++++++++++++ tests/e2e/tests/misc/ssl-with-cert.ts | 22 +++++++++++++++++ tests/e2e/utils/http.ts | 5 ++-- 7 files changed, 115 insertions(+), 4 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/packages/angular-cli/custom-typings.d.ts b/packages/angular-cli/custom-typings.d.ts index 5f17ab354124..224c2e974ee0 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 b7234b6a02b6..9c2e0a6229ad 100644 --- a/packages/angular-cli/tasks/serve-webpack.ts +++ b/packages/angular-cli/tasks/serve-webpack.ts @@ -52,6 +52,19 @@ export default Task.extend({ } } + let sslKey: string = null; + let sslCert: string = 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, @@ -66,13 +79,19 @@ export default Task.extend({ compress: commandOptions.target === 'production', watchOptions: { poll: CliConfig.fromProject().config.defaults.poll - } + }, + 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}. ** `)); 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..e16ec54291c9 --- /dev/null +++ b/tests/e2e/tests/misc/ssl-default.ts @@ -0,0 +1,16 @@ +import { request } from '../../utils/http'; +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..3930c91625fb --- /dev/null +++ b/tests/e2e/tests/misc/ssl-with-cert.ts @@ -0,0 +1,22 @@ +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..fba900c7e6e0 100644 --- a/tests/e2e/utils/http.ts +++ b/tests/e2e/utils/http.ts @@ -4,11 +4,12 @@ import * as _request from 'request'; export function request(url: string): Promise { return new Promise((resolve, reject) => { - _request(url, (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) { - reject(new Error(`Requesting "${url}" returned status code ${response.statusCode}.`); + reject(new Error(`Requesting "${url}" returned status code ${response.statusCode}.`)); } else { resolve(body); }