2024-08-23 19:58:15 +02:00
const http = require ( "http" ) ;
const fs = require ( "fs" ) ;
2024-08-25 18:57:46 +02:00
const os = require ( "os" ) ;
2024-08-26 07:00:08 +02:00
const dns = require ( "dns" ) ;
2024-08-25 20:18:35 +02:00
const readline = require ( "readline" ) ;
2024-08-25 18:57:46 +02:00
const logo = require ( "./res/logo.js" ) ;
2024-08-24 19:41:13 +02:00
const generateServerString = require ( "./utils/generateServerString.js" ) ;
2024-08-24 19:47:48 +02:00
const deleteFolderRecursive = require ( "./utils/deleteFolderRecursive.js" ) ;
2024-08-24 19:41:13 +02:00
const svrjsInfo = require ( "../svrjs.json" ) ;
2024-08-26 10:14:21 +02:00
const { name , version } = svrjsInfo ;
2024-08-23 19:58:15 +02:00
2024-08-23 21:58:26 +02:00
let inspector = undefined ;
try {
inspector = require ( "inspector" ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-23 21:58:26 +02:00
} catch ( err ) {
// Don't use inspector
}
2024-08-25 15:40:53 +02:00
let tar = { } ;
try {
tar = require ( "tar" ) ;
} catch ( err ) {
tar = {
_errored : err ,
} ;
}
2024-08-25 17:15:00 +02:00
let http2 = { } ;
try {
http2 = require ( "http2" ) ;
if ( process . isBun ) {
try {
http2 . Http2ServerRequest ( ) ;
} catch ( err ) {
if (
err . name == "NotImplementedError" ||
err . code == "ERR_NOT_IMPLEMENTED"
)
throw err ;
}
}
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 17:15:00 +02:00
} catch ( err ) {
http2 . _ _disabled _ _ = null ;
2024-08-27 10:54:08 +02:00
http2 . createServer = ( ) => {
2024-08-25 17:15:00 +02:00
throw new Error ( "HTTP/2 support is not present" ) ;
} ;
2024-08-27 10:54:08 +02:00
http2 . createSecureServer = ( ) => {
2024-08-25 17:15:00 +02:00
throw new Error ( "HTTP/2 support is not present" ) ;
} ;
2024-08-27 10:54:08 +02:00
http2 . connect = ( ) => {
2024-08-25 17:15:00 +02:00
throw new Error ( "HTTP/2 support is not present" ) ;
} ;
2024-08-27 10:54:08 +02:00
http2 . get = ( ) => {
2024-08-25 17:15:00 +02:00
throw new Error ( "HTTP/2 support is not present" ) ;
} ;
}
let crypto = {
_ _disabled _ _ : null ,
} ;
let https = {
2024-08-27 10:54:08 +02:00
createServer : ( ) => {
2024-08-25 17:15:00 +02:00
throw new Error ( "Crypto support is not present" ) ;
} ,
2024-08-27 10:54:08 +02:00
connect : ( ) => {
2024-08-25 17:15:00 +02:00
throw new Error ( "Crypto support is not present" ) ;
} ,
2024-08-27 10:54:08 +02:00
get : ( ) => {
2024-08-25 17:15:00 +02:00
throw new Error ( "Crypto support is not present" ) ;
} ,
} ;
try {
crypto = require ( "crypto" ) ;
https = require ( "https" ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 17:15:00 +02:00
} catch ( err ) {
2024-08-27 10:54:08 +02:00
http2 . createSecureServer = ( ) => {
2024-08-25 17:15:00 +02:00
throw new Error ( "Crypto support is not present" ) ;
} ;
}
2024-08-25 17:59:52 +02:00
let ocsp = { } ;
let ocspCache = { } ;
try {
ocsp = require ( "ocsp" ) ;
ocspCache = new ocsp . Cache ( ) ;
} catch ( err ) {
ocsp = {
2024-08-25 18:02:18 +02:00
_errored : err ,
2024-08-25 17:59:52 +02:00
} ;
}
2024-08-24 19:29:12 +02:00
process . dirname = _ _dirname ;
2024-08-25 07:13:41 +02:00
process . filename = _ _filename ;
2024-08-24 19:29:12 +02:00
2024-08-25 15:50:56 +02:00
let hexstrbase64 = undefined ;
try {
hexstrbase64 = require ( process . dirname + "/hexstrbase64/index.js" ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 15:50:56 +02:00
} catch ( err ) {
// Don't use hexstrbase64
}
2024-08-25 19:28:20 +02:00
process . singleThreaded = false ;
2024-08-24 19:41:13 +02:00
2024-08-25 09:44:53 +02:00
process . err4xxcounter = 0 ;
process . err5xxcounter = 0 ;
process . reqcounter = 0 ;
process . malformedcounter = 0 ;
2024-08-25 17:57:23 +02:00
process . messageEventListeners = [ ] ;
2024-08-24 19:41:13 +02:00
if ( process . versions ) process . versions . svrjs = version ; // Inject SVR.JS into process.versions
2024-08-25 16:23:16 +02:00
let exiting = false ;
2024-08-24 19:41:13 +02:00
let forceSecure = false ;
let disableMods = false ;
2024-08-24 19:47:48 +02:00
// Handle command line arguments
2024-08-24 19:41:13 +02:00
const args = process . argv ;
2024-08-25 11:32:03 +02:00
for (
let i =
process . argv [ 0 ] . indexOf ( "node" ) > - 1 || process . argv [ 0 ] . indexOf ( "bun" ) > - 1
? 2
: 1 ;
i < args . length ;
i ++
) {
if (
args [ i ] == "-h" ||
args [ i ] == "--help" ||
args [ i ] == "-?" ||
args [ i ] == "/h" ||
args [ i ] == "/?"
) {
2024-08-25 13:09:58 +02:00
console . log ( name + " usage:" ) ;
2024-08-25 11:32:03 +02:00
console . log (
"node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]" ,
) ;
2024-08-24 19:41:13 +02:00
console . log ( "-h -? /h /? --help -- Displays help" ) ;
2024-08-25 13:09:58 +02:00
console . log ( "--clean -- Cleans up files created by " + name ) ;
2024-08-25 11:32:03 +02:00
console . log (
2024-08-25 13:10:28 +02:00
"--reset -- Resets " +
name +
" to default settings (WARNING: DANGEROUS)" ,
2024-08-25 11:32:03 +02:00
) ;
2024-08-24 19:41:13 +02:00
console . log ( "--secure -- Runs HTTPS server" ) ;
console . log ( "--disable-mods -- Disables mods (safe mode)" ) ;
console . log ( "--single-threaded -- Run single-threaded" ) ;
console . log ( "-v --version -- Display server version" ) ;
process . exit ( 0 ) ;
} else if ( args [ i ] == "--secure" ) {
forceSecure = true ;
} else if ( args [ i ] == "-v" || args [ i ] == "--version" ) {
console . log ( generateServerString ( true ) ) ;
process . exit ( 0 ) ;
} else if ( args [ i ] == "--clean" ) {
console . log ( "Removing logs..." ) ;
deleteFolderRecursive ( process . dirname + "/log" ) ;
fs . mkdirSync ( process . dirname + "/log" ) ;
console . log ( "Removing temp folder..." ) ;
deleteFolderRecursive ( process . dirname + "/temp" ) ;
fs . mkdirSync ( process . dirname + "/temp" ) ;
console . log ( "Done!" ) ;
process . exit ( 0 ) ;
} else if ( args [ i ] == "--reset" ) {
console . log ( "Removing logs..." ) ;
deleteFolderRecursive ( process . dirname + "/log" ) ;
fs . mkdirSync ( process . dirname + "/log" ) ;
console . log ( "Removing temp folder..." ) ;
deleteFolderRecursive ( process . dirname + "/temp" ) ;
fs . mkdirSync ( process . dirname + "/temp" ) ;
console . log ( "Removing configuration file..." ) ;
2024-08-24 19:49:36 +02:00
fs . unlinkSync ( process . dirname + "/config.json" ) ;
2024-08-24 19:41:13 +02:00
console . log ( "Done!" ) ;
process . exit ( 0 ) ;
} else if ( args [ i ] == "--disable-mods" ) {
disableMods = true ;
} else if ( args [ i ] == "--single-threaded" ) {
2024-08-25 20:38:35 +02:00
process . singleThreaded = true ;
2024-08-24 19:41:13 +02:00
} else {
console . log ( "Unrecognized argument: " + args [ i ] ) ;
2024-08-25 13:09:58 +02:00
console . log ( name + " usage:" ) ;
2024-08-25 11:32:03 +02:00
console . log (
"node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]" ,
) ;
2024-08-24 19:41:13 +02:00
console . log ( "-h -? /h /? --help -- Displays help" ) ;
2024-08-25 13:09:58 +02:00
console . log ( "--clean -- Cleans up files created by " + name ) ;
2024-08-25 11:32:03 +02:00
console . log (
2024-08-25 13:10:28 +02:00
"--reset -- Resets " +
name +
" to default settings (WARNING: DANGEROUS)" ,
2024-08-25 11:32:03 +02:00
) ;
2024-08-24 19:41:13 +02:00
console . log ( "--secure -- Runs HTTPS server" ) ;
console . log ( "--disable-mods -- Disables mods (safe mode)" ) ;
console . log ( "--single-threaded -- Run single-threaded" ) ;
console . log ( "-v --version -- Display server version" ) ;
process . exit ( 1 ) ;
}
}
2024-08-23 19:58:15 +02:00
// Create log, mods and temp directories, if they don't exist.
2024-08-25 11:32:03 +02:00
if ( ! fs . existsSync ( process . dirname + "/log" ) )
fs . mkdirSync ( process . dirname + "/log" ) ;
if ( ! fs . existsSync ( process . dirname + "/mods" ) )
fs . mkdirSync ( process . dirname + "/mods" ) ;
if ( ! fs . existsSync ( process . dirname + "/temp" ) )
fs . mkdirSync ( process . dirname + "/temp" ) ;
2024-08-23 19:58:15 +02:00
2024-08-24 16:54:28 +02:00
const cluster = require ( "./utils/clusterBunShim.js" ) ; // Cluster module with shim for Bun
2024-08-25 15:40:53 +02:00
const legacyModWrapper = require ( "./utils/legacyModWrapper.js" ) ;
2024-08-25 16:23:16 +02:00
const generateErrorStack = require ( "./utils/generateErrorStack.js" ) ;
2024-08-25 19:17:19 +02:00
const {
calculateNetworkIPv4FromCidr ,
calculateBroadcastIPv4FromCidr ,
} = require ( "./utils/ipSubnetUtils.js" ) ;
2024-08-26 10:14:21 +02:00
const sendStatistics = require ( "./utils/sendStatistics.js" ) ;
2024-08-24 16:54:28 +02:00
2024-08-24 16:55:44 +02:00
process . serverConfig = { } ;
2024-08-25 09:12:39 +02:00
let configJSONRErr = undefined ;
let configJSONPErr = undefined ;
2024-08-26 07:05:04 +02:00
if ( fs . existsSync ( process . dirname + "/config.json" ) ) {
2024-08-25 09:12:39 +02:00
let configJSONf = "" ;
try {
2024-08-26 07:05:04 +02:00
configJSONf = fs . readFileSync ( process . dirname + "/config.json" ) ; // Read JSON File
2024-08-25 09:12:39 +02:00
try {
process . serverConfig = JSON . parse ( configJSONf ) ; // Parse JSON
} catch ( err2 ) {
configJSONPErr = err2 ;
}
} catch ( err ) {
2024-08-25 11:57:45 +02:00
configJSONRErr = err ;
2024-08-25 09:12:39 +02:00
}
}
2024-08-24 16:55:44 +02:00
if ( process . serverConfig . users === undefined ) process . serverConfig . users = [ ] ;
2024-08-25 11:32:03 +02:00
if ( process . serverConfig . secure === undefined )
process . serverConfig . secure = false ;
2024-08-24 19:41:13 +02:00
if ( forceSecure ) process . serverConfig . secure = true ;
2024-08-24 16:55:44 +02:00
if ( process . serverConfig . secure ) {
2024-08-25 11:32:03 +02:00
if ( process . serverConfig . key === undefined )
process . serverConfig . key = "cert/key.key" ;
if ( process . serverConfig . cert === undefined )
process . serverConfig . cert = "cert/cert.crt" ;
if ( process . serverConfig . sport === undefined )
process . serverConfig . sport = 443 ;
if ( process . serverConfig . spubport === undefined )
process . serverConfig . spubport = 443 ;
2024-08-24 16:55:44 +02:00
if ( process . serverConfig . sni === undefined ) process . serverConfig . sni = { } ;
2024-08-25 11:32:03 +02:00
if ( process . serverConfig . enableOCSPStapling === undefined )
process . serverConfig . enableOCSPStapling = false ;
2024-08-24 16:55:44 +02:00
}
if ( process . serverConfig . port === undefined ) process . serverConfig . port = 80 ;
2024-08-25 11:32:03 +02:00
if ( process . serverConfig . pubport === undefined )
process . serverConfig . pubport = 80 ;
if (
process . serverConfig . domain === undefined &&
process . serverConfig . domian !== undefined
)
process . serverConfig . domain = process . serverConfig . domian ;
2024-08-24 16:55:44 +02:00
delete process . serverConfig . domian ;
2024-08-25 11:32:03 +02:00
if ( process . serverConfig . page404 === undefined )
process . serverConfig . page404 = "404.html" ;
2024-08-25 08:32:49 +02:00
process . serverConfig . timestamp = new Date ( ) . getTime ( ) ;
2024-08-25 11:32:03 +02:00
if ( process . serverConfig . blacklist === undefined )
process . serverConfig . blacklist = [ ] ;
if ( process . serverConfig . nonStandardCodes === undefined )
process . serverConfig . nonStandardCodes = [ ] ;
if ( process . serverConfig . enableCompression === undefined )
process . serverConfig . enableCompression = true ;
if ( process . serverConfig . customHeaders === undefined )
process . serverConfig . customHeaders = { } ;
if ( process . serverConfig . enableHTTP2 === undefined )
process . serverConfig . enableHTTP2 = false ;
if ( process . serverConfig . enableLogging === undefined )
process . serverConfig . enableLogging = true ;
if ( process . serverConfig . enableDirectoryListing === undefined )
process . serverConfig . enableDirectoryListing = true ;
if ( process . serverConfig . enableDirectoryListingWithDefaultHead === undefined )
process . serverConfig . enableDirectoryListingWithDefaultHead = false ;
if ( process . serverConfig . serverAdministratorEmail === undefined )
process . serverConfig . serverAdministratorEmail = "[no contact information]" ;
if ( process . serverConfig . stackHidden === undefined )
process . serverConfig . stackHidden = false ;
if ( process . serverConfig . enableRemoteLogBrowsing === undefined )
process . serverConfig . enableRemoteLogBrowsing = false ;
if ( process . serverConfig . exposeServerVersion === undefined )
process . serverConfig . exposeServerVersion = true ;
if ( process . serverConfig . disableServerSideScriptExpose === undefined )
process . serverConfig . disableServerSideScriptExpose = true ;
if ( process . serverConfig . allowStatus === undefined )
process . serverConfig . allowStatus = true ;
if ( process . serverConfig . rewriteMap === undefined )
process . serverConfig . rewriteMap = [ ] ;
if ( process . serverConfig . dontCompress === undefined )
process . serverConfig . dontCompress = [
"/.*\\.ipxe$/" ,
"/.*\\.(?:jpe?g|png|bmp|tiff|jfif|gif|webp)$/" ,
"/.*\\.(?:[id]mg|iso|flp)$/" ,
"/.*\\.(?:zip|rar|bz2|[gb7x]z|lzma|tar)$/" ,
"/.*\\.(?:mp[34]|mov|wm[av]|avi|webm|og[gv]|mk[va])$/" ,
] ;
if ( process . serverConfig . enableIPSpoofing === undefined )
process . serverConfig . enableIPSpoofing = false ;
if ( process . serverConfig . disableNonEncryptedServer === undefined )
process . serverConfig . disableNonEncryptedServer = false ;
if ( process . serverConfig . disableToHTTPSRedirect === undefined )
process . serverConfig . disableToHTTPSRedirect = false ;
if ( process . serverConfig . enableETag === undefined )
process . serverConfig . enableETag = true ;
if ( process . serverConfig . disableUnusedWorkerTermination === undefined )
process . serverConfig . disableUnusedWorkerTermination = false ;
if ( process . serverConfig . rewriteDirtyURLs === undefined )
process . serverConfig . rewriteDirtyURLs = false ;
if ( process . serverConfig . errorPages === undefined )
process . serverConfig . errorPages = [ ] ;
if ( process . serverConfig . useWebRootServerSideScript === undefined )
process . serverConfig . useWebRootServerSideScript = true ;
if ( process . serverConfig . exposeModsInErrorPages === undefined )
process . serverConfig . exposeModsInErrorPages = true ;
if ( process . serverConfig . disableTrailingSlashRedirects === undefined )
process . serverConfig . disableTrailingSlashRedirects = false ;
if ( process . serverConfig . environmentVariables === undefined )
process . serverConfig . environmentVariables = { } ;
if ( process . serverConfig . customHeadersVHost === undefined )
process . serverConfig . customHeadersVHost = [ ] ;
if ( process . serverConfig . enableDirectoryListingVHost === undefined )
process . serverConfig . enableDirectoryListingVHost = [ ] ;
if ( process . serverConfig . wwwrootPostfixesVHost === undefined )
process . serverConfig . wwwrootPostfixesVHost = [ ] ;
if ( process . serverConfig . wwwrootPostfixPrefixesVHost === undefined )
process . serverConfig . wwwrootPostfixPrefixesVHost = [ ] ;
if ( process . serverConfig . allowDoubleSlashes === undefined )
process . serverConfig . allowDoubleSlashes = false ;
if ( process . serverConfig . allowPostfixDoubleSlashes === undefined )
process . serverConfig . allowPostfixDoubleSlashes = false ;
if ( process . serverConfig . optOutOfStatisticsServer === undefined )
process . serverConfig . optOutOfStatisticsServer = false ;
2024-08-24 16:55:44 +02:00
2024-08-25 17:15:40 +02:00
// Compatiblity for very old SVR.JS mods
process . serverConfig . version = version ;
process . serverConfig . productName = name ;
2024-08-24 16:55:44 +02:00
2024-08-25 18:57:46 +02:00
let listenAddress = undefined ;
let sListenAddress = undefined ;
if ( typeof process . serverConfig . port === "string" ) {
if ( process . serverConfig . port . match ( /^[0-9]+$/ ) ) {
process . serverConfig . port = parseInt ( process . serverConfig . port ) ;
} else {
2024-08-25 19:17:19 +02:00
const portLMatch = process . serverConfig . port . match (
2024-08-27 11:28:24 +02:00
/^(\[[^ \]@/\\]+\]|[^ \][:@/\\]+):([0-9]+)$/ ,
2024-08-25 18:59:16 +02:00
) ;
2024-08-25 18:57:46 +02:00
if ( portLMatch ) {
2024-08-25 18:59:16 +02:00
listenAddress = portLMatch [ 1 ]
. replace ( /^\[|\]$/g , "" )
. replace ( /^::ffff:/i , "" ) ;
2024-08-25 18:57:46 +02:00
process . serverConfig . port = parseInt ( portLMatch [ 2 ] ) ;
}
}
}
if ( typeof process . serverConfig . sport === "string" ) {
if ( process . serverConfig . sport . match ( /^[0-9]+$/ ) ) {
2024-08-25 19:17:19 +02:00
process . serverConfig . sport = parseInt ( process . serverConfig . sport ) ;
2024-08-25 18:57:46 +02:00
} else {
2024-08-25 18:59:16 +02:00
const sportLMatch = process . serverConfig . sport . match (
2024-08-27 11:28:24 +02:00
/^(\[[^ \]@/\\]+\]|[^ \][:@/\\]+):([0-9]+)$/ ,
2024-08-25 18:59:16 +02:00
) ;
2024-08-25 18:57:46 +02:00
if ( sportLMatch ) {
2024-08-25 18:59:16 +02:00
sListenAddress = sportLMatch [ 1 ]
. replace ( /^\[|\]$/g , "" )
. replace ( /^::ffff:/i , "" ) ;
2024-08-25 18:57:46 +02:00
process . serverConfig . sport = parseInt ( sportLMatch [ 2 ] ) ;
}
}
}
2024-08-24 16:58:27 +02:00
const serverconsole = require ( "./utils/serverconsole.js" ) ;
2024-08-23 19:58:15 +02:00
2024-08-25 19:35:58 +02:00
function addListenersToWorker ( worker ) {
2024-08-25 19:46:17 +02:00
process . messageEventListeners . forEach ( ( messageEventListener ) =>
worker . on ( "message" , messageEventListener ( worker , serverconsole ) ) ,
) ;
2024-08-25 19:35:58 +02:00
}
2024-08-23 21:58:26 +02:00
let inspectorURL = undefined ;
try {
if ( inspector ) {
inspectorURL = inspector . url ( ) ;
}
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-23 21:58:26 +02:00
} catch ( err ) {
// Failed to get inspector URL
}
if ( ! process . stdout . isTTY && ! inspectorURL ) {
// When stdout is not a terminal and not attached to an Node.JS inspector, disable it to improve performance of SVR.JS
2024-08-27 10:54:08 +02:00
console . log = ( ) => { } ;
process . stdout . write = ( ) => { } ;
process . stdout . _write = ( ) => { } ;
process . stdout . _writev = ( ) => { } ;
2024-08-23 21:58:26 +02:00
}
2024-08-25 11:32:03 +02:00
let wwwrootError = null ;
2024-08-23 21:33:26 +02:00
try {
2024-08-25 11:32:03 +02:00
if ( cluster . isPrimary || cluster . isPrimary === undefined )
process . chdir (
process . serverConfig . wwwroot != undefined
? process . serverConfig . wwwroot
: process . dirname ,
) ;
2024-08-23 21:33:26 +02:00
} catch ( err ) {
wwwrootError = err ;
}
2024-08-23 20:43:09 +02:00
2024-08-25 19:17:19 +02:00
// IP and network inteface-related
let ifaces = { } ;
let ifaceEx = null ;
try {
ifaces = os . networkInterfaces ( ) ;
} catch ( err ) {
ifaceEx = err ;
}
var ips = [ ] ;
const brdIPs = [ "255.255.255.255" , "127.255.255.255" , "0.255.255.255" ] ;
const netIPs = [ "127.0.0.0" ] ;
Object . keys ( ifaces ) . forEach ( ( ifname ) => {
let alias = 0 ;
ifaces [ ifname ] . forEach ( ( iface ) => {
if ( iface . family !== "IPv4" || iface . internal !== false ) {
return ;
}
if ( alias >= 1 ) {
ips . push ( ifname + ":" + alias , iface . address ) ;
} else {
ips . push ( ifname , iface . address ) ;
}
brdIPs . push ( calculateBroadcastIPv4FromCidr ( iface . cidr ) ) ;
netIPs . push ( calculateNetworkIPv4FromCidr ( iface . cidr ) ) ;
alias ++ ;
} ) ;
} ) ;
if ( ips . length == 0 ) {
Object . keys ( ifaces ) . forEach ( ( ifname ) => {
let alias = 0 ;
ifaces [ ifname ] . forEach ( ( iface ) => {
if ( iface . family !== "IPv6" || iface . internal !== false ) {
return ;
}
if ( alias >= 1 ) {
ips . push ( ifname + ":" + alias , iface . address ) ;
} else {
ips . push ( ifname , iface . address ) ;
}
alias ++ ;
} ) ;
} ) ;
}
// Server IP address
2024-08-25 19:17:54 +02:00
var host = ips [ ips . length - 1 ] ;
2024-08-25 19:17:19 +02:00
if ( ! host ) host = "[offline]" ;
2024-08-26 07:00:08 +02:00
// Public IP address-related
let ipRequestCompleted = false ;
let ipRequestGotError = false ;
let domain = process . serverConfig . domain ? process . serverConfig . domain : "" ;
let pubip = "" ;
function doIpRequest ( isHTTPS , options ) {
const ipRequest = ( isHTTPS ? https : http ) . get ( options , ( res ) => {
ipRequest . removeAllListeners ( "timeout" ) ;
2024-08-27 10:54:08 +02:00
res . on ( "data" , ( d ) => {
2024-08-26 07:00:08 +02:00
if ( res . statusCode != 200 ) {
ipRequestCompleted = true ;
process . emit ( "ipRequestCompleted" ) ;
return ;
}
pubip = d . toString ( ) ;
if ( domain ) {
ipRequestCompleted = true ;
process . emit ( "ipRequestCompleted" ) ;
} else {
let callbackDone = false ;
2024-08-27 10:54:08 +02:00
const dnsTimeout = setTimeout ( ( ) => {
2024-08-26 07:00:08 +02:00
callbackDone = true ;
ipRequestCompleted = true ;
process . emit ( "ipRequestCompleted" ) ;
} , 3000 ) ;
try {
2024-08-27 10:54:08 +02:00
dns . reverse ( pubip , ( err , hostnames ) => {
2024-08-26 07:00:08 +02:00
if ( callbackDone ) return ;
clearTimeout ( dnsTimeout ) ;
if ( ! err && hostnames . length > 0 ) domain = hostnames [ 0 ] ;
ipRequestCompleted = true ;
process . emit ( "ipRequestCompleted" ) ;
} ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-26 07:00:08 +02:00
} catch ( err ) {
clearTimeout ( dnsTimeout ) ;
callbackDone = true ;
ipRequestCompleted = true ;
process . emit ( "ipRequestCompleted" ) ;
}
}
} ) ;
} ) ;
2024-08-27 10:54:08 +02:00
ipRequest . on ( "error" , ( ) => {
2024-08-26 07:00:08 +02:00
if ( crypto . _ _disabled _ _ || ipRequestGotError ) {
ipRequestCompleted = true ;
process . emit ( "ipRequestCompleted" ) ;
} else {
ipRequestGotError = true ;
}
} ) ;
2024-08-27 10:54:08 +02:00
ipRequest . on ( "timeout" , ( ) => {
2024-08-26 07:00:08 +02:00
if ( crypto . _ _disabled _ _ || ipRequestGotError ) {
ipRequestCompleted = true ;
process . emit ( "ipRequestCompleted" ) ;
} else {
ipRequestGotError = true ;
}
} ) ;
return ipRequest ;
}
if ( host != "[offline]" || ifaceEx ) {
2024-08-26 07:58:37 +02:00
doIpRequest ( crypto . _ _disabled _ _ === undefined , {
2024-08-26 07:00:08 +02:00
host : "api64.ipify.org" ,
2024-08-26 07:06:02 +02:00
port : crypto . _ _disabled _ _ !== undefined ? 80 : 443 ,
2024-08-26 07:00:08 +02:00
path : "/" ,
headers : {
2024-08-26 07:06:02 +02:00
"User-Agent" : generateServerString ( true ) ,
2024-08-26 07:00:08 +02:00
} ,
2024-08-26 07:06:02 +02:00
timeout : 5000 ,
2024-08-26 07:00:08 +02:00
} ) ;
if ( crypto . _ _disabled _ _ === undefined ) {
2024-08-26 07:58:37 +02:00
doIpRequest ( true , {
2024-08-26 07:00:08 +02:00
host : "api.seeip.org" ,
port : 443 ,
path : "/" ,
headers : {
2024-08-26 07:06:02 +02:00
"User-Agent" : generateServerString ( true ) ,
2024-08-26 07:00:08 +02:00
} ,
2024-08-26 07:06:02 +02:00
timeout : 5000 ,
2024-08-26 07:00:08 +02:00
} ) ;
}
} else {
ipRequestCompleted = true ;
}
function ipStatusCallback ( callback ) {
if ( ipRequestCompleted ) {
callback ( ) ;
} else {
process . once ( "ipRequestCompleted" , callback ) ;
}
}
2024-08-25 19:17:19 +02:00
2024-08-25 17:11:31 +02:00
// SSL-related
let key = "" ;
let cert = "" ;
if ( process . serverConfig . secure ) {
if ( ! process . serverConfig . key ) process . serverConfig . key = "cert/key.key" ;
if ( ! process . serverConfig . cert ) process . serverConfig . cert = "cert/cert.crt" ;
} else {
key = "SSL DISABLED" ;
cert = "SSL DISABLED" ;
process . serverConfig . cert = "SSL DISABLED" ;
process . serverConfig . key = "SSL DISABLED" ;
}
let certificateError = null ;
let sniReDos = false ;
let sniCredentials = [ ] ;
// Load certificates
if ( process . serverConfig . secure ) {
try {
key = fs
. readFileSync (
process . serverConfig . key [ 0 ] != "/" &&
! process . serverConfig . key . match ( /^[A-Z0-9]:\\/ )
? process . dirname + "/" + process . serverConfig . key
: process . serverConfig . key ,
)
. toString ( ) ;
cert = fs
. readFileSync (
process . serverConfig . cert [ 0 ] != "/" &&
! process . serverConfig . cert . match ( /^[A-Z0-9]:\\/ )
? process . dirname + "/" + process . serverConfig . cert
: process . serverConfig . cert ,
)
. toString ( ) ;
const sniNames = Object . keys ( process . serverConfig . sni ) ;
2024-08-27 10:54:08 +02:00
sniNames . forEach ( ( sniName ) => {
2024-08-25 17:11:31 +02:00
if (
typeof sniName === "string" &&
sniName . match ( /\*[^*.:]*\*[^*.:]*(?:\.|:|$)/ )
) {
sniReDos = true ;
}
sniCredentials . push ( {
name : sniName ,
cert : fs
. readFileSync (
process . serverConfig . sni [ sniName ] . cert [ 0 ] != "/" &&
! process . serverConfig . sni [ sniName ] . cert . match ( /^[A-Z0-9]:\\/ )
? process . dirname + "/" + process . serverConfig . sni [ sniName ] . cert
: process . serverConfig . sni [ sniName ] . cert ,
)
. toString ( ) ,
key : fs
. readFileSync (
process . serverConfig . sni [ sniName ] . key [ 0 ] != "/" &&
! process . serverConfig . sni [ sniName ] . key . match ( /^[A-Z0-9]:\\/ )
? process . dirname + "/" + process . serverConfig . sni [ sniName ] . key
: process . serverConfig . sni [ sniName ] . key ,
)
. toString ( ) ,
} ) ;
} ) ;
} catch ( err ) {
certificateError = err ;
}
}
2024-08-25 18:57:46 +02:00
let vnum = 0 ;
try {
vnum = process . config . variables . node _module _version ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 18:57:46 +02:00
} catch ( err ) {
// Version number not retrieved
}
if ( vnum === undefined ) vnum = 0 ;
if ( process . isBun ) vnum = 64 ;
2024-08-25 14:02:45 +02:00
let mods = [ ] ;
const modFiles = fs . readdirSync ( _ _dirname + "/mods" ) . sort ( ) ;
let modInfos = [ ] ;
let modLoadingErrors = [ ] ;
2024-08-25 15:47:12 +02:00
let SSJSError = undefined ;
2024-08-25 14:02:45 +02:00
if ( ! disableMods ) {
// Iterate through the list of mod files
modFiles . forEach ( ( modFileRaw ) => {
// Build the path to the current mod file
const modFile = process . dirname + "/mods/" + modFileRaw ;
// Check if the current mod file is a regular file
if ( fs . statSync ( modFile ) . isFile ( ) ) {
if ( modFile . indexOf ( ".js" ) == modFile . length - 3 ) {
try {
const mod = require ( modFile ) ;
mods . push ( mod ) ;
if ( mod . modInfo ) modInfos . push ( mod . modInfo ) ;
else {
modInfos . push ( {
2024-08-26 19:23:52 +02:00
name : ` Unknown mod ( ${ modFileRaw } ; module.exports.modInfo not set) ` ,
2024-08-25 14:02:45 +02:00
version : "ERROR" ,
} ) ;
}
} catch ( err ) {
modLoadingErrors . push ( {
error : err ,
modName : modFileRaw ,
} ) ;
}
} else {
2024-08-25 15:40:53 +02:00
try {
// Define the modloader folder name
let modloaderFolderName = "modloader" ;
if ( cluster . isPrimary === false ) {
// If not the master process, create a unique modloader folder name for each worker
modloaderFolderName =
".modloader_w" + Math . floor ( Math . random ( ) * 65536 ) ;
}
2024-08-27 09:15:15 +02:00
try {
// Try creating the modloader folder (if not already exists)
try {
fs . mkdirSync ( process . dirname + "/temp/" + modloaderFolderName ) ;
} catch ( err ) {
// If the folder already exists, continue to the next step
if ( err . code != "EEXIST" ) {
// If there was another error, try creating the temp folder and then the modloader folder again
fs . mkdirSync ( process . dirname + "/temp" ) ;
try {
2024-08-27 10:55:24 +02:00
fs . mkdirSync (
process . dirname + "/temp/" + modloaderFolderName ,
) ;
2024-08-27 09:15:15 +02:00
} catch ( err ) {
// If there was another error, throw it
if ( err . code != "EEXIST" ) throw err ;
}
}
}
2024-08-27 10:54:08 +02:00
2024-08-27 10:55:24 +02:00
// Create a subfolder for the current mod within the modloader folder
fs . mkdirSync (
process . dirname +
"/temp/" +
modloaderFolderName +
"/" +
modFileRaw ,
) ;
2024-08-27 09:15:15 +02:00
} catch ( err ) {
// If there was an error creating the folder, ignore it if it's a known error
if ( err . code != "EEXIST" && err . code != "ENOENT" ) throw err ;
// Some other SVR.JS process may have created the files.
}
2024-08-25 15:40:53 +02:00
// Determine if the mod file is a ".tar.gz" file or not
if ( modFile . indexOf ( ".tar.gz" ) == modFile . length - 7 ) {
// If it's a ".tar.gz" file, extract its contents using `tar`
if ( tar . _errored ) throw tar . _errored ;
tar . x ( {
file : modFile ,
sync : true ,
C :
process . dirname +
"/temp/" +
modloaderFolderName +
"/" +
modFileRaw ,
} ) ;
} else {
// If it's not a ".tar.gz" file, throw an error about `svrmodpack` support being dropped
throw new Error (
"This version of " +
name +
2024-08-26 07:06:02 +02:00
' no longer supports "svrmodpack" library for ' +
name +
" mods. Please consider using newer mods with .tar.gz format." ,
2024-08-25 15:40:53 +02:00
) ;
}
// Add the mod to the mods list
mods . push (
legacyModWrapper (
require (
process . dirname +
"/temp/" +
modloaderFolderName +
"/" +
modFileRaw +
"/index.js" ,
) ,
) ,
) ;
// Read the mod's info file
try {
modInfos . push (
JSON . parse (
fs . readFileSync (
process . dirname +
"/temp/" +
modloaderFolderName +
"/" +
modFileRaw +
"/mod.info" ,
) ,
) ,
) ;
} catch ( err ) {
// If failed to read info file, add a placeholder entry to modInfos with an error message
modInfos . push ( {
2024-08-26 19:21:52 +02:00
name : ` Unknown mod ( ${ modFileRaw } ; ${ err . message } ) ` ,
2024-08-25 15:40:53 +02:00
version : "ERROR" ,
} ) ;
}
} catch ( err ) {
modLoadingErrors . push ( {
error : err ,
modName : modFileRaw ,
} ) ;
}
2024-08-25 14:02:45 +02:00
}
}
} ) ;
2024-08-25 15:47:12 +02:00
// Define the temporary server-side JavaScript file name
let tempServerSideScriptName = "serverSideScript.js" ;
2024-08-25 15:50:56 +02:00
if (
! (
process . isBun &&
process . versions . bun &&
process . versions . bun [ 0 ] == "0"
) &&
cluster . isPrimary === false
) {
2024-08-25 15:47:12 +02:00
// If not the master process and it's not Bun, create a unique temporary server-side JavaScript file name for each worker
2024-08-25 15:50:56 +02:00
tempServerSideScriptName =
".serverSideScript_w" + Math . floor ( Math . random ( ) * 65536 ) + ".js" ;
2024-08-25 15:47:12 +02:00
}
// Determine path of server-side script file
let SSJSPath = "./serverSideScript.js" ;
2024-08-25 15:50:56 +02:00
if ( ! process . serverConfig . useWebRootServerSideScript )
SSJSPath = process . dirname + "/serverSideScript.js" ;
2024-08-25 15:47:12 +02:00
// Check if a custom server side script file exists
if ( fs . existsSync ( SSJSPath ) && fs . statSync ( SSJSPath ) . isFile ( ) ) {
try {
// Prepend necessary modules and variables to the custom server side script
2024-08-26 19:23:52 +02:00
const modhead = ` var readline = require('readline'); \r \n var os = require('os'); \r \n var http = require('http'); \r \n var url = require('url'); \r \n var fs = require('fs'); \r \n var path = require('path'); \r \n ${
hexstrbase64 === undefined
2024-08-25 15:50:56 +02:00
? ""
2024-08-26 19:23:52 +02:00
: "var hexstrbase64 = require('../hexstrbase64/index.js');\r\n"
} $ {
crypto . _ _disabled _ _ === undefined
? "var crypto = require('crypto');\r\nvar https = require('https');\r\n"
: ""
2024-08-27 11:28:24 +02:00
} var stream = require ( 'stream' ) ; \ r \ nvar customvar1 ; \ r \ nvar customvar2 ; \ r \ nvar customvar3 ; \ r \ nvar customvar4 ; \ r \ n \ r \ nfunction Mod ( ) { } \ r \ nMod . prototype . callback = function callback ( req , res , serverconsole , responseEnd , href , ext , uobject , search , defaultpage , users , page404 , head , foot , fd , elseCallback , configJSON , callServerError , getCustomHeaders , origHref , redirect , parsePostData , authUser ) { \ r \ nreturn ( ) => { \ r \ nvar disableEndElseCallbackExecute = false ; \ r \ nfunction filterHeaders ( e ) { var r = { } ; return Object . keys ( e ) . forEach ( ( ( t ) => { null !== e [ t ] && void 0 !== e [ t ] && ( "object" == typeof e [ t ] ? r [ t ] = JSON . parse ( JSON . stringify ( e [ t ] ) ) : r [ t ] = e [ t ] ) } ) ) , r } \ r \ nfunction checkHostname ( e ) { if ( void 0 === e || "*" == e ) return ! 0 ; if ( req . headers . host && 0 == e . indexOf ( "*." ) && "*." != e ) { var r = e . substring ( 2 ) ; if ( req . headers . host == r || req . headers . host . indexOf ( "." + r ) == req . headers . host . length - r . length - 1 ) return ! 0 } else if ( req . headers . host && req . headers . host == e ) return ! 0 ; return ! 1 } \ r \ nfunction checkHref ( e ) { return href == e || "win32" == os . platform ( ) && href . toLowerCase ( ) == e . toLowerCase ( ) } \ r \ n ` ;
2024-08-25 15:50:56 +02:00
const modfoot =
"\r\nif(!disableEndElseCallbackExecute) {\r\ntry{\r\nelseCallback();\r\n} catch(err) {\r\n}\r\n}\r\n}\r\n}\r\nmodule.exports = Mod;" ;
2024-08-25 15:47:12 +02:00
// Write the modified server side script to the temp folder
2024-08-25 15:50:56 +02:00
fs . writeFileSync (
process . dirname + "/temp/" + tempServerSideScriptName ,
modhead + fs . readFileSync ( SSJSPath ) + modfoot ,
) ;
2024-08-25 15:47:12 +02:00
// Add the server side script to the mods list
2024-08-25 15:50:56 +02:00
mods . push (
legacyModWrapper (
require ( process . dirname + "/temp/" + tempServerSideScriptName ) ,
) ,
) ;
2024-08-25 15:47:12 +02:00
} catch ( err ) {
SSJSError = err ;
}
}
2024-08-25 14:02:45 +02:00
}
2024-08-25 16:47:26 +02:00
// Middleware
2024-08-23 20:43:09 +02:00
let middleware = [
2024-08-23 22:13:58 +02:00
require ( "./middleware/urlSanitizer.js" ) ,
2024-08-24 08:02:11 +02:00
require ( "./middleware/redirects.js" ) ,
2024-08-25 09:12:39 +02:00
require ( "./middleware/blocklist.js" ) ,
2024-08-24 08:22:49 +02:00
require ( "./middleware/webRootPostfixes.js" ) ,
2024-08-24 17:32:27 +02:00
require ( "./middleware/rewriteURL.js" ) ,
2024-08-24 17:44:25 +02:00
require ( "./middleware/responseHeaders.js" ) ,
2024-08-24 20:32:06 +02:00
require ( "./middleware/checkForbiddenPaths.js" ) ,
2024-08-24 20:40:28 +02:00
require ( "./middleware/nonStandardCodesAndHttpAuthentication.js" ) ,
2024-08-25 09:44:53 +02:00
require ( "./middleware/redirectTrailingSlashes.js" ) ,
2024-08-25 14:24:18 +02:00
... mods , // Load SVR.JS mods as middleware
2024-08-25 09:44:53 +02:00
require ( "./middleware/defaultHandlerChecks.js" ) ,
2024-08-25 10:23:37 +02:00
require ( "./middleware/status.js" ) ,
2024-08-25 11:32:03 +02:00
require ( "./middleware/staticFileServingAndDirectoryListings.js" ) ,
2024-08-23 20:43:09 +02:00
] ;
2024-08-25 16:51:25 +02:00
// HTTP server handlers
const requestHandler = require ( "./handlers/requestHandler.js" ) (
serverconsole ,
middleware ,
) ;
const proxyHandler = require ( "./handlers/proxyHandler.js" ) (
serverconsole ,
middleware ,
) ;
2024-08-25 17:04:49 +02:00
const noproxyHandler = require ( "./handlers/noproxyHandler.js" ) ( serverconsole ) ;
2024-08-25 16:51:25 +02:00
const clientErrorHandler = require ( "./handlers/clientErrorHandler.js" ) (
serverconsole ,
) ;
2024-08-25 17:51:25 +02:00
const serverErrorHandler = require ( "./handlers/serverErrorHandler.js" ) (
serverconsole ,
) ;
2024-08-26 07:00:08 +02:00
let messageTransmitted = false ;
2024-08-25 20:18:35 +02:00
function listeningMessage ( ) {
closedMaster = false ;
if ( ! cluster . isPrimary && cluster . isPrimary !== undefined ) {
process . send ( "\x12LISTEN" ) ;
return ;
}
2024-08-26 07:06:02 +02:00
const listenToLocalhost =
listenAddress &&
( listenAddress == "localhost" ||
listenAddress . match ( /^127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ ) ||
listenAddress . match ( /^(?:0{0,4}:)+0{0,3}1$/ ) ) ;
const listenToAny =
! listenAddress ||
listenAddress . match ( /^0{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ ) ||
listenAddress . match ( /^(?:0{0,4}:)+0{0,4}$/ ) ;
const sListenToLocalhost =
sListenAddress &&
( sListenAddress == "localhost" ||
sListenAddress . match ( /^127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ ) ||
sListenAddress . match ( /^(?:0{0,4}:)+0{0,3}1$/ ) ) ;
const sListenToAny =
! sListenAddress ||
sListenAddress . match ( /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ ) ||
sListenAddress . match ( /^(?:0{0,4}:)+0{0,4}$/ ) ;
2024-08-26 07:00:08 +02:00
let accHost = host ;
let sAccHost = host ;
if ( ! listenToAny ) accHost = listenAddress ;
if ( ! sListenToAny ) sAccHost = sListenAddress ;
if ( messageTransmitted ) return ;
messageTransmitted = true ;
serverconsole . locmessage ( "Started server at: " ) ;
if ( process . serverConfig . secure && ( sListenToLocalhost || sListenToAny ) ) {
if ( typeof process . serverConfig . sport === "number" ) {
2024-08-26 07:06:02 +02:00
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` * https://localhost ${
process . serverConfig . sport == 443
? ""
: ":" + process . serverConfig . sport
} ` ,
2024-08-26 07:06:02 +02:00
) ;
2024-08-26 07:00:08 +02:00
} else {
serverconsole . locmessage ( "* " + process . serverConfig . sport ) ; // Unix socket or Windows named pipe
}
}
2024-08-26 07:06:02 +02:00
if (
! (
process . serverConfig . secure &&
process . serverConfig . disableNonEncryptedServer
) &&
( listenToLocalhost || listenToAny )
) {
2024-08-26 07:00:08 +02:00
if ( typeof process . serverConfig . port === "number" ) {
2024-08-26 07:06:02 +02:00
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` * http://localhost ${
process . serverConfig . port == 80 ? "" : ":" + process . serverConfig . port
} ` ,
2024-08-26 07:06:02 +02:00
) ;
2024-08-26 07:00:08 +02:00
} else {
serverconsole . locmessage ( "* " + process . serverConfig . port ) ; // Unix socket or Windows named pipe
}
}
2024-08-26 07:06:02 +02:00
if (
process . serverConfig . secure &&
typeof process . serverConfig . sport === "number" &&
! sListenToLocalhost &&
( ! sListenToAny || ( host != "" && host != "[offline]" ) )
)
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` * https:// ${ sAccHost . indexOf ( ":" ) > - 1 ? "[" + sAccHost + "]" : sAccHost } ${
process . serverConfig . sport == 443
? ""
: ":" + process . serverConfig . sport
} ` ,
2024-08-26 07:06:02 +02:00
) ;
if (
! (
process . serverConfig . secure &&
process . serverConfig . disableNonEncryptedServer
) &&
! listenToLocalhost &&
( ! listenToAny || ( host != "" && host != "[offline]" ) ) &&
typeof process . serverConfig . port === "number"
)
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` * http:// ${ accHost . indexOf ( ":" ) > - 1 ? "[" + accHost + "]" : accHost } ${
process . serverConfig . port == 80 ? "" : ":" + process . serverConfig . port
} ` ,
2024-08-26 07:06:02 +02:00
) ;
2024-08-26 07:00:08 +02:00
ipStatusCallback ( ( ) => {
if ( pubip != "" ) {
2024-08-26 07:06:02 +02:00
if ( process . serverConfig . secure && ! sListenToLocalhost )
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` * https:// ${ pubip . indexOf ( ":" ) > - 1 ? "[" + pubip + "]" : pubip } ${
process . serverConfig . spubport == 443
? ""
: ":" + process . serverConfig . spubport
} ` ,
2024-08-26 07:06:02 +02:00
) ;
if (
! (
process . serverConfig . secure &&
process . serverConfig . disableNonEncryptedServer
) &&
! listenToLocalhost
)
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` * http:// ${ pubip . indexOf ( ":" ) > - 1 ? "[" + pubip + "]" : pubip } ${
process . serverConfig . pubport == 80
? ""
: ":" + process . serverConfig . pubport
} ` ,
2024-08-26 07:06:02 +02:00
) ;
2024-08-26 07:00:08 +02:00
}
if ( domain != "" ) {
2024-08-26 07:06:02 +02:00
if ( process . serverConfig . secure && ! sListenToLocalhost )
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` * https:// ${ domain } ${
process . serverConfig . spubport == 443
? ""
: ":" + process . serverConfig . spubport
} ` ,
2024-08-26 07:06:02 +02:00
) ;
if (
! (
process . serverConfig . secure &&
process . serverConfig . disableNonEncryptedServer
) &&
! listenToLocalhost
)
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` * http:// ${ domain } ${
process . serverConfig . pubport == 80
? ""
: ":" + process . serverConfig . pubport
} ` ,
2024-08-26 07:06:02 +02:00
) ;
2024-08-26 07:00:08 +02:00
}
2024-08-26 07:06:02 +02:00
serverconsole . locmessage ( 'For CLI help, you can type "help"' ) ;
2024-08-26 07:00:08 +02:00
// Code for sending data to a statistics server
2024-08-26 07:01:52 +02:00
if ( ! process . serverConfig . optOutOfStatisticsServer ) {
2024-08-26 07:00:08 +02:00
if ( crypto . _ _disabled _ _ !== undefined ) {
2024-08-26 07:06:02 +02:00
serverconsole . locwarnmessage (
"Sending data to statistics server is disabled, because the server only supports HTTPS, and your Node.JS version doesn't have crypto support." ,
) ;
2024-08-26 07:00:08 +02:00
} else {
2024-08-26 10:14:21 +02:00
sendStatistics ( modInfos , ( err ) => {
if ( err )
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` There was a problem, when sending data to statistics server! Reason: ${ err . message } ` ,
2024-08-26 10:14:21 +02:00
) ;
2024-08-26 07:00:08 +02:00
} ) ;
}
2024-08-26 07:01:52 +02:00
}
2024-08-26 07:00:08 +02:00
} ) ;
2024-08-25 20:18:35 +02:00
}
2024-08-25 19:39:49 +02:00
let reqcounterKillReq = 0 ;
2024-08-25 20:18:35 +02:00
let closedMaster = true ;
2024-08-25 19:39:49 +02:00
2024-08-25 17:04:49 +02:00
let server = { } ;
let server2 = { } ;
// Create secondary HTTP server
try {
server2 = http . createServer ( {
requireHostHeader : false ,
} ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 17:04:49 +02:00
} catch ( err ) {
server2 = http . createServer ( ) ;
}
// Add handlers to secondary HTTP server
server2 . on ( "request" , requestHandler ) ;
server2 . on ( "checkExpectation" , requestHandler ) ;
server2 . on ( "clientError" , clientErrorHandler ) ;
2024-08-25 17:11:31 +02:00
server2 . on (
"connect" ,
process . serverConfig . disableToHTTPSRedirect ? proxyHandler : noproxyHandler ,
) ;
2024-08-25 17:51:25 +02:00
server2 . on ( "error" , ( err ) => {
serverErrorHandler ( err , true , server2 , start ) ;
} ) ;
server2 . on ( "listening" , ( ) => {
serverErrorHandler . resetAttempts ( true ) ;
2024-08-25 20:18:35 +02:00
listeningMessage ( ) ;
2024-08-25 17:51:25 +02:00
} ) ;
2024-08-25 17:04:49 +02:00
2024-08-25 16:51:25 +02:00
// Create HTTP server
2024-08-25 17:04:49 +02:00
if ( process . serverConfig . enableHTTP2 == true ) {
if ( process . serverConfig . secure ) {
server = http2 . createSecureServer ( {
allowHTTP1 : true ,
requireHostHeader : false ,
key : key ,
cert : cert ,
2024-08-25 17:15:00 +02:00
requestCert : process . serverConfig . useClientCertificate ,
rejectUnauthorized :
process . serverConfig . rejectUnauthorizedClientCertificates ,
ciphers : process . serverConfig . cipherSuite ,
ecdhCurve : process . serverConfig . ecdhCurve ,
minVersion : process . serverConfig . tlsMinVersion ,
maxVersion : process . serverConfig . tlsMaxVersion ,
sigalgs : process . serverConfig . signatureAlgorithms ,
settings : process . serverConfig . http2Settings ,
2024-08-25 17:04:49 +02:00
} ) ;
} else {
server = http2 . createServer ( {
allowHTTP1 : true ,
requireHostHeader : false ,
2024-08-25 17:15:00 +02:00
settings : process . serverConfig . http2Settings ,
2024-08-25 17:04:49 +02:00
} ) ;
}
} else {
if ( process . serverConfig . secure ) {
server = https . createServer ( {
key : key ,
cert : cert ,
requireHostHeader : false ,
2024-08-25 17:15:00 +02:00
requestCert : process . serverConfig . useClientCertificate ,
rejectUnauthorized :
process . serverConfig . rejectUnauthorizedClientCertificates ,
ciphers : process . serverConfig . cipherSuite ,
ecdhCurve : process . serverConfig . ecdhCurve ,
minVersion : process . serverConfig . tlsMinVersion ,
maxVersion : process . serverConfig . tlsMaxVersion ,
sigalgs : process . serverConfig . signatureAlgorithms ,
2024-08-25 17:04:49 +02:00
} ) ;
} else {
try {
server = http . createServer ( {
requireHostHeader : false ,
} ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 17:04:49 +02:00
} catch ( err ) {
server = http . createServer ( ) ;
}
}
}
2024-08-25 17:11:31 +02:00
// Load SNI contexts into HTTP server
2024-08-25 17:15:00 +02:00
if ( process . serverConfig . secure ) {
2024-08-25 17:11:31 +02:00
try {
2024-08-27 10:54:08 +02:00
sniCredentials . forEach ( ( sniCredentialsSingle ) => {
2024-08-25 17:11:31 +02:00
server . addContext ( sniCredentialsSingle . name , {
cert : sniCredentialsSingle . cert ,
2024-08-25 17:15:00 +02:00
key : sniCredentialsSingle . key ,
2024-08-25 17:11:31 +02:00
} ) ;
try {
2024-08-25 17:15:00 +02:00
var snMatches = sniCredentialsSingle . name . match (
/^([^:[]*|\[[^]]*\]?)((?::.*)?)$/ ,
) ;
if ( ! snMatches [ 1 ] [ 0 ] . match ( /^\.+$/ ) )
snMatches [ 1 ] [ 0 ] = snMatches [ 1 ] [ 0 ] . replace ( /\.+$/ , "" ) ;
server . _contexts [ server . _contexts . length - 1 ] [ 0 ] = new RegExp (
"^" +
snMatches [ 1 ]
. replace ( /([.^$+?\-\\[\]{}])/g , "\\$1" )
. replace ( /\*/g , "[^.:]*" ) +
( snMatches [ 1 ] [ 0 ] == "[" ||
snMatches [ 1 ] . match (
/^(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/ ,
)
? ""
: ".?" ) +
snMatches [ 2 ]
. replace ( /([.^$+?\-\\[\]{}])/g , "\\$1" )
. replace ( /\*/g , "[^.]*" ) +
"$" ,
"i" ,
) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 17:15:00 +02:00
} catch ( err ) {
2024-08-25 17:11:31 +02:00
// Can't replace regex, ignoring...
}
} ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 17:11:31 +02:00
} catch ( err ) {
// SNI error
}
}
2024-08-25 17:04:49 +02:00
// Add handlers to the server
server . on ( "request" , requestHandler ) ;
server . on ( "checkExpectation" , requestHandler ) ;
server . on ( "connect" , proxyHandler ) ;
server . on ( "clientError" , clientErrorHandler ) ;
2024-08-27 10:54:08 +02:00
server . on ( "error" , ( err ) => {
2024-08-25 17:51:25 +02:00
serverErrorHandler ( err , false , server , start ) ;
} ) ;
server . on ( "listening" , ( ) => {
serverErrorHandler . resetAttempts ( false ) ;
2024-08-25 20:18:35 +02:00
listeningMessage ( ) ;
2024-08-25 17:51:25 +02:00
} ) ;
2024-08-25 17:04:49 +02:00
if ( process . serverConfig . secure ) {
2024-08-27 10:54:08 +02:00
server . prependListener ( "connection" , ( sock ) => {
2024-08-25 17:04:49 +02:00
sock . reallyDestroy = sock . destroy ;
2024-08-27 10:54:08 +02:00
sock . destroy = ( ) => {
2024-08-25 17:04:49 +02:00
sock . toDestroy = true ;
} ;
} ) ;
2024-08-27 10:54:08 +02:00
server . prependListener ( "tlsClientError" , ( err , sock ) => {
2024-08-25 17:04:49 +02:00
if (
err . code == "ERR_SSL_HTTP_REQUEST" ||
err . message . indexOf ( "http request" ) != - 1
) {
sock . _parent . destroy = sock . _parent . reallyDestroy ;
sock . _readableState = sock . _parent . _readableState ;
sock . _writableState = sock . _parent . _writableState ;
sock . _parent . toDestroy = false ;
2024-08-27 10:54:08 +02:00
sock . pipe = ( a , b , c ) => {
2024-08-25 17:04:49 +02:00
sock . _parent . pipe ( a , b , c ) ;
} ;
2024-08-27 10:54:08 +02:00
sock . write = ( a , b , c ) => {
2024-08-25 17:04:49 +02:00
sock . _parent . write ( a , b , c ) ;
} ;
2024-08-27 10:54:08 +02:00
sock . end = ( a , b , c ) => {
2024-08-25 17:04:49 +02:00
sock . _parent . end ( a , b , c ) ;
} ;
sock . destroyed = sock . _parent . destroyed ;
sock . readable = sock . _parent . readable ;
sock . writable = sock . _parent . writable ;
sock . remoteAddress = sock . _parent . remoteAddress ;
sock . remotePort = sock . _parent . remoteAddress ;
2024-08-27 10:54:08 +02:00
sock . destroy = ( a , b , c ) => {
2024-08-25 17:04:49 +02:00
try {
sock . _parent . destroy ( a , b , c ) ;
sock . destroyed = sock . _parent . destroyed ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 17:04:49 +02:00
} catch ( err ) {
// Socket is probably already destroyed.
}
} ;
} else {
sock . _parent . destroy = sock . _parent . reallyDestroy ;
try {
if ( sock . _parent . toDestroy ) sock . _parent . destroy ( ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 17:04:49 +02:00
} catch ( err ) {
// Socket is probably already destroyed.
}
}
} ) ;
2024-08-27 10:54:08 +02:00
server . prependListener ( "secureConnection" , ( sock ) => {
2024-08-25 17:04:49 +02:00
sock . _parent . destroy = sock . _parent . reallyDestroy ;
delete sock . _parent . reallyDestroy ;
} ) ;
2024-08-25 17:59:52 +02:00
if ( process . serverConfig . enableOCSPStapling && ! ocsp . _errored ) {
2024-08-27 10:54:08 +02:00
server . on ( "OCSPRequest" , ( cert , issuer , callback ) => {
ocsp . getOCSPURI ( cert , ( err , uri ) => {
2024-08-25 17:04:49 +02:00
if ( err ) return callback ( err ) ;
2024-08-25 18:02:02 +02:00
const req = ocsp . request . generate ( cert , issuer ) ;
const options = {
2024-08-25 17:04:49 +02:00
url : uri ,
2024-08-25 18:02:18 +02:00
ocsp : req . data ,
2024-08-25 17:04:49 +02:00
} ;
ocspCache . request ( req . id , options , callback ) ;
} ) ;
} ) ;
2024-08-25 17:59:52 +02:00
}
2024-08-25 17:04:49 +02:00
}
2024-08-25 16:51:25 +02:00
2024-08-25 16:47:26 +02:00
// Base commands
let commands = {
2024-08-25 19:23:01 +02:00
close : ( args , log ) => {
2024-08-25 19:20:15 +02:00
try {
server . close ( ) ;
if (
process . serverConfig . secure &&
! process . serverConfig . disableNonEncryptedServer
) {
server2 . close ( ) ;
}
log ( "Server closed." ) ;
if ( cluster . isPrimary !== undefined ) process . send ( "\x12CLOSE" ) ;
} catch ( err ) {
2024-08-26 19:21:52 +02:00
log ( ` Cannot close server! Reason: ${ err . message } ` ) ;
2024-08-25 19:20:15 +02:00
}
} ,
2024-08-25 19:23:01 +02:00
open : ( args , log ) => {
2024-08-25 19:20:15 +02:00
try {
if (
typeof ( process . serverConfig . secure
? process . serverConfig . sport
: process . serverConfig . port ) == "number" &&
( process . serverConfig . secure ? sListenAddress : listenAddress )
) {
server . listen (
process . serverConfig . secure
? process . serverConfig . sport
: process . serverConfig . port ,
process . serverConfig . secure ? sListenAddress : listenAddress ,
) ;
} else {
server . listen (
process . serverConfig . secure
? process . serverConfig . sport
: process . serverConfig . port ,
) ;
}
if (
process . serverConfig . secure &&
! process . serverConfig . disableNonEncryptedServer
) {
if ( typeof process . serverConfig . port == "number" && listenAddress ) {
server2 . listen ( process . serverConfig . port , listenAddress ) ;
} else {
server2 . listen ( process . serverConfig . port ) ;
}
}
log ( "Server opened." ) ;
} catch ( err ) {
2024-08-26 19:21:52 +02:00
log ( ` Cannot open server! Reason: ${ err . message } ` ) ;
2024-08-25 19:20:15 +02:00
}
} ,
2024-08-25 16:47:26 +02:00
help : ( args , log ) => {
2024-08-26 19:21:52 +02:00
log ( ` Server commands: \n ${ Object . keys ( commands ) . join ( " " ) } ` ) ;
2024-08-25 16:47:26 +02:00
} ,
2024-08-25 19:23:01 +02:00
mods : ( args , log ) => {
2024-08-25 16:47:26 +02:00
log ( "Mods:" ) ;
for ( let i = 0 ; i < modInfos . length ; i ++ ) {
2024-08-26 19:23:52 +02:00
log ( ` ${ ( i + 1 ) . toString ( ) } . ${ modInfos [ i ] . name } ${ modInfos [ i ] . version } ` ) ;
2024-08-25 16:47:26 +02:00
}
if ( modInfos . length == 0 ) {
log ( "No mods installed." ) ;
}
} ,
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 19:23:01 +02:00
clear : ( args , log ) => {
2024-08-25 16:47:26 +02:00
console . clear ( ) ;
} ,
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 19:23:01 +02:00
stop : ( args , log ) => {
let retcode = args [ 0 ] ;
2024-08-25 19:46:17 +02:00
if (
! cluster . isPrimary &&
cluster . isPrimary !== undefined &&
server . listening
) {
2024-08-25 19:23:01 +02:00
try {
2024-08-27 10:54:08 +02:00
server . close ( ( ) => {
2024-08-25 19:23:01 +02:00
if ( server2 . listening ) {
try {
2024-08-27 10:54:08 +02:00
server2 . close ( ( ) => {
2024-08-25 19:23:01 +02:00
if ( ! process . removeFakeIPC ) {
if ( typeof retcode == "number" ) {
process . exit ( retcode ) ;
} else {
process . exit ( 0 ) ;
}
}
} ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 19:23:01 +02:00
} catch ( err ) {
if ( ! process . removeFakeIPC ) {
if ( typeof retcode == "number" ) {
process . exit ( retcode ) ;
} else {
process . exit ( 0 ) ;
}
}
}
} else {
if ( ! process . removeFakeIPC ) {
if ( typeof retcode == "number" ) {
process . exit ( retcode ) ;
} else {
process . exit ( 0 ) ;
}
}
}
} ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 19:23:01 +02:00
} catch ( err ) {
if ( typeof retcode == "number" ) {
process . exit ( retcode ) ;
} else {
process . exit ( 0 ) ;
}
}
if ( process . removeFakeIPC ) process . removeFakeIPC ( ) ;
} else {
if ( typeof retcode == "number" ) {
process . exit ( retcode ) ;
} else {
process . exit ( 0 ) ;
}
}
} ,
2024-08-25 20:37:25 +02:00
restart : ( args , log ) => {
2024-08-26 06:11:19 +02:00
if ( cluster . isPrimary === undefined )
2024-08-26 19:21:52 +02:00
log ( ` This command is not supported on single-threaded ${ name } . ` ) ;
else log ( ` This command need to be run in ${ name } master. ` ) ;
2024-08-26 06:11:19 +02:00
} ,
2024-08-25 16:47:26 +02:00
} ;
// Load commands from middleware
middleware . forEach ( ( middlewareO ) => {
if ( middlewareO . commands ) {
Object . keys ( middlewareO . commands ) . forEach ( ( command ) => {
2024-08-25 20:18:35 +02:00
const prevCommand = commands [ command ] ;
if ( prevCommand ) {
2024-08-26 06:11:19 +02:00
commands [ command ] = ( args , log ) =>
middlewareO . commands [ command ] ( args , log , prevCommand ) ;
2024-08-25 16:47:26 +02:00
} else {
2024-08-26 06:11:19 +02:00
commands [ command ] = ( args , log ) =>
middlewareO . commands [ command ] ( args , log , ( ) => { } ) ;
2024-08-25 16:47:26 +02:00
}
} ) ;
}
} ) ;
2024-08-25 19:17:19 +02:00
// SVR.JS worker spawn-related
let SVRJSInitialized = false ;
let crashed = false ;
let threadLimitWarned = false ;
// SVR.JS worker forking function
function SVRJSFork ( ) {
// Log
if ( SVRJSInitialized )
serverconsole . locmessage (
"Starting next thread, because previous one hung up/crashed..." ,
) ;
// Fork new worker
var newWorker = { } ;
try {
if (
! threadLimitWarned &&
cluster . _ _shimmed _ _ &&
process . isBun &&
process . versions . bun &&
process . versions . bun [ 0 ] != "0"
) {
threadLimitWarned = true ;
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` ${ name } limited the number of workers to one, because of startup problems in Bun 1.0 and newer with shimmed (not native) clustering module. Reliability may suffer. ` ,
2024-08-25 19:17:19 +02:00
) ;
}
if (
! (
cluster . _ _shimmed _ _ &&
process . isBun &&
process . versions . bun &&
process . versions . bun [ 0 ] != "0" &&
Object . keys ( cluster . workers ) > 0
)
) {
newWorker = cluster . fork ( ) ;
} else {
if ( SVRJSInitialized )
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` ${ name } limited the number of workers to one, because of startup problems in Bun 1.0 and newer with shimmed (not native) clustering module. Reliability may suffer. ` ,
2024-08-25 19:17:19 +02:00
) ;
}
} catch ( err ) {
if ( err . name == "NotImplementedError" ) {
// If cluster.fork throws a NotImplementedError, shim cluster module
cluster . bunShim ( ) ;
if (
! threadLimitWarned &&
cluster . _ _shimmed _ _ &&
process . isBun &&
process . versions . bun &&
process . versions . bun [ 0 ] != "0"
) {
threadLimitWarned = true ;
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` ${ name } limited the number of workers to one, because of startup problems in Bun 1.0 and newer with shimmed (not native) clustering module. Reliability may suffer. ` ,
2024-08-25 19:17:19 +02:00
) ;
}
if (
! (
cluster . _ _shimmed _ _ &&
process . isBun &&
process . versions . bun &&
process . versions . bun [ 0 ] != "0" &&
Object . keys ( cluster . workers ) > 0
)
) {
newWorker = cluster . fork ( ) ;
} else {
if ( SVRJSInitialized )
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` ${ name } limited the number of workers to one, because of startup problems in Bun 1.0 and newer with shimmed (not native) clustering module. Reliability may suffer. ` ,
2024-08-25 19:17:19 +02:00
) ;
}
} else {
throw err ;
}
}
// Add event listeners
if ( newWorker . on ) {
2024-08-27 10:54:08 +02:00
newWorker . on ( "error" , ( err ) => {
2024-08-25 19:17:19 +02:00
if ( ! exiting )
serverconsole . locwarnmessage (
2024-08-26 07:06:02 +02:00
"There was a problem when handling " +
name +
" worker! (from master process side) Reason: " +
2024-08-25 19:17:19 +02:00
err . message ,
) ;
} ) ;
2024-08-27 10:54:08 +02:00
newWorker . on ( "exit" , ( ) => {
2024-08-25 19:17:19 +02:00
if ( ! exiting && Object . keys ( cluster . workers ) . length == 0 ) {
crashed = true ;
SVRJSFork ( ) ;
}
} ) ;
2024-08-25 19:35:58 +02:00
addListenersToWorker ( newWorker ) ;
2024-08-25 19:17:19 +02:00
}
}
2024-08-25 19:37:22 +02:00
function getWorkerCountToFork ( ) {
2024-08-25 19:46:17 +02:00
let workersToFork = os . availableParallelism
? os . availableParallelism ( )
: os . cpus ( ) . length ;
2024-08-25 19:37:22 +02:00
try {
2024-08-25 19:46:17 +02:00
const useAvailableCores = Math . round ( os . freemem ( ) / 50000000 ) - 1 ; // 1 core deleted for safety...
2024-08-25 19:37:22 +02:00
if ( workersToFork > useAvailableCores ) workersToFork = useAvailableCores ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 19:37:22 +02:00
} catch ( err ) {
// Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working.
}
if ( workersToFork < 1 ) workersToFork = 1 ; // If SVR.JS is run on Haiku (os.cpus in Haiku returns empty array) or if useAvailableCores = 0
return workersToFork ;
}
function forkWorkers ( workersToFork , callback ) {
for ( var i = 0 ; i < workersToFork ; i ++ ) {
if ( i == 0 ) {
SVRJSFork ( ) ;
} else {
2024-08-25 19:46:17 +02:00
setTimeout (
2024-08-27 10:54:08 +02:00
( ( i ) => {
return ( ) => {
2024-08-25 19:46:17 +02:00
SVRJSFork ( ) ;
if ( i >= workersToFork - 1 ) callback ( ) ;
} ;
} ) ( i ) ,
i * 6.6 ,
) ;
2024-08-25 19:37:22 +02:00
}
}
}
2024-08-26 06:23:24 +02:00
// Listening message event listener
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 19:35:58 +02:00
process . messageEventListeners . push ( ( worker , serverconsole ) => {
return ( message ) => {
2024-08-25 20:18:35 +02:00
if ( message == "\x12LISTEN" ) {
listeningMessage ( ) ;
}
2024-08-25 19:46:17 +02:00
} ;
2024-08-25 19:35:58 +02:00
} ) ;
2024-08-26 07:08:31 +02:00
let isWorkerHungUpBuff = true ;
2024-08-26 06:17:51 +02:00
let isWorkerHungUpBuff2 = true ;
2024-08-26 06:11:19 +02:00
2024-08-25 20:18:35 +02:00
function msgListener ( message ) {
if ( message == "\x12END" ) {
for ( let i = 0 ; i < Object . keys ( cluster . workers ) . length ; i ++ ) {
2024-08-26 06:11:19 +02:00
cluster . workers [ Object . keys ( cluster . workers ) [ i ] ] . removeAllListeners (
"message" ,
) ;
2024-08-25 20:18:35 +02:00
addListenersToWorker ( cluster . workers [ Object . keys ( cluster . workers ) [ i ] ] ) ;
}
}
if ( message == "\x12END" ) {
// Do nothing
2024-08-25 20:21:48 +02:00
} else if ( message == "\x12CLOSE" ) {
2024-08-25 20:20:15 +02:00
closedMaster = true ;
2024-08-26 06:11:19 +02:00
} else if ( message == "\x12KILLOK" ) {
if ( typeof isWorkerHungUpBuff != "undefined" ) isWorkerHungUpBuff = false ;
2024-08-26 06:17:51 +02:00
} else if ( message == "\x12PINGOK" ) {
if ( typeof isWorkerHungUpBuff2 != "undefined" ) isWorkerHungUpBuff2 = false ;
2024-08-26 06:11:19 +02:00
} else if ( message == "\x12KILLTERMMSG" ) {
serverconsole . locmessage ( "Terminating unused worker process..." ) ;
2024-08-26 06:17:51 +02:00
} else if ( message == "\x12SAVEGOOD" ) {
serverconsole . locmessage ( "Configuration saved." ) ;
} else if ( message . indexOf ( "\x12SAVEERR" ) == 0 ) {
2024-08-26 06:19:53 +02:00
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` There was a problem while saving configuration file. Reason: ${ message . substring ( 8 ) } ` ,
2024-08-26 06:19:53 +02:00
) ;
2024-08-25 20:18:35 +02:00
} else if ( message [ 0 ] == "\x12" ) {
2024-08-26 07:02:26 +02:00
// Discard unrecognized control messages
2024-08-25 20:18:35 +02:00
} else {
serverconsole . climessage ( message ) ;
}
}
2024-08-26 06:26:22 +02:00
// Save configuration file
2024-08-26 06:17:51 +02:00
function saveConfig ( ) {
2024-08-26 06:26:22 +02:00
for ( var i = 0 ; i < 3 ; i ++ ) {
try {
var configJSONobj = { } ;
2024-08-26 06:32:54 +02:00
if ( fs . existsSync ( process . dirname + "/config.json" ) )
configJSONobj = JSON . parse (
fs . readFileSync ( process . dirname + "/config.json" ) . toString ( ) ,
) ;
2024-08-26 06:26:22 +02:00
if ( configJSONobj . users === undefined ) configJSONobj . users = [ ] ;
if ( process . serverConfig . secure ) {
if ( configJSONobj . key === undefined ) configJSONobj . key = "cert/key.key" ;
2024-08-26 06:32:54 +02:00
if ( configJSONobj . cert === undefined )
configJSONobj . cert = "cert/cert.crt" ;
2024-08-26 06:26:22 +02:00
if ( configJSONobj . sport === undefined ) configJSONobj . sport = 443 ;
if ( configJSONobj . spubport === undefined ) configJSONobj . spubport = 443 ;
if ( configJSONobj . sni === undefined ) configJSONobj . sni = { } ;
2024-08-26 06:32:54 +02:00
if ( configJSONobj . enableOCSPStapling === undefined )
configJSONobj . enableOCSPStapling = false ;
2024-08-26 06:26:22 +02:00
}
if ( configJSONobj . port === undefined ) configJSONobj . port = 80 ;
if ( configJSONobj . pubport === undefined ) configJSONobj . pubport = 80 ;
2024-08-26 06:32:54 +02:00
if (
configJSONobj . domain === undefined &&
configJSONobj . domian !== undefined
)
configJSONobj . domain = configJSONobj . domian ;
2024-08-26 06:26:22 +02:00
delete configJSONobj . domian ;
2024-08-26 06:32:54 +02:00
if ( configJSONobj . page404 === undefined )
configJSONobj . page404 = "404.html" ;
2024-08-26 06:26:22 +02:00
configJSONobj . timestamp = process . serverConfig . timestamp ;
configJSONobj . blacklist = process . serverConfig . blacklist ;
2024-08-26 06:32:54 +02:00
if ( configJSONobj . nonStandardCodes === undefined )
configJSONobj . nonStandardCodes = [ ] ;
if ( configJSONobj . enableCompression === undefined )
configJSONobj . enableCompression = true ;
if ( configJSONobj . customHeaders === undefined )
configJSONobj . customHeaders = { } ;
if ( configJSONobj . enableHTTP2 === undefined )
configJSONobj . enableHTTP2 = false ;
if ( configJSONobj . enableLogging === undefined )
configJSONobj . enableLogging = true ;
if ( configJSONobj . enableDirectoryListing === undefined )
configJSONobj . enableDirectoryListing = true ;
if ( configJSONobj . enableDirectoryListingWithDefaultHead === undefined )
configJSONobj . enableDirectoryListingWithDefaultHead = false ;
if ( configJSONobj . serverAdministratorEmail === undefined )
configJSONobj . serverAdministratorEmail = "[no contact information]" ;
if ( configJSONobj . stackHidden === undefined )
configJSONobj . stackHidden = false ;
if ( configJSONobj . enableRemoteLogBrowsing === undefined )
configJSONobj . enableRemoteLogBrowsing = false ;
if ( configJSONobj . exposeServerVersion === undefined )
configJSONobj . exposeServerVersion = true ;
if ( configJSONobj . disableServerSideScriptExpose === undefined )
configJSONobj . disableServerSideScriptExpose = true ;
if ( configJSONobj . allowStatus === undefined )
configJSONobj . allowStatus = true ;
2024-08-26 06:26:22 +02:00
if ( configJSONobj . rewriteMap === undefined ) configJSONobj . rewriteMap = [ ] ;
2024-08-26 06:32:54 +02:00
if ( configJSONobj . dontCompress === undefined )
configJSONobj . dontCompress = [
"/.*\\.ipxe$/" ,
"/.*\\.(?:jpe?g|png|bmp|tiff|jfif|gif|webp)$/" ,
"/.*\\.(?:[id]mg|iso|flp)$/" ,
"/.*\\.(?:zip|rar|bz2|[gb7x]z|lzma|tar)$/" ,
"/.*\\.(?:mp[34]|mov|wm[av]|avi|webm|og[gv]|mk[va])$/" ,
] ;
if ( configJSONobj . enableIPSpoofing === undefined )
configJSONobj . enableIPSpoofing = false ;
2024-08-26 06:26:22 +02:00
if ( configJSONobj . secure === undefined ) configJSONobj . secure = false ;
2024-08-26 06:32:54 +02:00
if ( configJSONobj . disableNonEncryptedServer === undefined )
configJSONobj . disableNonEncryptedServer = false ;
if ( configJSONobj . disableToHTTPSRedirect === undefined )
configJSONobj . disableToHTTPSRedirect = false ;
if ( configJSONobj . enableETag === undefined )
configJSONobj . enableETag = true ;
if ( configJSONobj . disableUnusedWorkerTermination === undefined )
configJSONobj . disableUnusedWorkerTermination = false ;
if ( configJSONobj . rewriteDirtyURLs === undefined )
configJSONobj . rewriteDirtyURLs = false ;
2024-08-26 06:26:22 +02:00
if ( configJSONobj . errorPages === undefined ) configJSONobj . errorPages = [ ] ;
2024-08-26 06:32:54 +02:00
if ( configJSONobj . useWebRootServerSideScript === undefined )
configJSONobj . useWebRootServerSideScript = true ;
if ( configJSONobj . exposeModsInErrorPages === undefined )
configJSONobj . exposeModsInErrorPages = true ;
if ( configJSONobj . disableTrailingSlashRedirects === undefined )
configJSONobj . disableTrailingSlashRedirects = false ;
if ( configJSONobj . environmentVariables === undefined )
configJSONobj . environmentVariables = { } ;
if ( configJSONobj . allowDoubleSlashes === undefined )
configJSONobj . allowDoubleSlashes = false ;
if ( configJSONobj . optOutOfStatisticsServer === undefined )
configJSONobj . optOutOfStatisticsServer = false ;
2024-08-26 06:26:22 +02:00
var configString = JSON . stringify ( configJSONobj , null , 2 ) + "\n" ;
fs . writeFileSync ( _ _dirname + "/config.json" , configString ) ;
break ;
} catch ( err ) {
if ( i >= 2 ) throw err ;
var now = Date . now ( ) ;
while ( Date . now ( ) - now < 2 ) ;
}
}
2024-08-26 06:17:51 +02:00
}
2024-08-25 19:17:19 +02:00
// Starting function
2024-08-25 18:57:46 +02:00
function start ( init ) {
init = Boolean ( init ) ;
if ( cluster . isPrimary || cluster . isPrimary === undefined ) {
if ( init ) {
2024-08-25 19:40:27 +02:00
for ( let i = 0 ; i < logo . length ; i ++ ) console . log ( logo [ i ] ) ; // Print logo
2024-08-25 18:57:46 +02:00
console . log ( ) ;
2024-08-25 18:59:16 +02:00
console . log (
2024-08-26 19:21:52 +02:00
` Welcome to \x 1b[1m ${ name } - a web server running on Node.JS \x 1b[0m ` ,
2024-08-25 18:59:16 +02:00
) ;
2024-08-25 18:57:46 +02:00
// Print warnings
2024-08-25 18:59:16 +02:00
if ( version . indexOf ( "Nightly-" ) === 0 )
serverconsole . locwarnmessage (
"This version is only for test purposes and may be unstable." ,
) ;
if ( process . serverConfig . enableHTTP2 && ! process . serverConfig . secure )
serverconsole . locwarnmessage (
"HTTP/2 without HTTPS may not work in web browsers. Web browsers only support HTTP/2 with HTTPS!" ,
) ;
2024-08-25 18:57:46 +02:00
if ( process . isBun ) {
2024-08-25 18:59:16 +02:00
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` Bun support is experimental. Some features of ${ name } , ${ name } mods and ${ name } server-side JavaScript may not work as expected. ` ,
2024-08-25 18:59:16 +02:00
) ;
if (
process . isBun &&
! (
process . versions . bun &&
! process . versions . bun . match (
/^(?:0\.|1\.0\.|1\.1\.[0-9](?![0-9])|1\.1\.1[0-2](?![0-9]))/ ,
)
) &&
2024-08-27 10:54:08 +02:00
process . serverConfig . users . some ( ( entry ) => {
2024-08-25 18:59:16 +02:00
return entry . pbkdf2 ;
} )
)
serverconsole . locwarnmessage (
"PBKDF2 password hashing function in Bun versions older than v1.1.13 blocks the event loop, which may result in denial of service." ,
) ;
2024-08-25 18:57:46 +02:00
}
2024-08-25 18:59:16 +02:00
if ( cluster . isPrimary === undefined )
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` You're running ${ name } on single thread. Reliability may suffer, as the server is stopped after crash. ` ,
2024-08-25 18:59:16 +02:00
) ;
if ( crypto . _ _disabled _ _ !== undefined )
serverconsole . locwarnmessage (
"Your Node.JS version doesn't have crypto support! The 'crypto' module is essential for providing cryptographic functionality in Node.JS. Without crypto support, certain security features may be unavailable, and some functionality may not work as expected. It's recommended to use a Node.JS version that includes crypto support to ensure the security and proper functioning of your server." ,
) ;
if ( crypto . _ _disabled _ _ === undefined && ! crypto . scrypt )
serverconsole . locwarnmessage (
"Your JavaScript runtime doesn't have native scrypt support. HTTP authentication involving scrypt hashes will not work." ,
) ;
if (
! process . isBun &&
/^v(?:[0-9]\.|1[0-7]\.|18\.(?:[0-9]|1[0-8])\.|18\.19\.0|20\.(?:[0-9]|10)\.|20\.11\.0|21\.[0-5]\.|21\.6\.0|21\.6\.1(?![0-9]))/ . test (
process . version ,
)
)
serverconsole . locwarnmessage (
"Your Node.JS version is vulnerable to HTTP server DoS (CVE-2024-22019)." ,
) ;
if (
! process . isBun &&
/^v(?:[0-9]\.|1[0-7]\.|18\.(?:1?[0-9])\.|18\.20\.0|20\.(?:[0-9]|1[01])\.|20\.12\.0|21\.[0-6]\.|21\.7\.0|21\.7\.1(?![0-9]))/ . test (
process . version ,
)
)
serverconsole . locwarnmessage (
"Your Node.JS version is vulnerable to HTTP server request smuggling (CVE-2024-27982)." ,
) ;
if ( process . getuid && process . getuid ( ) == 0 )
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` You're running ${ name } as root. It's recommended to run ${ name } as an non-root user. Running ${ name } as root may increase the risks of OS command execution vulnerabilities. ` ,
2024-08-25 18:59:16 +02:00
) ;
if (
! process . isBun &&
process . serverConfig . secure &&
process . versions &&
process . versions . openssl &&
process . versions . openssl . substring ( 0 , 2 ) == "1."
) {
2024-08-25 18:57:46 +02:00
if ( new Date ( ) > new Date ( "11 September 2023" ) ) {
2024-08-25 18:59:16 +02:00
serverconsole . locwarnmessage (
"OpenSSL 1.x is no longer receiving security updates after 11th September 2023. Your HTTPS communication might be vulnerable. It is recommended to update to a newer version of Node.JS that includes OpenSSL 3.0 or higher to ensure the security of your server and data." ,
) ;
2024-08-25 18:57:46 +02:00
} else {
2024-08-25 18:59:16 +02:00
serverconsole . locwarnmessage (
"OpenSSL 1.x will no longer receive security updates after 11th September 2023. Your HTTPS communication might be vulnerable in future. It is recommended to update to a newer version of Node.JS that includes OpenSSL 3.0 or higher to ensure the security of your server and data." ,
) ;
2024-08-25 18:57:46 +02:00
}
}
2024-08-25 18:59:16 +02:00
if (
process . serverConfig . secure &&
process . serverConfig . enableOCSPStapling &&
ocsp . _errored
)
serverconsole . locwarnmessage (
"Can't load OCSP module. OCSP stapling will be disabled. OCSP stapling is a security feature that improves the performance and security of HTTPS connections by caching the certificate status response. If you require this feature, consider updating your Node.JS version or checking for any issues with the 'ocsp' module." ,
) ;
2024-08-27 12:16:21 +02:00
if ( disableMods )
2024-08-25 18:59:16 +02:00
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` ${ name } is running without mods and server-side JavaScript enabled. Web applications may not work as expected ` ,
2024-08-25 18:59:16 +02:00
) ;
if ( process . serverConfig . optOutOfStatisticsServer )
serverconsole . locmessage (
2024-08-26 19:21:52 +02:00
` ${ name } is configured to opt out of sending data to the statistics server. ` ,
2024-08-25 18:59:16 +02:00
) ;
2024-08-25 18:57:46 +02:00
console . log ( ) ;
// Display mod and server-side JavaScript errors
if ( process . isPrimary || process . isPrimary === undefined ) {
2024-08-27 10:54:08 +02:00
modLoadingErrors . forEach ( ( modLoadingError ) => {
2024-08-25 18:59:16 +02:00
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` There was a problem while loading a " ${ String ( modLoadingError . modName ) . replace ( /[\r\n]/g , "" ) } " mod. ` ,
2024-08-25 18:59:16 +02:00
) ;
2024-08-25 18:57:46 +02:00
serverconsole . locwarnmessage ( "Stack:" ) ;
2024-08-25 18:59:16 +02:00
serverconsole . locwarnmessage (
generateErrorStack ( modLoadingError . error ) ,
) ;
2024-08-25 18:57:46 +02:00
} ) ;
if ( SSJSError ) {
2024-08-25 18:59:16 +02:00
serverconsole . locwarnmessage (
"There was a problem while loading server-side JavaScript." ,
) ;
2024-08-25 18:57:46 +02:00
serverconsole . locwarnmessage ( "Stack:" ) ;
serverconsole . locwarnmessage ( generateErrorStack ( SSJSError ) ) ;
}
if ( SSJSError || modLoadingErrors . length > 0 ) console . log ( ) ;
}
// Print server information
serverconsole . locmessage ( "Server version: " + version ) ;
2024-08-25 18:59:16 +02:00
if ( process . isBun )
serverconsole . locmessage ( "Bun version: v" + process . versions . bun ) ;
2024-08-25 18:57:46 +02:00
else serverconsole . locmessage ( "Node.JS version: " + process . version ) ;
const CPUs = os . cpus ( ) ;
2024-08-25 18:59:16 +02:00
if ( CPUs . length > 0 )
serverconsole . locmessage (
2024-08-26 19:21:52 +02:00
` CPU: ${ CPUs . length > 1 ? CPUs . length + "x " : "" } ${ CPUs [ 0 ] . model } ` ,
2024-08-25 18:59:16 +02:00
) ;
2024-08-25 18:57:46 +02:00
// Throw errors
2024-08-25 18:59:16 +02:00
if ( vnum < 64 )
throw new Error (
2024-08-26 19:21:52 +02:00
` ${ name } requires Node.JS 10.0.0 and newer, but your Node.JS version isn't supported by ${ name } . ` ,
2024-08-25 18:59:16 +02:00
) ;
if ( configJSONRErr )
throw new Error (
2024-08-26 19:21:52 +02:00
` Can't read ${ name } configuration file: ${ configJSONRErr . message } ` ,
2024-08-25 18:59:16 +02:00
) ;
if ( configJSONPErr )
throw new Error (
2024-08-26 19:21:52 +02:00
` ${ name } configuration parse error: ${ configJSONPErr . message } ` ,
2024-08-25 18:59:16 +02:00
) ;
if (
process . serverConfig . enableHTTP2 &&
! process . serverConfig . secure &&
typeof process . serverConfig . port != "number"
)
throw new Error (
2024-08-26 19:21:52 +02:00
` HTTP/2 without HTTPS, along with Unix sockets/Windows named pipes aren't supported by ${ name } . ` ,
2024-08-25 18:59:16 +02:00
) ;
if ( process . serverConfig . enableHTTP2 && http2 . _ _disabled _ _ !== undefined )
throw new Error (
2024-08-26 19:21:52 +02:00
` HTTP/2 isn't supported by your Node.JS version! You may not be able to use HTTP/2 with ${ name } ` ,
2024-08-25 18:59:16 +02:00
) ;
2024-08-25 18:57:46 +02:00
if ( listenAddress ) {
2024-08-25 18:59:16 +02:00
if ( listenAddress . match ( /^[0-9]+$/ ) )
throw new Error (
"Listening network address can't be numeric (it need to be either valid IP address, or valid domain name)." ,
) ;
if (
listenAddress . match (
/^(?:2(?:2[4-9]|3[0-9])\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$|ff[0-9a-f][0-9a-f]:[0-9a-f:])/i ,
)
)
2024-08-26 19:21:52 +02:00
throw new Error ( ` ${ name } can't listen on multicast address. ` ) ;
2024-08-25 18:59:16 +02:00
if ( brdIPs . indexOf ( listenAddress ) > - 1 )
2024-08-26 19:21:52 +02:00
throw new Error ( ` ${ name } can't listen on broadcast address. ` ) ;
2024-08-25 18:59:16 +02:00
if ( netIPs . indexOf ( listenAddress ) > - 1 )
2024-08-26 19:21:52 +02:00
throw new Error ( ` ${ name } can't listen on subnet address. ` ) ;
2024-08-25 18:57:46 +02:00
}
2024-08-25 18:59:16 +02:00
if ( certificateError )
throw new Error (
2024-08-26 19:21:52 +02:00
` There was a problem with SSL certificate/private key: ${ certificateError . message } ` ,
2024-08-25 18:59:16 +02:00
) ;
if ( wwwrootError )
throw new Error (
2024-08-26 19:21:52 +02:00
` There was a problem with your web root: ${ wwwrootError . message } ` ,
2024-08-25 18:59:16 +02:00
) ;
if ( sniReDos )
throw new Error (
"Refusing to start, because the current SNI configuration would make the server vulnerable to ReDoS." ,
) ;
2024-08-25 18:57:46 +02:00
}
// Print server startup information
2024-08-25 19:17:19 +02:00
if (
! (
process . serverConfig . secure &&
process . serverConfig . disableNonEncryptedServer
)
)
2024-08-25 18:59:16 +02:00
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` Starting HTTP server at ${
typeof process . serverConfig . port == "number"
? listenAddress
? ( listenAddress . indexOf ( ":" ) > - 1
? "[" + listenAddress + "]"
: listenAddress ) + ":"
: "port "
: ""
} $ { process . serverConfig . port . toString ( ) } ... ` ,
2024-08-25 18:59:16 +02:00
) ;
if ( process . serverConfig . secure )
serverconsole . locmessage (
2024-08-26 19:23:52 +02:00
` Starting HTTPS server at ${
typeof process . serverConfig . sport == "number"
? sListenAddress
? ( sListenAddress . indexOf ( ":" ) > - 1
? "[" + sListenAddress + "]"
: sListenAddress ) + ":"
: "port "
: ""
} $ { process . serverConfig . sport . toString ( ) } ... ` ,
2024-08-25 18:59:16 +02:00
) ;
2024-08-25 18:57:46 +02:00
}
if ( ! cluster . isPrimary ) {
try {
2024-08-25 18:59:16 +02:00
if (
typeof ( process . serverConfig . secure
? process . serverConfig . sport
: process . serverConfig . port ) == "number" &&
( process . serverConfig . secure ? sListenAddress : listenAddress )
) {
server . listen (
process . serverConfig . secure
? process . serverConfig . sport
: process . serverConfig . port ,
process . serverConfig . secure ? sListenAddress : listenAddress ,
) ;
2024-08-25 18:57:46 +02:00
} else {
2024-08-25 18:59:16 +02:00
server . listen (
process . serverConfig . secure
? process . serverConfig . sport
: process . serverConfig . port ,
) ;
2024-08-25 18:57:46 +02:00
}
} catch ( err ) {
if ( err . code != "ERR_SERVER_ALREADY_LISTEN" ) throw err ;
}
2024-08-25 19:17:19 +02:00
if (
process . serverConfig . secure &&
! process . serverConfig . disableNonEncryptedServer
) {
2024-08-25 18:57:46 +02:00
try {
if ( typeof process . serverConfig . port == "number" && listenAddress ) {
server2 . listen ( process . serverConfig . port , listenAddress ) ;
} else {
server2 . listen ( process . serverConfig . port ) ;
}
} catch ( err ) {
if ( err . code != "ERR_SERVER_ALREADY_LISTEN" ) throw err ;
}
}
}
2024-08-25 19:28:20 +02:00
if ( init ) {
2024-08-25 19:35:58 +02:00
let workersToFork = 1 ;
2024-08-25 18:57:46 +02:00
2024-08-26 06:17:51 +02:00
if ( cluster . isPrimary === undefined ) {
2024-08-27 10:54:08 +02:00
setInterval ( ( ) => {
2024-08-25 18:57:46 +02:00
try {
saveConfig ( ) ;
serverconsole . locmessage ( "Configuration saved." ) ;
} catch ( err ) {
throw new Error ( err ) ;
}
} , 300000 ) ;
} else if ( cluster . isPrimary ) {
2024-08-27 10:54:08 +02:00
setInterval ( ( ) => {
2024-08-25 18:57:46 +02:00
var allWorkers = Object . keys ( cluster . workers ) ;
var goodWorkers = [ ] ;
2024-08-26 06:17:51 +02:00
const checkWorker = ( callback , _id ) => {
2024-08-25 18:57:46 +02:00
if ( typeof _id === "undefined" ) _id = 0 ;
if ( _id >= allWorkers . length ) {
callback ( ) ;
return ;
}
try {
if ( cluster . workers [ allWorkers [ _id ] ] ) {
isWorkerHungUpBuff2 = true ;
cluster . workers [ allWorkers [ _id ] ] . on ( "message" , msgListener ) ;
cluster . workers [ allWorkers [ _id ] ] . send ( "\x14PINGPING" ) ;
2024-08-27 10:54:08 +02:00
setTimeout ( ( ) => {
2024-08-25 18:57:46 +02:00
if ( isWorkerHungUpBuff2 ) {
checkWorker ( callback , _id + 1 ) ;
} else {
goodWorkers . push ( allWorkers [ _id ] ) ;
checkWorker ( callback , _id + 1 ) ;
}
} , 250 ) ;
} else {
checkWorker ( callback , _id + 1 ) ;
}
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 18:57:46 +02:00
} catch ( err ) {
if ( cluster . workers [ allWorkers [ _id ] ] ) {
cluster . workers [ allWorkers [ _id ] ] . removeAllListeners ( "message" ) ;
2024-08-26 06:17:51 +02:00
addListenersToWorker ( cluster . workers [ allWorkers [ _id ] ] ) ;
2024-08-25 18:57:46 +02:00
}
checkWorker ( callback , _id + 1 ) ;
}
2024-08-26 06:19:53 +02:00
} ;
2024-08-27 10:54:08 +02:00
checkWorker ( ( ) => {
2024-08-26 06:17:51 +02:00
const wN = Math . floor ( Math . random ( ) * goodWorkers . length ) ; //Send a configuration saving message to a random worker.
2024-08-25 18:57:46 +02:00
try {
if ( cluster . workers [ goodWorkers [ wN ] ] ) {
isWorkerHungUpBuff2 = true ;
cluster . workers [ goodWorkers [ wN ] ] . on ( "message" , msgListener ) ;
cluster . workers [ goodWorkers [ wN ] ] . send ( "\x14SAVECONF" ) ;
}
} catch ( err ) {
if ( cluster . workers [ goodWorkers [ wN ] ] ) {
cluster . workers [ goodWorkers [ wN ] ] . removeAllListeners ( "message" ) ;
2024-08-26 06:17:51 +02:00
addListenersToWorker ( cluster . workers [ goodWorkers [ wN ] ] ) ;
2024-08-25 18:57:46 +02:00
}
2024-08-26 06:19:53 +02:00
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` There was a problem while saving configuration file. Reason: ${ err . message } ` ,
2024-08-26 06:19:53 +02:00
) ;
2024-08-25 18:57:46 +02:00
}
} ) ;
} , 300000 ) ;
2024-08-26 06:17:51 +02:00
}
2024-08-25 19:28:20 +02:00
2024-08-25 18:57:46 +02:00
if ( ! cluster . isPrimary && cluster . isPrimary !== undefined ) {
2024-08-27 10:54:08 +02:00
process . on ( "message" , ( line ) => {
2024-08-25 18:57:46 +02:00
try {
if ( line == "" ) {
// Does Nothing
process . send ( "\x12END" ) ;
2024-08-26 06:17:51 +02:00
} else if ( line == "\x14SAVECONF" ) {
2024-08-25 18:57:46 +02:00
// Save configuration file
try {
saveConfig ( ) ;
process . send ( "\x12SAVEGOOD" ) ;
} catch ( err ) {
process . send ( "\x12SAVEERR" + err . message ) ;
}
process . send ( "\x12END" ) ;
2024-08-26 06:17:51 +02:00
} else if ( line == "\x14KILLPING" ) {
2024-08-26 06:11:19 +02:00
process . send ( "\x12KILLOK" ) ;
process . send ( "\x12END" ) ;
2024-08-26 06:17:51 +02:00
} else if ( line == "\x14PINGPING" ) {
process . send ( "\x12PINGOK" ) ;
process . send ( "\x12END" ) ;
} else if ( line == "\x14KILLREQ" ) {
2024-08-26 06:11:19 +02:00
if ( process . reqcounter - reqcounterKillReq < 2 ) {
2024-08-25 18:57:46 +02:00
process . send ( "\x12KILLTERMMSG" ) ;
2024-08-26 06:11:19 +02:00
process . nextTick ( ( ) => {
commands . stop ( [ ] , ( ) => { } ) ;
} ) ;
2024-08-25 18:57:46 +02:00
} else {
2024-08-26 06:11:19 +02:00
reqcounterKillReq = process . reqcounter ;
2024-08-25 18:57:46 +02:00
}
2024-08-26 06:32:54 +02:00
} else if ( line [ 0 ] == "\x14" ) {
2024-08-26 06:19:27 +02:00
// Discard unrecognized control messages
2024-08-26 06:11:19 +02:00
} else if (
commands [ line . split ( " " ) [ 0 ] ] !== undefined &&
commands [ line . split ( " " ) [ 0 ] ] !== null
) {
2024-08-25 18:57:46 +02:00
var argss = line . split ( " " ) ;
var command = argss . shift ( ) ;
2024-08-25 20:18:35 +02:00
commands [ command ] ( argss , ( msg ) => process . send ( msg ) ) ;
2024-08-25 18:57:46 +02:00
process . send ( "\x12END" ) ;
} else {
2024-08-26 19:21:52 +02:00
process . send ( ` Unrecognized command " ${ line . split ( " " ) [ 0 ] } ". ` ) ;
2024-08-25 18:57:46 +02:00
process . send ( "\x12END" ) ;
}
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 18:57:46 +02:00
} catch ( err ) {
if ( line != "" ) {
2024-08-27 11:28:24 +02:00
process . send ( ` Can't execute command " ${ line . split ( " " ) [ 0 ] } ". ` ) ;
2024-08-25 18:57:46 +02:00
process . send ( "\x12END" ) ;
}
}
} ) ;
} else {
2024-08-25 20:18:35 +02:00
const rla = readline . createInterface ( {
2024-08-25 18:57:46 +02:00
input : process . stdin ,
output : process . stdout ,
2024-08-26 06:11:19 +02:00
prompt : "" ,
2024-08-25 18:57:46 +02:00
} ) ;
rla . prompt ( ) ;
2024-08-27 10:54:08 +02:00
rla . on ( "line" , ( line ) => {
2024-08-25 18:57:46 +02:00
line = line . trim ( ) ;
2024-08-25 20:18:35 +02:00
const argss = line . split ( " " ) ;
const command = argss . shift ( ) ;
2024-08-25 18:57:46 +02:00
if ( line != "" ) {
if ( cluster . isPrimary !== undefined ) {
var allWorkers = Object . keys ( cluster . workers ) ;
2024-08-26 06:11:19 +02:00
if ( command == "block" )
commands . block ( argss , serverconsole . climessage ) ;
if ( command == "unblock" )
commands . unblock ( argss , serverconsole . climessage ) ;
2024-08-25 18:57:46 +02:00
if ( command == "restart" ) {
var stopError = false ;
exiting = true ;
for ( var i = 0 ; i < allWorkers . length ; i ++ ) {
try {
if ( cluster . workers [ allWorkers [ i ] ] ) {
cluster . workers [ allWorkers [ i ] ] . kill ( ) ;
}
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 18:57:46 +02:00
} catch ( err ) {
stopError = true ;
}
}
2024-08-26 06:11:19 +02:00
if ( stopError )
serverconsole . climessage (
2024-08-26 19:21:52 +02:00
` Some ${ name } workers might not be stopped. ` ,
2024-08-26 06:11:19 +02:00
) ;
2024-08-25 18:57:46 +02:00
SVRJSInitialized = false ;
closedMaster = true ;
workersToFork = getWorkerCountToFork ( ) ;
2024-08-27 10:54:08 +02:00
forkWorkers ( workersToFork , ( ) => {
2024-08-25 18:57:46 +02:00
SVRJSInitialized = true ;
exiting = false ;
2024-08-26 19:21:52 +02:00
serverconsole . climessage ( ` ${ name } workers restarted. ` ) ;
2024-08-25 18:57:46 +02:00
} ) ;
return ;
}
if ( command == "stop" ) {
exiting = true ;
allWorkers = Object . keys ( cluster . workers ) ;
}
2024-08-26 06:11:19 +02:00
allWorkers . forEach ( ( clusterID ) => {
2024-08-25 18:57:46 +02:00
try {
if ( cluster . workers [ clusterID ] ) {
cluster . workers [ clusterID ] . on ( "message" , msgListener ) ;
cluster . workers [ clusterID ] . send ( line ) ;
}
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 18:57:46 +02:00
} catch ( err ) {
if ( cluster . workers [ clusterID ] ) {
cluster . workers [ clusterID ] . removeAllListeners ( "message" ) ;
2024-08-25 20:18:35 +02:00
addListenersToWorker ( cluster . workers [ clusterID ] ) ;
2024-08-25 18:57:46 +02:00
}
2024-08-26 19:23:52 +02:00
serverconsole . climessage ( ` Can't run command " ${ command } ". ` ) ;
2024-08-25 18:57:46 +02:00
}
} ) ;
if ( command == "stop" ) {
2024-08-27 10:54:08 +02:00
setTimeout ( ( ) => {
2024-08-25 20:18:35 +02:00
commands [ command ] ( argss , serverconsole . climessage ) ;
2024-08-25 18:57:46 +02:00
} , 50 ) ;
}
} else {
try {
2024-08-25 20:18:35 +02:00
commands [ command ] ( argss , serverconsole . climessage ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 18:57:46 +02:00
} catch ( err ) {
2024-08-26 06:11:19 +02:00
serverconsole . climessage (
'Unrecognized command "' + command + '".' ,
) ;
2024-08-25 18:57:46 +02:00
}
}
}
rla . prompt ( ) ;
} ) ;
2024-08-25 20:18:35 +02:00
}
2024-08-25 18:57:46 +02:00
if ( cluster . isPrimary || cluster . isPrimary === undefined ) {
// Cluster forking code
if ( cluster . isPrimary !== undefined && init ) {
workersToFork = getWorkerCountToFork ( ) ;
2024-08-27 10:54:08 +02:00
forkWorkers ( workersToFork , ( ) => {
2024-08-25 18:57:46 +02:00
SVRJSInitialized = true ;
} ) ;
// Hangup check and restart
2024-08-27 10:54:08 +02:00
setInterval ( ( ) => {
2024-08-25 18:57:46 +02:00
if ( ! closedMaster && ! exiting ) {
2024-08-25 19:45:02 +02:00
let chksocket = { } ;
2024-08-25 19:46:17 +02:00
if (
process . serverConfig . secure &&
process . serverConfig . disableNonEncryptedServer
) {
chksocket = https
. get (
{
hostname :
typeof process . serverConfig . sport == "number" &&
sListenAddress
? sListenAddress
: "localhost" ,
port :
typeof process . serverConfig . sport == "number"
? process . serverConfig . sport
: undefined ,
socketPath :
typeof process . serverConfig . sport == "number"
? undefined
: process . serverConfig . sport ,
headers : {
"X-SVR-JS-From-Main-Thread" : "true" ,
"User-Agent" : generateServerString ( true ) ,
} ,
timeout : 1620 ,
rejectUnauthorized : false ,
} ,
2024-08-27 10:54:08 +02:00
( res ) => {
2024-08-25 19:46:17 +02:00
chksocket . removeAllListeners ( "timeout" ) ;
res . destroy ( ) ;
2024-08-27 10:54:08 +02:00
res . on ( "data" , ( ) => { } ) ;
res . on ( "end" , ( ) => { } ) ;
2024-08-25 19:46:17 +02:00
crashed = false ;
} ,
)
2024-08-27 10:54:08 +02:00
. on ( "error" , ( ) => {
2024-08-25 19:46:17 +02:00
if ( ! exiting ) {
if ( ! crashed ) SVRJSFork ( ) ;
else crashed = false ;
}
} )
2024-08-27 10:54:08 +02:00
. on ( "timeout" , ( ) => {
2024-08-25 19:46:17 +02:00
if ( ! exiting ) SVRJSFork ( ) ;
crashed = true ;
} ) ;
} else if (
( process . serverConfig . enableHTTP2 == undefined
? false
: process . serverConfig . enableHTTP2 ) &&
! process . serverConfig . secure
) {
2024-08-25 18:57:46 +02:00
// It doesn't support through Unix sockets or Windows named pipes
2024-08-25 19:46:17 +02:00
var address = (
typeof process . serverConfig . port == "number" && listenAddress
? listenAddress
: "localhost"
) . replace ( /\/@/g , "" ) ;
2024-08-25 18:57:46 +02:00
if ( address . indexOf ( ":" ) > - 1 ) {
address = "[" + address + "]" ;
}
2024-08-25 19:46:17 +02:00
var connection = http2 . connect (
"http://" +
address +
":" +
process . serverConfig . port . toString ( ) ,
) ;
2024-08-27 10:54:08 +02:00
connection . on ( "error" , ( ) => {
2024-08-25 18:57:46 +02:00
if ( ! exiting ) {
if ( ! crashed ) SVRJSFork ( ) ;
else crashed = false ;
}
} ) ;
2024-08-27 10:54:08 +02:00
connection . setTimeout ( 1620 , ( ) => {
2024-08-25 18:57:46 +02:00
if ( ! exiting ) SVRJSFork ( ) ;
crashed = true ;
} ) ;
chksocket = connection . request ( {
":path" : "/" ,
"x-svr-js-from-main-thread" : "true" ,
2024-08-25 19:46:17 +02:00
"user-agent" : generateServerString ( true ) ,
2024-08-25 18:57:46 +02:00
} ) ;
2024-08-27 10:54:08 +02:00
chksocket . on ( "response" , ( ) => {
2024-08-25 18:57:46 +02:00
connection . close ( ) ;
crashed = false ;
} ) ;
2024-08-27 10:54:08 +02:00
chksocket . on ( "error" , ( ) => {
2024-08-25 18:57:46 +02:00
if ( ! exiting ) {
if ( ! crashed ) SVRJSFork ( ) ;
else crashed = false ;
}
} ) ;
} else {
2024-08-25 19:46:17 +02:00
chksocket = http
. get (
{
hostname :
typeof process . serverConfig . port == "number" &&
listenAddress
? listenAddress
: "localhost" ,
port :
typeof process . serverConfig . port == "number"
? process . serverConfig . port
: undefined ,
socketPath :
typeof process . serverConfig . port == "number"
? undefined
: process . serverConfig . port ,
headers : {
"X-SVR-JS-From-Main-Thread" : "true" ,
"User-Agent" : generateServerString ( true ) ,
} ,
timeout : 1620 ,
} ,
2024-08-27 10:54:08 +02:00
( res ) => {
2024-08-25 19:46:17 +02:00
chksocket . removeAllListeners ( "timeout" ) ;
res . destroy ( ) ;
2024-08-27 10:54:08 +02:00
res . on ( "data" , ( ) => { } ) ;
res . on ( "end" , ( ) => { } ) ;
2024-08-25 19:46:17 +02:00
crashed = false ;
} ,
)
2024-08-27 10:54:08 +02:00
. on ( "error" , ( ) => {
2024-08-25 19:46:17 +02:00
if ( ! exiting ) {
if ( ! crashed ) SVRJSFork ( ) ;
else crashed = false ;
}
} )
2024-08-27 10:54:08 +02:00
. on ( "timeout" , ( ) => {
2024-08-25 19:46:17 +02:00
if ( ! exiting ) SVRJSFork ( ) ;
crashed = true ;
} ) ;
2024-08-25 18:57:46 +02:00
}
}
2024-08-25 19:45:02 +02:00
} , 4550 ) ;
2024-08-25 18:57:46 +02:00
// Termination of unused good workers
2024-08-26 06:11:19 +02:00
if (
! process . serverConfig . disableUnusedWorkerTermination &&
cluster . isPrimary !== undefined
) {
2024-08-27 10:54:08 +02:00
setTimeout ( ( ) => {
setInterval ( ( ) => {
2024-08-25 18:57:46 +02:00
if ( ! closedMaster && ! exiting ) {
2024-08-26 06:11:19 +02:00
const allWorkers = Object . keys ( cluster . workers ) ;
2024-08-25 18:57:46 +02:00
2024-08-26 06:11:19 +02:00
let minWorkers = 0 ;
2024-08-25 18:57:46 +02:00
minWorkers = Math . ceil ( workersToFork * 0.625 ) ;
if ( minWorkers < 2 ) minWorkers = 2 ;
if ( minWorkers > 12 ) minWorkers = 12 ;
2024-08-26 06:11:19 +02:00
let goodWorkers = [ ] ;
2024-08-25 18:57:46 +02:00
2024-08-26 06:11:19 +02:00
const checkWorker = ( callback , _id ) => {
2024-08-25 18:57:46 +02:00
if ( typeof _id === "undefined" ) _id = 0 ;
if ( _id >= allWorkers . length ) {
callback ( ) ;
return ;
}
try {
if ( cluster . workers [ allWorkers [ _id ] ] ) {
isWorkerHungUpBuff = true ;
2024-08-26 06:11:19 +02:00
cluster . workers [ allWorkers [ _id ] ] . on (
"message" ,
msgListener ,
) ;
2024-08-25 18:57:46 +02:00
cluster . workers [ allWorkers [ _id ] ] . send ( "\x14KILLPING" ) ;
2024-08-27 10:54:08 +02:00
setTimeout ( ( ) => {
2024-08-25 18:57:46 +02:00
if ( isWorkerHungUpBuff ) {
checkWorker ( callback , _id + 1 ) ;
} else {
goodWorkers . push ( allWorkers [ _id ] ) ;
checkWorker ( callback , _id + 1 ) ;
}
} , 250 ) ;
} else {
checkWorker ( callback , _id + 1 ) ;
}
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 18:57:46 +02:00
} catch ( err ) {
if ( cluster . workers [ allWorkers [ _id ] ] ) {
2024-08-26 06:11:19 +02:00
cluster . workers [ allWorkers [ _id ] ] . removeAllListeners (
"message" ,
) ;
addListenersToWorker ( cluster . workers [ allWorkers [ _id ] ] ) ;
2024-08-25 18:57:46 +02:00
}
checkWorker ( callback , _id + 1 ) ;
}
2024-08-26 06:11:19 +02:00
} ;
checkWorker ( ( ) => {
2024-08-25 18:57:46 +02:00
if ( goodWorkers . length > minWorkers ) {
2024-08-26 06:11:19 +02:00
const wN = Math . floor ( Math . random ( ) * goodWorkers . length ) ;
2024-08-25 18:57:46 +02:00
if ( wN == goodWorkers . length ) return ;
try {
if ( cluster . workers [ goodWorkers [ wN ] ] ) {
isWorkerHungUpBuff = true ;
2024-08-26 06:11:19 +02:00
cluster . workers [ goodWorkers [ wN ] ] . on (
"message" ,
msgListener ,
) ;
2024-08-25 18:57:46 +02:00
cluster . workers [ goodWorkers [ wN ] ] . send ( "\x14KILLREQ" ) ;
}
} catch ( err ) {
if ( cluster . workers [ goodWorkers [ wN ] ] ) {
2024-08-26 06:11:19 +02:00
cluster . workers [ goodWorkers [ wN ] ] . removeAllListeners (
"message" ,
) ;
addListenersToWorker ( cluster . workers [ goodWorkers [ wN ] ] ) ;
2024-08-25 18:57:46 +02:00
}
2024-08-26 06:11:19 +02:00
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` There was a problem while terminating unused worker process. Reason: ${ err . message } ` ,
2024-08-26 06:11:19 +02:00
) ;
2024-08-25 18:57:46 +02:00
}
}
} ) ;
}
} , 300000 ) ;
} , 2000 ) ;
2024-08-26 06:11:19 +02:00
}
2024-08-25 18:57:46 +02:00
}
}
2024-08-25 19:28:20 +02:00
}
2024-08-25 17:51:25 +02:00
}
2024-08-23 21:34:58 +02:00
2024-08-25 16:23:16 +02:00
// Process event listeners
if ( cluster . isPrimary || cluster . isPrimary === undefined ) {
// Crash handler
function crashHandlerMaster ( err ) {
2024-08-26 19:21:52 +02:00
serverconsole . locerrmessage ( ` ${ name } main process just crashed!!! ` ) ;
2024-08-25 16:23:16 +02:00
serverconsole . locerrmessage ( "Stack:" ) ;
2024-08-25 16:25:25 +02:00
serverconsole . locerrmessage (
err . stack ? generateErrorStack ( err ) : String ( err ) ,
) ;
2024-08-25 16:35:19 +02:00
process . exit ( err . errno !== undefined ? err . errno : 1 ) ;
2024-08-25 16:23:16 +02:00
}
process . on ( "uncaughtException" , crashHandlerMaster ) ;
process . on ( "unhandledRejection" , crashHandlerMaster ) ;
2024-08-27 10:54:08 +02:00
process . on ( "exit" , ( code ) => {
2024-08-25 16:23:16 +02:00
try {
2024-08-26 06:20:23 +02:00
if ( ! configJSONRErr && ! configJSONPErr ) {
2024-08-25 16:23:16 +02:00
saveConfig ( ) ;
2024-08-26 06:20:23 +02:00
}
2024-08-25 16:23:16 +02:00
} catch ( err ) {
2024-08-25 16:25:25 +02:00
serverconsole . locwarnmessage (
2024-08-26 19:21:52 +02:00
` There was a problem while saving configuration file. Reason: ${ err . message } ` ,
2024-08-25 16:25:25 +02:00
) ;
2024-08-25 16:23:16 +02:00
}
try {
deleteFolderRecursive ( process . dirname + "/temp" ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 16:23:16 +02:00
} catch ( err ) {
// Error!
}
try {
fs . mkdirSync ( process . dirname + "/temp" ) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 16:23:16 +02:00
} catch ( err ) {
// Error!
}
2024-08-25 16:25:25 +02:00
if (
process . isBun &&
process . versions . bun &&
process . versions . bun [ 0 ] == "0"
) {
2024-08-25 16:23:16 +02:00
try {
2024-08-25 16:25:25 +02:00
fs . writeFileSync (
process . dirname + "/temp/serverSideScript.js" ,
"// Placeholder server-side JavaScript to workaround Bun bug.\r\n" ,
) ;
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 16:23:16 +02:00
} catch ( err ) {
// Error!
}
}
serverconsole . locmessage ( "Server closed with exit code: " + code ) ;
} ) ;
2024-08-27 10:54:08 +02:00
process . on ( "warning" , ( warning ) => {
2024-08-25 16:23:16 +02:00
serverconsole . locwarnmessage ( warning . message ) ;
if ( generateErrorStack ( warning ) ) {
serverconsole . locwarnmessage ( "Stack:" ) ;
serverconsole . locwarnmessage ( generateErrorStack ( warning ) ) ;
}
} ) ;
2024-08-27 10:54:08 +02:00
process . on ( "SIGINT" , ( ) => {
2024-08-25 16:23:16 +02:00
if ( cluster . isPrimary !== undefined ) {
exiting = true ;
2024-08-25 20:45:40 +02:00
const allWorkers = Object . keys ( cluster . workers ) ;
for ( var i = 0 ; i < allWorkers . length ; i ++ ) {
2024-08-25 16:23:16 +02:00
try {
if ( cluster . workers [ allWorkers [ i ] ] ) {
cluster . workers [ allWorkers [ i ] ] . send ( "stop" ) ;
}
2024-08-27 11:28:24 +02:00
// eslint-disable-next-line no-unused-vars
2024-08-25 16:23:16 +02:00
} catch ( err ) {
// Worker will crash with EPIPE anyway.
}
2024-08-25 20:45:40 +02:00
}
2024-08-25 16:23:16 +02:00
}
serverconsole . locmessage ( "Server terminated using SIGINT" ) ;
process . exit ( ) ;
} ) ;
} else {
// Crash handler
function crashHandler ( err ) {
2024-08-26 19:21:52 +02:00
serverconsole . locerrmessage ( ` ${ name } worker just crashed!!! ` ) ;
2024-08-25 16:23:16 +02:00
serverconsole . locerrmessage ( "Stack:" ) ;
2024-08-25 16:25:25 +02:00
serverconsole . locerrmessage (
err . stack ? generateErrorStack ( err ) : String ( err ) ,
) ;
2024-08-25 16:35:19 +02:00
process . exit ( err . errno !== undefined ? err . errno : 1 ) ;
2024-08-25 16:23:16 +02:00
}
process . on ( "uncaughtException" , crashHandler ) ;
process . on ( "unhandledRejection" , crashHandler ) ;
// Warning handler
2024-08-27 10:54:08 +02:00
process . on ( "warning" , ( warning ) => {
2024-08-25 16:23:16 +02:00
serverconsole . locwarnmessage ( warning . message ) ;
if ( warning . stack ) {
serverconsole . locwarnmessage ( "Stack:" ) ;
serverconsole . locwarnmessage ( generateErrorStack ( warning ) ) ;
}
} ) ;
2024-08-25 16:25:25 +02:00
}
2024-08-25 17:51:25 +02:00
2024-08-26 06:35:25 +02:00
// Start SVR.JS!
try {
start ( true ) ;
} catch ( err ) {
2024-08-26 19:21:52 +02:00
serverconsole . locerrmessage ( ` There was a problem starting ${ name } !!! ` ) ;
2024-08-26 06:35:25 +02:00
serverconsole . locerrmessage ( "Stack:" ) ;
serverconsole . locerrmessage ( generateErrorStack ( err ) ) ;
2024-08-27 10:54:08 +02:00
setTimeout ( ( ) => {
2024-08-26 06:35:25 +02:00
process . exit ( err . errno !== undefined ? err . errno : 1 ) ;
} , 10 ) ;
2024-08-26 07:06:02 +02:00
}