mirror of
https://github.com/thinkjs/thinkjs.git
synced 2026-01-18 14:26:56 +00:00
275 lines
7.6 KiB
JavaScript
275 lines
7.6 KiB
JavaScript
'use strict';
|
|
|
|
import cluster from 'cluster';
|
|
import fs from 'fs';
|
|
import domain from 'domain';
|
|
import os from 'os';
|
|
import http from 'http';
|
|
|
|
export default class extends think.http.base {
|
|
/**
|
|
* exec logic
|
|
* @return {Promise} []
|
|
*/
|
|
execLogic(){
|
|
let name = `${this.http.module}/${think.dirname.logic}/${this.http.controller}`;
|
|
let cls = think.require(name, true);
|
|
if (!cls) {
|
|
return Promise.resolve();
|
|
}
|
|
let instance = new cls(this.http);
|
|
let action = think.camelCase(this.http.action);
|
|
if (think.isFunction(instance[`${action}Action`])) {
|
|
return this.action(instance, action, false);
|
|
}
|
|
//call action
|
|
else if (think.isFunction(instance.__call)) {
|
|
return this.action(instance, '__call', false);
|
|
}
|
|
//only has before method
|
|
else if(think.isFunction(instance.__before)){
|
|
return think.co(instance.__before(instance));
|
|
//return think.co.wrap(instance.__before).bind(instance)(instance);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
/**
|
|
* exec controller
|
|
* @return {Promise} []
|
|
*/
|
|
execController(){
|
|
let http = this.http;
|
|
let name = `${http.module}/${think.dirname.controller}/${http.controller}`;
|
|
let cls = think.require(name, true);
|
|
if (cls) {
|
|
return this.execAction(new cls(http));
|
|
}
|
|
http.error = new Error(think.locale('CONTROLLER_NOT_FOUND', http.controller, http.url));
|
|
return think.statusAction(404, http);
|
|
}
|
|
/**
|
|
* exec action
|
|
* @param {Object} controller [controller instance]
|
|
* @param {Boolean} call [is call controller]
|
|
* @return {Promise} []
|
|
*/
|
|
execAction(controller){
|
|
let http = this.http;
|
|
//if is rest api, rewrite action
|
|
if(controller._isRest){
|
|
let method = controller._method;
|
|
//get method from GET params
|
|
if(method){
|
|
method = controller.get(method).toLowerCase();
|
|
}
|
|
if(!method){
|
|
method = http.method.toLowerCase();
|
|
}
|
|
http.action = method;
|
|
}
|
|
let action = think.camelCase(http.action);
|
|
let actionWithSuffix = `${action}Action`;
|
|
//action is exist
|
|
if(think.isFunction(controller[actionWithSuffix])){
|
|
return this.action(controller, action, false);
|
|
}
|
|
//call action
|
|
if(think.isFunction(controller.__call)){
|
|
return this.action(controller, '__call', false);
|
|
}
|
|
http.error = new Error(think.locale('ACTION_NOT_FOUND', actionWithSuffix, http.url));
|
|
return think.statusAction(404, http);
|
|
}
|
|
/**
|
|
* exec
|
|
* @return {Promise} []
|
|
*/
|
|
async exec(){
|
|
await this.hook('resource');
|
|
await this.hook('route_parse');
|
|
|
|
//set module config, can not set config in request
|
|
this.http._config = think.getModuleConfig(this.http.module);
|
|
|
|
//babel compile error
|
|
if(think.compileError){
|
|
this.http.error = think.compileError;
|
|
return think.statusAction(500, this.http);
|
|
}
|
|
|
|
await this.hook('logic_before');
|
|
await this.execLogic().catch(err => {
|
|
//ignore prevent reject promise
|
|
//make logic_after hook can be invoked
|
|
if(!think.isPrevent(err)){
|
|
return Promise.reject(err);
|
|
}
|
|
});
|
|
await this.hook('logic_after');
|
|
|
|
//http is end
|
|
if (this.http._isEnd) {
|
|
return think.prevent();
|
|
}
|
|
|
|
await this.hook('controller_before');
|
|
await this.execController().catch(err => {
|
|
//ignore prevent reject promise
|
|
//make controller_after & response_end hook can be invoked
|
|
if(!think.isPrevent(err)){
|
|
return Promise.reject(err);
|
|
}
|
|
});
|
|
await this.hook('controller_after');
|
|
|
|
await this.hook('response_end');
|
|
}
|
|
/**
|
|
* run
|
|
* @return {} []
|
|
*/
|
|
run(){
|
|
let http = this.http;
|
|
http.header('X-Powered-By', `thinkjs-${think.version}`);
|
|
//service off
|
|
if(!think.config('service_on')){
|
|
http.error = new Error(think.locale('SERVICE_UNAVAILABLE'));
|
|
return think.statusAction(503, http);
|
|
}
|
|
//deny access by ip + port
|
|
if (think.config('proxy_on') && http.host !== http.hostname && !http.socket) {
|
|
http.error = new Error(think.locale('DISALLOW_PORT'));
|
|
return think.statusAction(403, http);
|
|
}
|
|
|
|
let instance = domain.create();
|
|
instance.on('error', err => {
|
|
http.error = err;
|
|
think.statusAction(500, http, true);
|
|
});
|
|
instance.run(async () => {
|
|
try{
|
|
await this.exec();
|
|
}catch(err){
|
|
http.error = err;
|
|
think.statusAction(500, http, true);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* create server
|
|
* @return {} []
|
|
*/
|
|
static createServer(){
|
|
let handle = think.config('create_server');
|
|
let host = think.config('host');
|
|
let port = think.port || think.config('port');
|
|
//createServer callback
|
|
let callback = async (req, res) => {
|
|
let http = await think.http(req, res);
|
|
return new this(http).run();
|
|
};
|
|
let server;
|
|
//define createServer in application
|
|
if (handle) {
|
|
server = handle(callback, port, host, this);
|
|
}else{
|
|
//create server
|
|
server = http.createServer(callback);
|
|
server.listen(port, host);
|
|
}
|
|
this.logPid(port);
|
|
|
|
//start websocket
|
|
let websocket = think.parseConfig(think.config('websocket'));
|
|
if(websocket.on){
|
|
let Cls = think.adapter('websocket', websocket.type);
|
|
let instance = new Cls(server, websocket, this);
|
|
instance.run();
|
|
}
|
|
}
|
|
/**
|
|
* log
|
|
* @return {} []
|
|
*/
|
|
static log(){
|
|
let host = think.config('host');
|
|
let port = think.port || think.config('port');
|
|
let websocketStatus = think.config('websocket.on') ? 'open' : 'closed';
|
|
let clusterStatus = think.config('cluster_on') ? 'open' : 'closed';
|
|
|
|
think.log('Server running at http://' + (host || '127.0.0.1') + ':' + port + '/', 'THINK');
|
|
think.log(() => `ThinkJS Version: ${think.version}`, 'THINK');
|
|
think.log(colors => `Cluster Status: ${colors.magenta(clusterStatus)}`, 'THINK');
|
|
think.log(colors => `WebSocket Status: ${colors.magenta(websocketStatus)}`, 'THINK');
|
|
think.log(colors => `File Auto Compile: ${colors.magenta(!!think.autoCompile)}`, 'THINK');
|
|
think.log(colors => `File Auto Reload: ${colors.magenta(think.config('auto_reload'))}`, 'THINK');
|
|
think.log(colors => `App Enviroment: ${colors.magenta(think.env)}\n`, 'THINK');
|
|
}
|
|
/**
|
|
* cli mode
|
|
* @return {} []
|
|
*/
|
|
static async cli(){
|
|
let http = await think.http(think.cli);
|
|
return new this(http).run();
|
|
}
|
|
/**
|
|
* load process id
|
|
* @return {} []
|
|
*/
|
|
static logPid(port){
|
|
if (!cluster.isMaster || !think.config('log_pid')) {
|
|
return;
|
|
}
|
|
let dir = think.getPath(undefined, think.dirname.runtime) + '/pid';
|
|
think.mkdir(dir);
|
|
let pidFile = `${dir}/${port}.pid`;
|
|
fs.writeFileSync(pidFile, process.pid);
|
|
//change pid file mode
|
|
think.chmod(pidFile);
|
|
//remove pid file when process exit
|
|
process.on('SIGTERM', () => {
|
|
if (fs.existsSync(pidFile)) {
|
|
fs.unlinkSync(pidFile);
|
|
}
|
|
process.exit(0);
|
|
});
|
|
}
|
|
/**
|
|
* http mode
|
|
* @return {} []
|
|
*/
|
|
static http(){
|
|
let nums = think.config('cluster_on');
|
|
if (!nums) {
|
|
this.createServer();
|
|
return this.log();
|
|
}
|
|
if (nums === true) {
|
|
nums = os.cpus().length;
|
|
}
|
|
if (cluster.isMaster) {
|
|
for (let i = 0; i < nums; i++) {
|
|
cluster.fork();
|
|
}
|
|
cluster.on('exit', worker => {
|
|
think.log(new Error(think.locale('WORKER_DIED', worker.process.pid)), 'THINK');
|
|
process.nextTick(() => cluster.fork());
|
|
});
|
|
this.log();
|
|
}else {
|
|
this.createServer();
|
|
}
|
|
}
|
|
/**
|
|
* run
|
|
* @return {} []
|
|
*/
|
|
static run(){
|
|
if (think.cli) {
|
|
return this.cli();
|
|
}
|
|
return this.http();
|
|
}
|
|
} |