Rewrite AppDomain to handle uncaughts & promises

This commit is contained in:
斟酌 鵬兄 2022-04-06 13:51:04 +09:00
parent 7dedaf8d77
commit 5674f3936c
9 changed files with 137 additions and 91 deletions

View File

@ -133,7 +133,19 @@ class Dragonfly
else
{
// Send to master to prevent multiple process competing to write into the handler
this.logHandler = { write: function( e ) { process.send({ cmd: "dragonLog", data: e }); } };
this.logHandler = {
write: function( e ) {
process.send(
{ cmd: "dragonLog", data: e }
, ( er ) => {
if( er )
{
console.log( er.code, e );
}
}
);
}
};
}
var tag = cluster.isMaster ? "M" : "S";

View File

@ -1,70 +1,88 @@
var cl = global.botanLoader;
var Dragonfly = global.Dragonfly;
"use strict";
var domain = require('domain');
const cluster = require( "cluster" );
const cl = global.botanLoader;
const Dragonfly = global.Dragonfly;
var FatalError = cl.load( "botanss.errors.FatalError" );
const domain = require('domain');
const http = require( "http" );
// Message is hardcoded to prevent further exceptions occured
// This function must be bug-free
function server500( response, e )
class Serving
{
response.statusCode = 500;
response.setHeader( 'Content-Type', 'text/plain' );
response.end( e.message || e );
}
constructor( server, handler, req, res )
{
this.server = server;
this.handler = handler;
this.req = req;
this.res = res;
function serverHandle( server, request, response, rHandle )
{
var d = domain.create();
var _self = this;
d.addListener( 'error', function( e ) {
Dragonfly.Error( e.stack );
var _rejection = ( e, p ) => _self.tryExitGracefully( e );
var _uncaught = e => _self.tryExitGracefully( e );
process
.once( "unhandledRejection", _rejection )
.once( "uncaughtException", _uncaught )
;
res._rejection = _rejection;
res._uncaught = _uncaught;
var d = domain.create();
d.addListener( 'error', ( e ) => _self.tryExitGracefully( e ) );
d.add( req );
d.add( res );
d.run( () => _self.handler( req, res ) );
}
tryExitGracefully( e )
{
Dragonfly.Error( e.stack || e );
try
{
var killtimer = setTimeout( function()
{
process.exit(1);
}, 3000);
var killtimer = setTimeout( () => process.exit(1), 3000 );
killtimer.unref();
server.close();
this.server.close();
global.X_SERVER_CLUSTER.worker.destroy();
server500( response, e );
// Message is hardcoded to prevent further exceptions occured
// This function must be bug-free
this.res.statusCode = 500;
this.res.setHeader( 'Content-Type', 'text/plain' );
this.res.end( e.message || e );
}
catch( ex )
{
Dragonfly.Error( ex.stack );
process.exit();
}
});
d.add( request );
d.add( response );
d.run( function() {
rHandle( request, response );
});
finally
{
cluster.worker.disconnect();
}
}
}
// Construncor
function AppDomain( handler, port, cluster )
module.exports = function( handler, port )
{
var http = require( "http" );
var server = http.createServer(
function(req, res) {
if( cluster.isMaster )
{
Dragonfly.Debug( "Not Listening on master" );
return;
}
var server = null;
server = http.createServer(
function( req, res )
{
res._hrtime = process.hrtime.bigint();
serverHandle( server, req, res, handler );
new Serving( server, handler, req, res );
}
);
server.listen( port );
Dragonfly.Info( "Listening on: " + port, Dragonfly.Visibility.VISIBLE );
}
module.exports = AppDomain;
return server;
};

View File

@ -30,6 +30,16 @@ class CResponse
this.raw.writeHead( this.statusCode, this.headers );
this.raw.end( this.content );
if( this.raw._rejection )
{
process.removeListener( "unhandledRejection", this.raw._rejection );
}
if( this.raw._uncaught )
{
process.removeListener( "uncaughtException", this.raw._uncaught );
}
}
}

View File

@ -57,7 +57,7 @@ class HttpRequest extends EventEmitter
{
this.Method = "POST";
this.Headers[ "Content-Type" ] = "application/x-www-form-urlencoded";
this.RawPostData = Data == undefined ? new Buffer([]) : new Buffer( Data );
this.RawPostData = Data == undefined ? Buffer.alloc( 0 ) : Buffer.from( Data );
this.Headers[ "Content-Length" ] = this.RawPostData.length;
}
@ -88,7 +88,7 @@ class HttpRequest extends EventEmitter
OnResponseReceived( Response )
{
var ResponseData = new Buffer( 0 );
var ResponseData = Buffer.alloc( 0 );
Response.addListener( "data",
Data => ResponseData = Buffer.concat([ ResponseData, Data ])

View File

@ -75,7 +75,7 @@ class PostFrame extends EventEmitter
{
if( !( this.result instanceof Buffer ) )
{
this.result = new Buffer( this.result + "" );
this.result = Buffer.from( this.result + "" );
}
this.HTTP.response.headers["Content-Length"] = this.result.length;

View File

@ -129,7 +129,7 @@ class WebFrame
{
if( !( this.result instanceof Buffer ) )
{
this.result = new Buffer( this.result + "" );
this.result = Buffer.from( this.result + "" );
}
var acceptEncoding = this.HTTP.request.headers[ "accept-encoding" ] || "";

View File

@ -11,7 +11,7 @@ class HttpRequestComplete extends EventArgs
if( ResponseData === undefined )
{
this.statusCode = -1;
this.Data = new Buffer( 0 );
this.Data = Buffer.alloc( 0 );
}
else
{

View File

@ -1,49 +1,55 @@
"use strict";
// The Package Loader
var fs = require( "fs" );
var rootNS = {
botanss: "./"
};
const fs = require( "fs" );
const cluster = require( "cluster" );
var Package = function() { };
Package.prototype.rootNS = function( name, path )
class Package
{
if( rootNS[ name ] ) return;
rootNS[ name ] = fs.realpathSync( path ) + "/";
};
var _reload = function( e, filename )
{
if( this._lock == global.X_SERVER_CLUSTER.worker.id )
return;
this._lock = global.X_SERVER_CLUSTER.worker.id;
setTimeout( () =>
constructor()
{
global.Dragonfly.Info( `Change detected: ${this.src}, reloading` );
global.X_SERVER_CLUSTER.worker.destroy();
} , 200 );
};
Package.prototype.load = function( _class )
{
var fSep = _class.indexOf( "." );
var nsdomain = _class.substr( 0, fSep );
_class = _class.substr( fSep + 1 ).replace( /\./g, "/" );
var file = rootNS[ nsdomain ] + _class;
if( global.debug && global.X_SERVER_CLUSTER && global.X_SERVER_CLUSTER.worker )
{
var src = require.resolve( file );
if(!( src in require.cache ))
{
fs.watch( src, _reload.bind({ "src": src }) );
}
this._rootNS = { botanss: "./" };
}
return require( file );
};
rootNS( name, path )
{
if( this._rootNS[ name ] ) return;
this._rootNS[ name ] = fs.realpathSync( path ) + "/";
}
load( _class )
{
var fSep = _class.indexOf( "." );
var nsdomain = _class.substr( 0, fSep );
_class = _class.substr( fSep + 1 ).replace( /\./g, "/" );
var file = this._rootNS[ nsdomain ] + _class;
if( global.debug && cluster.worker )
{
var src = require.resolve( file );
if(!( src in require.cache ))
{
fs.watch( src, this._reload.bind({ "src": src }) );
}
}
return require( file );
}
_reload( e, filename )
{
if( this._lock == cluster.worker.id )
return;
this._lock = cluster.worker.id;
setTimeout( () =>
{
global.Dragonfly.Info( `Change detected: ${this.src}, reloading` );
cluster.worker.disconnect();
setTimeout( () => process.exit(0), 1000 ).unref();
} , 200 );
}
}
global.botanLoader = new Package();

View File

@ -97,7 +97,7 @@ class ConditionalStream extends String
this.__error = "Received data is too large to process";
}
return new Buffer( this.hexData, "hex" ).toString( enc );
return Buffer.from( this.hexData, "hex" ).toString( enc );
}
resultStream()