From 2993cdd9b5974776f6c12fa205897ecf7ece4205 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Wed, 29 Aug 2018 20:34:14 +0800 Subject: [PATCH 01/17] feat: read https config from app:config @TOFIX log print http not https cause master.js@Line469 use this.options.https --- lib/app_worker.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/app_worker.js b/lib/app_worker.js index 4daf9ce..91e9bb2 100644 --- a/lib/app_worker.js +++ b/lib/app_worker.js @@ -21,6 +21,7 @@ const app = new Application(options); const clusterConfig = app.config.cluster || /* istanbul ignore next */ {}; const listenConfig = clusterConfig.listen || /* istanbul ignore next */ {}; const port = options.port = options.port || listenConfig.port; +const https = options.https || listenConfig.https process.send({ to: 'master', action: 'realport', data: port }); app.ready(startServer); @@ -41,10 +42,10 @@ function startServer(err) { app.removeListener('startTimeout', startTimeoutHandler); let server; - if (options.https) { - const httpsOptions = Object.assign({}, options.https, { - key: fs.readFileSync(options.https.key), - cert: fs.readFileSync(options.https.cert), + if (https) { + const httpsOptions = Object.assign({}, https, { + key: fs.readFileSync(https.key), + cert: fs.readFileSync(https.cert), }); server = require('https').createServer(httpsOptions, app.callback()); } else { From f2f03688068e0e4b59ef68f26ca411e2a07b87ac Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Wed, 29 Aug 2018 20:49:59 +0800 Subject: [PATCH 02/17] fix: Missing semicolon --- lib/app_worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/app_worker.js b/lib/app_worker.js index 91e9bb2..a6c747e 100644 --- a/lib/app_worker.js +++ b/lib/app_worker.js @@ -21,7 +21,7 @@ const app = new Application(options); const clusterConfig = app.config.cluster || /* istanbul ignore next */ {}; const listenConfig = clusterConfig.listen || /* istanbul ignore next */ {}; const port = options.port = options.port || listenConfig.port; -const https = options.https || listenConfig.https +const https = options.https || listenConfig.https; process.send({ to: 'master', action: 'realport', data: port }); app.ready(startServer); From fe79cc807a80014873c914035d8241b7833d1a2b Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 16:06:46 +0800 Subject: [PATCH 03/17] feat: options.https full support type SecureContextOptions --- lib/app_worker.js | 25 ++++++++++---- lib/utils/options.js | 17 ++------- lib/utils/tls_options.js | 74 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 lib/utils/tls_options.js diff --git a/lib/app_worker.js b/lib/app_worker.js index a6c747e..b543e6f 100644 --- a/lib/app_worker.js +++ b/lib/app_worker.js @@ -15,13 +15,16 @@ const gracefulExit = require('graceful-process'); const ConsoleLogger = require('egg-logger').EggConsoleLogger; const consoleLogger = new ConsoleLogger({ level: process.env.EGG_APP_WORKER_LOGGER_LEVEL }); +const tlsUtil = require('./utils/tls_options'); const Application = require(options.framework).Application; debug('new Application with options %j', options); + const app = new Application(options); const clusterConfig = app.config.cluster || /* istanbul ignore next */ {}; const listenConfig = clusterConfig.listen || /* istanbul ignore next */ {}; +listenConfig.https = tlsUtil.parseTLSOpts(listenConfig.https); +const https = tlsUtil.mergeTLSOpts(options.https, listenConfig.https); const port = options.port = options.port || listenConfig.port; -const https = options.https || listenConfig.https; process.send({ to: 'master', action: 'realport', data: port }); app.ready(startServer); @@ -42,12 +45,20 @@ function startServer(err) { app.removeListener('startTimeout', startTimeoutHandler); let server; - if (https) { - const httpsOptions = Object.assign({}, https, { - key: fs.readFileSync(https.key), - cert: fs.readFileSync(https.cert), - }); - server = require('https').createServer(httpsOptions, app.callback()); + if (https && typeof https === 'object') { + /* istanbul ignore else */ + if (typeof https.ca === 'string') { + https.ca = fs.readFileSync(https.ca); + } + /* istanbul ignore else */ + if (typeof https.cert === 'string') { + https.cert = fs.readFileSync(https.cert); + } + /* istanbul ignore else */ + if (typeof https.key === 'string') { + https.key = fs.readFileSync(https.key); + } + server = require('https').createServer(https, app.callback()); } else { server = require('http').createServer(app.callback()); } diff --git a/lib/utils/options.js b/lib/utils/options.js index 7842a97..0b3c993 100644 --- a/lib/utils/options.js +++ b/lib/utils/options.js @@ -8,6 +8,8 @@ const utils = require('egg-utils'); const is = require('is-type-of'); const deprecate = require('depd')('egg'); +const parseTLSOpts = require('./tls_options').parseTLSOpts + module.exports = function(options) { const defaults = { framework: '', @@ -35,20 +37,7 @@ module.exports = function(options) { assert(egg.Application, `should define Application in ${options.framework}`); assert(egg.Agent, `should define Agent in ${options.framework}`); - // https - if (options.https) { - if (is.boolean(options.https)) { - // TODO: compatible options.key, options.cert, will remove at next major - deprecate('[master] Please use `https: { key, cert }` instead of `https: true`'); - options.https = { - key: options.key, - cert: options.cert, - }; - } - assert(options.https.key && fs.existsSync(options.https.key), 'options.https.key should exists'); - assert(options.https.cert && fs.existsSync(options.https.cert), 'options.https.cert should exists'); - } - + options.https = parseTLSOpts(options.https) options.port = parseInt(options.port, 10) || undefined; options.workers = parseInt(options.workers, 10); if (options.require) options.require = [].concat(options.require); diff --git a/lib/utils/tls_options.js b/lib/utils/tls_options.js new file mode 100644 index 0000000..a3953e4 --- /dev/null +++ b/lib/utils/tls_options.js @@ -0,0 +1,74 @@ +'use strict'; + +const fs = require('fs'); +const assert = require('assert'); +const deprecate = require('depd')('egg'); + +/** + * Parse TLS SecureContextOptions + * + * @param {SecureContextOptions|void} options + * @returns {SecureContextOptions|void} + */ +function parseTLSOpts(options) { + if (options === true) { + deprecate('[master] Deprecated: Please use `https: { key, cert }` instead of `https: true`. \ + Docs: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options'); + return; + } + else if (! options) { + return; + } + + const opts = Object.assign({}, options); + + /* istanbul ignore else */ + if (typeof opts.ca === 'string') { + assert(opts.ca && fs.existsSync(opts.ca), 'File of https.ca should exists'); + } + /* istanbul ignore else */ + if (typeof opts.cert === 'string') { + assert(opts.cert && fs.existsSync(opts.cert), 'File of https.cert should exists'); + } + /* istanbul ignore else */ + if (typeof opts.key === 'string') { + assert(opts.key && fs.existsSync(opts.key), 'File of https.key should exists'); + } + /* istanbul ignore else */ + if (typeof opts.pfx === 'string') { + assert(opts.pfx && fs.existsSync(opts.pfx), 'File of https.pfx should exists'); + } + + return opts; +}; + + +/** + * Merge TLS options. first param with higher priority + * + * @param {SecureContextOptions|void} optionsHttp + * @param {SecureContextOptions|void} listenHttp + * @returns {SecureContextOptions|void} + */ +function mergeTLSOpts(optionsHttp, listenHttp) { + const ret = { }; + + /* istanbul ignore else */ + if (listenHttp && typeof listenHttp === 'object') { + Object.assign(ret, listenHttp); + } + /* istanbul ignore else */ + if (optionsHttp && typeof optionsHttp === 'object') { + Object.assign(ret, optionsHttp); + } + + if (Object.keys(ret).length) { + return ret; + } +} + + +module.exports = { + mergeTLSOpts, + parseTLSOpts, +} \ No newline at end of file From f409a3a04c3ce76b48d070473ce2d8d99b5241c7 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 17:24:16 +0800 Subject: [PATCH 04/17] docs: Update typeof https to SecureContextOptions --- README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1079c00..ed6cd8f 100644 --- a/README.md +++ b/README.md @@ -50,17 +50,21 @@ startCluster(options, () => { ## Options -| Param | Type | Description | -| ------------ | --------- | ---------------------------------------- | -| baseDir | `String` | directory of application | -| framework | `String` | specify framework that can be absolute path or npm package | -| plugins | `Object` | plugins for unittest | -| workers | `Number` | numbers of app workers | -| sticky | `Boolean` | sticky mode server | -| port | `Number` | port | -| https | `Object` | start a https server, note: `key` / `cert` should be full path to file | -| typescript | `Boolean` | enable loader's typescript support | -| require | `Array\|String` | will inject into worker/agent process | +| Param | Type | Description | +| ---------- | ---------------------- | ----------------------------------------------------------------------- | +| baseDir | `String` | directory of application | +| framework | `String` | specify framework that can be absolute path or npm package | +| plugins | `Object` | plugins for unittest | +| workers | `Number` | numbers of app workers | +| sticky | `Boolean` | sticky mode server | +| port | `Number` | port | +| https | `SecureContextOptions` | start a https server, note: `key|cert|ca` must be absolute path if file | +| typescript | `Boolean` | enable loader's typescript support | +| require | `Array\|String` | will inject into worker/agent process | + +## References + +- [SecureContextOptions](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) ## Env From 881e79bb1d0a20fe9403504a3e9fba000f705fe8 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 17:36:00 +0800 Subject: [PATCH 05/17] fix(eslint) --- lib/utils/options.js | 6 ++---- lib/utils/tls_options.js | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/utils/options.js b/lib/utils/options.js index 0b3c993..627c331 100644 --- a/lib/utils/options.js +++ b/lib/utils/options.js @@ -5,10 +5,8 @@ const fs = require('fs'); const path = require('path'); const assert = require('assert'); const utils = require('egg-utils'); -const is = require('is-type-of'); -const deprecate = require('depd')('egg'); -const parseTLSOpts = require('./tls_options').parseTLSOpts +const parseTLSOpts = require('./tls_options').parseTLSOpts; module.exports = function(options) { const defaults = { @@ -37,7 +35,7 @@ module.exports = function(options) { assert(egg.Application, `should define Application in ${options.framework}`); assert(egg.Agent, `should define Agent in ${options.framework}`); - options.https = parseTLSOpts(options.https) + options.https = parseTLSOpts(options.https); options.port = parseInt(options.port, 10) || undefined; options.workers = parseInt(options.workers, 10); if (options.require) options.require = [].concat(options.require); diff --git a/lib/utils/tls_options.js b/lib/utils/tls_options.js index a3953e4..aa28dd0 100644 --- a/lib/utils/tls_options.js +++ b/lib/utils/tls_options.js @@ -7,16 +7,15 @@ const deprecate = require('depd')('egg'); /** * Parse TLS SecureContextOptions * - * @param {SecureContextOptions|void} options - * @returns {SecureContextOptions|void} + * @param {SecureContextOptions|void} options tls + * @return {SecureContextOptions|void} parsed options */ function parseTLSOpts(options) { if (options === true) { - deprecate('[master] Deprecated: Please use `https: { key, cert }` instead of `https: true`. \ - Docs: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options'); + const msg = '[master] Deprecated: Please use `https: { key, cert }` instead of `https: true`. Docs: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options'; + deprecate(msg); return; - } - else if (! options) { + } else if (!options) { return; } @@ -40,15 +39,15 @@ function parseTLSOpts(options) { } return opts; -}; +} /** * Merge TLS options. first param with higher priority * - * @param {SecureContextOptions|void} optionsHttp - * @param {SecureContextOptions|void} listenHttp - * @returns {SecureContextOptions|void} + * @param {SecureContextOptions|void} optionsHttp from options + * @param {SecureContextOptions|void} listenHttp from listenConfig + * @return {SecureContextOptions|void} merged */ function mergeTLSOpts(optionsHttp, listenHttp) { const ret = { }; @@ -71,4 +70,4 @@ function mergeTLSOpts(optionsHttp, listenHttp) { module.exports = { mergeTLSOpts, parseTLSOpts, -} \ No newline at end of file +}; From 97e09247b0dbb6488f1fb5ddc16271fca8037a26 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 17:59:26 +0800 Subject: [PATCH 06/17] refactor: parameter name of mergeTLSOpts() --- lib/utils/tls_options.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/utils/tls_options.js b/lib/utils/tls_options.js index aa28dd0..ff6ea5a 100644 --- a/lib/utils/tls_options.js +++ b/lib/utils/tls_options.js @@ -45,20 +45,20 @@ function parseTLSOpts(options) { /** * Merge TLS options. first param with higher priority * - * @param {SecureContextOptions|void} optionsHttp from options - * @param {SecureContextOptions|void} listenHttp from listenConfig + * @param {SecureContextOptions|void} optionsHttps from options + * @param {SecureContextOptions|void} listenHttps from listenConfig * @return {SecureContextOptions|void} merged */ -function mergeTLSOpts(optionsHttp, listenHttp) { +function mergeTLSOpts(optionsHttps, listenHttps) { const ret = { }; /* istanbul ignore else */ - if (listenHttp && typeof listenHttp === 'object') { - Object.assign(ret, listenHttp); + if (listenHttps && typeof listenHttps === 'object') { + Object.assign(ret, listenHttps); } /* istanbul ignore else */ - if (optionsHttp && typeof optionsHttp === 'object') { - Object.assign(ret, optionsHttp); + if (optionsHttps && typeof optionsHttps === 'object') { + Object.assign(ret, optionsHttps); } if (Object.keys(ret).length) { From 7c49e2fd97f7aa4e4ea2ecb85383060a63eae6e5 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 18:03:24 +0800 Subject: [PATCH 07/17] feat: return undefined if result is empty object --- lib/utils/tls_options.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/utils/tls_options.js b/lib/utils/tls_options.js index ff6ea5a..0d9e46c 100644 --- a/lib/utils/tls_options.js +++ b/lib/utils/tls_options.js @@ -38,7 +38,9 @@ function parseTLSOpts(options) { assert(opts.pfx && fs.existsSync(opts.pfx), 'File of https.pfx should exists'); } - return opts; + if (Object.keys(opts).length) { + return opts; + } } From 13f841e37035799e2235e33a818562bf9a472160 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 18:15:31 +0800 Subject: [PATCH 08/17] test: add test/tls_options.test.js --- test/tls_options.test.js | 130 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 test/tls_options.test.js diff --git a/test/tls_options.test.js b/test/tls_options.test.js new file mode 100644 index 0000000..ca3b758 --- /dev/null +++ b/test/tls_options.test.js @@ -0,0 +1,130 @@ +'use strict'; + +const readFileSync = require('fs').readFileSync; +const join = require('path').join; +const assert = require('assert'); +const tlsUtil = require('../lib/utils/tls_options'); +const mergeTLSOpts = tlsUtil.mergeTLSOpts; +const parseTLSOpts = tlsUtil.parseTLSOpts; + +describe('test/tls_options.test.js', () => { + describe('Should parseTLSOpts()', () => { + it('with https:true', () => { + const ret = parseTLSOpts(true); + assert(typeof ret === 'undefined'); + }); + + it('with https:false', () => { + const ret = parseTLSOpts(false); + assert(typeof ret === 'undefined'); + }); + + it('with https:{}', () => { + const ret = parseTLSOpts({}); + assert(typeof ret === 'undefined'); + }); + + it('with https:undefined', () => { + const ret = parseTLSOpts(); + assert(typeof ret === 'undefined'); + }); + + it('with https:key/cert by file type', () => { + const key = join(__dirname, 'fixtures/server.key'); + const cert = join(__dirname, 'fixtures/server.crt'); + const opts = { + key, + cert, + }; + const ret = parseTLSOpts(opts); + + assert(ret && Object.keys(ret).length === 2); + assert(ret.key === key); + assert(ret.cert === cert); + }); + + it('with https:key/cert by Buffer type', () => { + const key = join(__dirname, 'fixtures/server.key'); + const cert = join(__dirname, 'fixtures/server.crt'); + const opts = { + key: readFileSync(key), + cert: readFileSync(cert), + }; + const ret = parseTLSOpts(opts); + + assert(ret && Object.keys(ret).length === 2); + assert(ret.key.toString() === opts.key.toString()); + assert(ret.cert.toString() === opts.cert.toString()); + }); + }); + + + describe('Should mergeTLSOpts()', () => { + it('with both optionsHttps and listenHttps invalid', () => { + let ret = mergeTLSOpts(true, true); + assert(typeof ret === 'undefined'); + + ret = mergeTLSOpts({}, {}); + assert(typeof ret === 'undefined'); + + ret = mergeTLSOpts({}); + assert(typeof ret === 'undefined'); + + ret = mergeTLSOpts(true); + assert(typeof ret === 'undefined'); + + ret = mergeTLSOpts(); + assert(typeof ret === 'undefined'); + }); + + it('with valid optionsHttps and invalid listenHttps', () => { + const opts = { + key: Math.random().toString(), + cert: Math.random().toString(), + }; + const ret = mergeTLSOpts(opts, {}); + + assert(ret && Object.keys(ret).length === 2); + assert(ret && ret.key === opts.key && ret.cert === opts.cert); + }); + + it('with invalid optionsHttps and valid listenHttps', () => { + const opts = { + key: Math.random().toString(), + cert: Math.random().toString(), + }; + let ret = mergeTLSOpts({}, opts); + assert(ret && Object.keys(ret).length === 2); + assert(ret && ret.key === opts.key && ret.cert === opts.cert); + + ret = mergeTLSOpts(true, opts); + assert(ret && Object.keys(ret).length === 2); + assert(ret && ret.key === opts.key && ret.cert === opts.cert); + + ret = mergeTLSOpts(false, opts); + assert(ret && Object.keys(ret).length === 2); + assert(ret && ret.key === opts.key && ret.cert === opts.cert); + }); + + it('with both optionsHttps and listenHttps valid', () => { + const optionsHttps = { + key: Math.random().toString(), + cert: Math.random().toString(), + }; + const listenHttps = { + key: Math.random().toString(), + cert: Math.random().toString(), + ca: Math.random().toString(), + }; + const ret = mergeTLSOpts(optionsHttps, listenHttps); + + assert(ret && Object.keys(ret).length === 3); + assert(ret + && ret.key === optionsHttps.key + && ret.cert === optionsHttps.cert + && ret.ca === listenHttps.ca + ); + }); + }); + +}); From 34ec43e13aeded04c4076bcf7ff3c164c82fd487 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 19:34:58 +0800 Subject: [PATCH 09/17] test: should options.{key|cert} NOT works --- test/options.test.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/options.test.js b/test/options.test.js index a8c3d93..d725c17 100644 --- a/test/options.test.js +++ b/test/options.test.js @@ -15,12 +15,26 @@ describe('test/options.test.js', () => { assert(options.port === undefined); }); - it('should start with https and listen 8443', () => { + + it('should options.{key|cert} NOT works', () => { const options = parseOptions({ https: true, key: utils.getFilepath('server.key'), cert: utils.getFilepath('server.crt'), }); + + assert(options.port === 8443); + assert(typeof options.https === 'undefined'); + }); + + + it('should start with https and listen 8443 with https.{key|cert}', () => { + const options = parseOptions({ + https: { + key: utils.getFilepath('server.key'), + cert: utils.getFilepath('server.crt'), + }, + }); assert(options.port === 8443); assert(options.https.key); assert(options.https.cert); From 5d906cb9d74f3cb2d82ca2c5f23e888c6bfbece6 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 19:41:08 +0800 Subject: [PATCH 10/17] feat(typings): add index.d.ts --- index.d.ts | 22 ++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 24 insertions(+) create mode 100644 index.d.ts diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..79ec842 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,22 @@ +import { SecureContextOptions } from 'tls'; + + +export interface ClusterOptions { + /** specify framework that can be absolute path or npm package */ + framework?: string; + /** directory of application, default to `process.cwd()` */ + baseDir?: string; + /** customized plugins, for unittest */ + plugins?: object | null; + /** numbers of app workers, default to `os.cpus().length` */ + workers?: number; + /** listening port, default to 7001(http) or 8443(https) */ + port?: number; + /** Ref: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options */ + https?: SecureContextOptions; + [prop: string]: any; +} + + + +export function startCluster(options: ClusterOptions, callback: (...args: any[]) => any): void; diff --git a/package.json b/package.json index 3511069..a0daa2b 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.20.0", "description": "cluster manager for egg", "main": "index.js", + "types": "index.d.ts", "scripts": { "autod": "autod", "lint": "eslint .", @@ -14,6 +15,7 @@ }, "files": [ "index.js", + "index.d.ts", "lib" ], "repository": { From 792ae9c7afde9285e4ae5cce28bac142121c70cb Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Mon, 10 Sep 2018 20:17:18 +0800 Subject: [PATCH 11/17] docs: fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed6cd8f..40d8fb4 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ startCluster(options, () => { | workers | `Number` | numbers of app workers | | sticky | `Boolean` | sticky mode server | | port | `Number` | port | -| https | `SecureContextOptions` | start a https server, note: `key|cert|ca` must be absolute path if file | +| https | `SecureContextOptions` | start a https server, note: `key\|cert\|ca` must be absolute path if file | | typescript | `Boolean` | enable loader's typescript support | | require | `Array\|String` | will inject into worker/agent process | From e0d9063427a4a1062072ef2ea14620c05f4892a2 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Tue, 11 Sep 2018 10:11:59 +0800 Subject: [PATCH 12/17] refactor(typings): rename interface ClusterOptions to Options --- index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 79ec842..b7b9135 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,7 +1,8 @@ import { SecureContextOptions } from 'tls'; -export interface ClusterOptions { +/** Cluster Options */ +export interface Options { /** specify framework that can be absolute path or npm package */ framework?: string; /** directory of application, default to `process.cwd()` */ @@ -18,5 +19,4 @@ export interface ClusterOptions { } - -export function startCluster(options: ClusterOptions, callback: (...args: any[]) => any): void; +export function startCluster(options: Options, callback: (...args: any[]) => any): void; From 649a8bc50097d6ee1df204c1f79a40e565df1a49 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Tue, 11 Sep 2018 10:35:12 +0800 Subject: [PATCH 13/17] feat(typings): update callback signature of startCluster() --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index b7b9135..647b353 100644 --- a/index.d.ts +++ b/index.d.ts @@ -19,4 +19,4 @@ export interface Options { } -export function startCluster(options: Options, callback: (...args: any[]) => any): void; +export function startCluster(options: Options, callback: () => void): void; From a98894a065ee4000b959a8b10221bd5692cb7772 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Tue, 11 Sep 2018 15:05:14 +0800 Subject: [PATCH 14/17] docs: format --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 40d8fb4..6b986a4 100644 --- a/README.md +++ b/README.md @@ -50,17 +50,17 @@ startCluster(options, () => { ## Options -| Param | Type | Description | -| ---------- | ---------------------- | ----------------------------------------------------------------------- | -| baseDir | `String` | directory of application | -| framework | `String` | specify framework that can be absolute path or npm package | -| plugins | `Object` | plugins for unittest | -| workers | `Number` | numbers of app workers | -| sticky | `Boolean` | sticky mode server | -| port | `Number` | port | +| Param | Type | Description | +| ---------- | ---------------------- | ------------------------------------------------------------------------- | +| baseDir | `String` | directory of application | +| framework | `String` | specify framework that can be absolute path or npm package | +| plugins | `Object` | plugins for unittest | +| workers | `Number` | numbers of app workers | +| sticky | `Boolean` | sticky mode server | +| port | `Number` | port | | https | `SecureContextOptions` | start a https server, note: `key\|cert\|ca` must be absolute path if file | -| typescript | `Boolean` | enable loader's typescript support | -| require | `Array\|String` | will inject into worker/agent process | +| typescript | `Boolean` | enable loader's typescript support | +| require | `Array\|String` | will inject into worker/agent process | ## References From 43f2cb313202ce7897d2d80fe92f984a8998ea74 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Tue, 11 Sep 2018 15:18:01 +0800 Subject: [PATCH 15/17] refactor: move reading cert file into parseTLSOpts() validate cert file exists during start worker --- lib/app_worker.js | 26 ++--------- lib/utils/options.js | 3 +- lib/utils/tls_options.js | 28 +++++++----- test/tls_options.test.js | 99 ++++++++++++++++++++++++++++++++-------- 4 files changed, 104 insertions(+), 52 deletions(-) diff --git a/lib/app_worker.js b/lib/app_worker.js index b543e6f..2f28520 100644 --- a/lib/app_worker.js +++ b/lib/app_worker.js @@ -9,7 +9,6 @@ if (options.require) { }); } -const fs = require('fs'); const debug = require('debug')('egg-cluster'); const gracefulExit = require('graceful-process'); const ConsoleLogger = require('egg-logger').EggConsoleLogger; @@ -22,8 +21,8 @@ debug('new Application with options %j', options); const app = new Application(options); const clusterConfig = app.config.cluster || /* istanbul ignore next */ {}; const listenConfig = clusterConfig.listen || /* istanbul ignore next */ {}; -listenConfig.https = tlsUtil.parseTLSOpts(listenConfig.https); -const https = tlsUtil.mergeTLSOpts(options.https, listenConfig.https); +let https = tlsUtil.mergeTLSOpts(options.https, listenConfig.https); +https = tlsUtil.parseTLSOpts(https); const port = options.port = options.port || listenConfig.port; process.send({ to: 'master', action: 'realport', data: port }); app.ready(startServer); @@ -44,24 +43,9 @@ function startServer(err) { app.removeListener('startTimeout', startTimeoutHandler); - let server; - if (https && typeof https === 'object') { - /* istanbul ignore else */ - if (typeof https.ca === 'string') { - https.ca = fs.readFileSync(https.ca); - } - /* istanbul ignore else */ - if (typeof https.cert === 'string') { - https.cert = fs.readFileSync(https.cert); - } - /* istanbul ignore else */ - if (typeof https.key === 'string') { - https.key = fs.readFileSync(https.key); - } - server = require('https').createServer(https, app.callback()); - } else { - server = require('http').createServer(app.callback()); - } + const server = https && typeof https === 'object' + ? require('https').createServer(https, app.callback()) + : require('http').createServer(app.callback()); server.once('error', err => { consoleLogger.error('[app_worker] server got error: %s, code: %s', err.message, err.code); diff --git a/lib/utils/options.js b/lib/utils/options.js index 627c331..687a572 100644 --- a/lib/utils/options.js +++ b/lib/utils/options.js @@ -6,7 +6,7 @@ const path = require('path'); const assert = require('assert'); const utils = require('egg-utils'); -const parseTLSOpts = require('./tls_options').parseTLSOpts; +// const parseTLSOpts = require('./tls_options').parseTLSOpts; module.exports = function(options) { const defaults = { @@ -35,7 +35,6 @@ module.exports = function(options) { assert(egg.Application, `should define Application in ${options.framework}`); assert(egg.Agent, `should define Agent in ${options.framework}`); - options.https = parseTLSOpts(options.https); options.port = parseInt(options.port, 10) || undefined; options.workers = parseInt(options.workers, 10); if (options.require) options.require = [].concat(options.require); diff --git a/lib/utils/tls_options.js b/lib/utils/tls_options.js index 0d9e46c..82d8cce 100644 --- a/lib/utils/tls_options.js +++ b/lib/utils/tls_options.js @@ -2,7 +2,7 @@ const fs = require('fs'); const assert = require('assert'); -const deprecate = require('depd')('egg'); +// const deprecate = require('depd')('egg'); /** * Parse TLS SecureContextOptions @@ -11,31 +11,33 @@ const deprecate = require('depd')('egg'); * @return {SecureContextOptions|void} parsed options */ function parseTLSOpts(options) { - if (options === true) { - const msg = '[master] Deprecated: Please use `https: { key, cert }` instead of `https: true`. Docs: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options'; - deprecate(msg); - return; - } else if (!options) { + const msg = '[master] Deprecated: Please use `https: { key, cert }` instead of `https: true`. Docs: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options'; + assert(!(options === true), msg); + + if (!options) { return; } - const opts = Object.assign({}, options); /* istanbul ignore else */ if (typeof opts.ca === 'string') { - assert(opts.ca && fs.existsSync(opts.ca), 'File of https.ca should exists'); + assert(opts.ca && fs.existsSync(opts.ca), `File of https.ca should exists: "${opts.ca}"`); + opts.ca = fs.readFileSync(opts.ca); } /* istanbul ignore else */ if (typeof opts.cert === 'string') { - assert(opts.cert && fs.existsSync(opts.cert), 'File of https.cert should exists'); + assert(opts.cert && fs.existsSync(opts.cert), `File of https.cert should exists: "${opts.cert}"`); + opts.cert = fs.readFileSync(opts.cert); } /* istanbul ignore else */ if (typeof opts.key === 'string') { - assert(opts.key && fs.existsSync(opts.key), 'File of https.key should exists'); + assert(opts.key && fs.existsSync(opts.key), `File of https.key should exists: "${opts.key}"`); + opts.key = fs.readFileSync(opts.key); } /* istanbul ignore else */ if (typeof opts.pfx === 'string') { - assert(opts.pfx && fs.existsSync(opts.pfx), 'File of https.pfx should exists'); + assert(opts.pfx && fs.existsSync(opts.pfx), `File of https.pfx should exists: "${opts.pfx}"`); + opts.pfx = fs.readFileSync(opts.pfx); } if (Object.keys(opts).length) { @@ -53,6 +55,10 @@ function parseTLSOpts(options) { */ function mergeTLSOpts(optionsHttps, listenHttps) { const ret = { }; + const msg = '[master] Deprecated: Please use `https: { key, cert }` instead of `https: true`. Docs: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options'; + + assert(!(optionsHttps === true), msg); + assert(!(listenHttps === true), msg); /* istanbul ignore else */ if (listenHttps && typeof listenHttps === 'object') { diff --git a/test/tls_options.test.js b/test/tls_options.test.js index ca3b758..aa5a45a 100644 --- a/test/tls_options.test.js +++ b/test/tls_options.test.js @@ -10,8 +10,7 @@ const parseTLSOpts = tlsUtil.parseTLSOpts; describe('test/tls_options.test.js', () => { describe('Should parseTLSOpts()', () => { it('with https:true', () => { - const ret = parseTLSOpts(true); - assert(typeof ret === 'undefined'); + assert.throws(() => parseTLSOpts(true)); }); it('with https:false', () => { @@ -29,52 +28,118 @@ describe('test/tls_options.test.js', () => { assert(typeof ret === 'undefined'); }); - it('with https:key/cert by file type', () => { + it('with invalid https.ca file path', () => { + const opts = { + ca: Math.random().toString(), + }; + assert.throws( + () => parseTLSOpts(opts), + new RegExp(`File of https.ca should exists: "${opts.ca}"`) + ); + + opts.ca = ''; + assert.throws( + () => parseTLSOpts(opts), + new RegExp(`File of https.ca should exists: "${opts.ca}"`) + ); + }); + + it('with invalid https.cert file path', () => { + const opts = { + cert: Math.random().toString(), + }; + assert.throws( + () => parseTLSOpts(opts), + new RegExp(`File of https.cert should exists: "${opts.cert}"`) + ); + + opts.cert = ''; + assert.throws( + () => parseTLSOpts(opts), + new RegExp(`File of https.cert should exists: "${opts.cert}"`) + ); + }); + + it('with invalid https.key file path', () => { + const opts = { + key: Math.random().toString(), + }; + assert.throws( + () => parseTLSOpts(opts), + new RegExp(`File of https.key should exists: "${opts.key}"`) + ); + + opts.key = ''; + assert.throws( + () => parseTLSOpts(opts), + new RegExp(`File of https.key should exists: "${opts.key}"`) + ); + }); + + it('with invalid https.pfx file path', () => { + const opts = { + pfx: Math.random().toString(), + }; + assert.throws( + () => parseTLSOpts(opts), + new RegExp(`File of https.pfx should exists: "${opts.pfx}"`) + ); + + opts.pfx = ''; + assert.throws( + () => parseTLSOpts(opts), + new RegExp(`File of https.pfx should exists: "${opts.pfx}"`) + ); + }); + + it('with https:key/cert by file path', () => { const key = join(__dirname, 'fixtures/server.key'); const cert = join(__dirname, 'fixtures/server.crt'); + const pfx = join(__dirname, 'fixtures/server.crt'); const opts = { key, cert, + pfx, }; const ret = parseTLSOpts(opts); - assert(ret && Object.keys(ret).length === 2); - assert(ret.key === key); - assert(ret.cert === cert); + assert(ret && Object.keys(ret).length === 3); + assert(ret.key.toString() === readFileSync(key).toString()); + assert(ret.cert.toString() === readFileSync(cert).toString()); + assert(ret.pfx.toString() === readFileSync(cert).toString()); }); - it('with https:key/cert by Buffer type', () => { + it('with https:key/cert by Buffer', () => { const key = join(__dirname, 'fixtures/server.key'); const cert = join(__dirname, 'fixtures/server.crt'); const opts = { key: readFileSync(key), cert: readFileSync(cert), + pfx: readFileSync(cert), }; const ret = parseTLSOpts(opts); - assert(ret && Object.keys(ret).length === 2); + assert(ret && Object.keys(ret).length === 3); assert(ret.key.toString() === opts.key.toString()); assert(ret.cert.toString() === opts.cert.toString()); + assert(ret.pfx.toString() === opts.pfx.toString()); }); }); describe('Should mergeTLSOpts()', () => { it('with both optionsHttps and listenHttps invalid', () => { - let ret = mergeTLSOpts(true, true); - assert(typeof ret === 'undefined'); - - ret = mergeTLSOpts({}, {}); + let ret = mergeTLSOpts({}, {}); assert(typeof ret === 'undefined'); ret = mergeTLSOpts({}); assert(typeof ret === 'undefined'); - ret = mergeTLSOpts(true); - assert(typeof ret === 'undefined'); - ret = mergeTLSOpts(); assert(typeof ret === 'undefined'); + + assert.throws(() => mergeTLSOpts(true)); + assert.throws(() => mergeTLSOpts(true, true)); }); it('with valid optionsHttps and invalid listenHttps', () => { @@ -97,9 +162,7 @@ describe('test/tls_options.test.js', () => { assert(ret && Object.keys(ret).length === 2); assert(ret && ret.key === opts.key && ret.cert === opts.cert); - ret = mergeTLSOpts(true, opts); - assert(ret && Object.keys(ret).length === 2); - assert(ret && ret.key === opts.key && ret.cert === opts.cert); + assert.throws(() => mergeTLSOpts(true, opts)); ret = mergeTLSOpts(false, opts); assert(ret && Object.keys(ret).length === 2); From f6fb15c747c0944a24a9e4868975972a21229a25 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Tue, 11 Sep 2018 16:38:05 +0800 Subject: [PATCH 16/17] chore: prune --- lib/utils/options.js | 1 - lib/utils/tls_options.js | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/utils/options.js b/lib/utils/options.js index 687a572..fa92e90 100644 --- a/lib/utils/options.js +++ b/lib/utils/options.js @@ -6,7 +6,6 @@ const path = require('path'); const assert = require('assert'); const utils = require('egg-utils'); -// const parseTLSOpts = require('./tls_options').parseTLSOpts; module.exports = function(options) { const defaults = { diff --git a/lib/utils/tls_options.js b/lib/utils/tls_options.js index 82d8cce..fe4eed4 100644 --- a/lib/utils/tls_options.js +++ b/lib/utils/tls_options.js @@ -2,7 +2,6 @@ const fs = require('fs'); const assert = require('assert'); -// const deprecate = require('depd')('egg'); /** * Parse TLS SecureContextOptions From 5944a6020fc093aabc76374817d45a68bd4d7373 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Fri, 14 Sep 2018 18:07:30 +0800 Subject: [PATCH 17/17] test: remove https in options.test.js test exists in tls_options.test.js --- test/options.test.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/options.test.js b/test/options.test.js index d725c17..42e0ba2 100644 --- a/test/options.test.js +++ b/test/options.test.js @@ -16,18 +16,6 @@ describe('test/options.test.js', () => { }); - it('should options.{key|cert} NOT works', () => { - const options = parseOptions({ - https: true, - key: utils.getFilepath('server.key'), - cert: utils.getFilepath('server.crt'), - }); - - assert(options.port === 8443); - assert(typeof options.https === 'undefined'); - }); - - it('should start with https and listen 8443 with https.{key|cert}', () => { const options = parseOptions({ https: {