define([ "dojo/_base/declare", "dojo/_base/lang", "dojo/when", "cluster", "os", "implab/safe", "./HttpResponse", "./BaseResponse", "dojo/Deferred", "./HttpException", "@implab/core/log/trace!"], function (declare, lang, when, cluster, os, safe, HttpResponse, BaseResponse, Deferred, HttpException, trace) { return declare(null, { _container: null, _serverPort: null, _bindAddr: null, _useCluster: null, _requestConfig: null, _restartFailedWorkers: null, // модули, обрабатывающие запрос. _modules: null, _handlers: null, constructor: function (options) { safe.argumentNotNull(options, "options"); safe.argumentNotNull(options.container, "options.container"); safe.argumentNotNull(options.serverPort, "options.serverPort"); safe.argumentNotNull(options.useCluster, "options.useCluster"); safe.argumentNotEmptyString(options.bindAddr, "options.bindAddr"); safe.argumentNotEmptyString(options.requestConfig, "options.requestConfig"); this.options = options || {}; this._modules = []; this._container = options.container; this._serverPort = options.serverPort; this._bindAddr = options.bindAddr; this._useCluster = options.useCluster; this._restartFailedWorkers = options.restartFailedWorkers; this._requestConfig = options.requestConfig; this._container.getService("httpHandlers").forEach(handler => { this.handle(handler); }); }, start: function () { let me = this; const numCPUs = os.cpus().length; if (cluster.isMaster && me._useCluster) { // Fork workers. for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', function (worker, code, signal) { if(me._restartFailedWorkers){ trace.log("worker {0} died. Signal {1}. Restarting ...", worker.process.pid, signal); cluster.fork(); } else { trace.log("worker {0} died.", worker.process.pid); } }); } else { // Workers can share any TCP connection // In this case it is an HTTP server this.listen(this._serverPort, this._bindAddr); } }, listen: function (port, host) { this._host = host; this._port = port; this._createServer(this.options, lang.hitch(this, "_handler")).listen(port, host, lang.hitch(this, "_listening")); }, handle: function (module) { if (module instanceof Function) this._modules.push(module); else if (module.invoke instanceof Function) this._modules.push(function (req, next) { return module.invoke(req, next); }); else throw "module shoud be a function or should have an invoke method"; }, _createServer: function () { throw "NOT IMPLEMENTED"; }, _createRequest: function (container, req, res) { return container.configure(this._requestConfig) .then(function () { let httpRequest = container.getService("httpRequest"); httpRequest.init(req, res); return httpRequest; }); }, //handler function to pass to the approperate server (like node.http) _handler: function (req, res) { let i = 0; let me = this; let requestContainer = this._container.createChildContainer(); when(this._createRequest(requestContainer, req, res), function (httpRequest) { let next = function () { if (i < me._modules.length) { let module = me._modules[i]; i++; try { return when(module(httpRequest, next)); } catch (err) { let d = new Deferred(); d.reject(err); return d; } } }; when(next(), function (result) { if (!result) throw "no response is provided, this is a serious bug"; if (result instanceof BaseResponse) { result.send(res); } else { let httpResp = new HttpResponse(); httpResp.content = "BUG: " + result.toString(); httpResp.send(res); } requestContainer.dispose(); }).then(null, function (err) { if (res.finished) { trace.log("caught exception after the response is sent: {0}", err); return; } let httpResp; if (err instanceof HttpException) httpResp = new HttpResponse(err.message, { statusCode: err.code, headers: err.headers }); else httpResp = new HttpResponse(err.toString(), { statusCode: 500 }); httpResp.send(res); requestContainer.dispose(); }); }); }, _listening: function () { trace.log("Listening {0}:{1}", this._host, this._port); }, }); });