Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: read https config from app:config #75

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 15 additions & 11 deletions README.md
Expand Up @@ -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 |
atian25 marked this conversation as resolved.
Show resolved Hide resolved
| require | `Array\|String` | will inject into worker/agent process |

## References

- [SecureContextOptions](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options)

## Env

Expand Down
22 changes: 22 additions & 0 deletions index.d.ts
@@ -0,0 +1,22 @@
import { SecureContextOptions } from 'tls';


/** Cluster Options */
export interface Options {
/** 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: Options, callback: () => void): void;
18 changes: 7 additions & 11 deletions lib/app_worker.js
Expand Up @@ -9,17 +9,20 @@ if (options.require) {
});
}

const fs = require('fs');
const debug = require('debug')('egg-cluster');
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 */ {};
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);
Expand All @@ -40,16 +43,9 @@ 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),
});
server = require('https').createServer(httpsOptions, 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);
Expand Down
17 changes: 1 addition & 16 deletions lib/utils/options.js
Expand Up @@ -5,8 +5,7 @@ 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');


module.exports = function(options) {
const defaults = {
Expand Down Expand Up @@ -35,20 +34,6 @@ 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.port = parseInt(options.port, 10) || undefined;
options.workers = parseInt(options.workers, 10);
if (options.require) options.require = [].concat(options.require);
Expand Down
80 changes: 80 additions & 0 deletions lib/utils/tls_options.js
@@ -0,0 +1,80 @@
'use strict';

const fs = require('fs');
const assert = require('assert');

/**
* Parse TLS SecureContextOptions
*
* @param {SecureContextOptions|void} options tls
* @return {SecureContextOptions|void} parsed options
*/
function parseTLSOpts(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: "${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: "${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: "${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: "${opts.pfx}"`);
opts.pfx = fs.readFileSync(opts.pfx);
}

if (Object.keys(opts).length) {
return opts;
}
}


/**
* Merge TLS options. first param with higher priority
*
* @param {SecureContextOptions|void} optionsHttps from options
* @param {SecureContextOptions|void} listenHttps from listenConfig
* @return {SecureContextOptions|void} merged
*/
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') {
Object.assign(ret, listenHttps);
}
/* istanbul ignore else */
if (optionsHttps && typeof optionsHttps === 'object') {
Object.assign(ret, optionsHttps);
}

if (Object.keys(ret).length) {
return ret;
}
}


module.exports = {
mergeTLSOpts,
parseTLSOpts,
};
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -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 .",
Expand All @@ -14,6 +15,7 @@
},
"files": [
"index.js",
"index.d.ts",
"lib"
],
"repository": {
Expand Down
10 changes: 6 additions & 4 deletions test/options.test.js
Expand Up @@ -15,11 +15,13 @@ describe('test/options.test.js', () => {
assert(options.port === undefined);
});

it('should start with https and listen 8443', () => {

it('should start with https and listen 8443 with https.{key|cert}', () => {
const options = parseOptions({
https: true,
key: utils.getFilepath('server.key'),
cert: utils.getFilepath('server.crt'),
https: {
key: utils.getFilepath('server.key'),
cert: utils.getFilepath('server.crt'),
},
});
assert(options.port === 8443);
assert(options.https.key);
Expand Down