2024-02-16 19:58:44 +01:00
var https = require ( "https" ) ;
var os = require ( "os" ) ;
var http = require ( "http" ) ;
var url = require ( "url" ) ;
var fs = require ( "fs" ) ;
var path = require ( "path" ) ;
var net = require ( "net" ) ;
var stream = require ( "stream" ) ;
var EventEmitter = require ( "events" ) ;
var version = "UNKNOWN" ;
try {
version = JSON . parse ( fs . readFileSync ( _ _dirname + "/mod.info" ) ) . version ;
} catch ( ex ) {
// Can't determine version
}
var configJSONS = { } ;
try {
configJSONS = JSON . parse ( fs . readFileSync ( _ _dirname + "/../../../config.json" ) ) ; // Read configuration JSON
} catch ( ex ) {
//GreenRhombus will not care about configJSONS in SVR.JS 3.x and newer
//SVR.JS 2.x and older will fail to start with broken configuration file anyway...
}
var fastcgiConf = { } ;
try {
fastcgiConf = JSON . parse ( fs . readFileSync ( _ _dirname + "/../../../greenrhombus-config.json" ) ) ;
} catch ( ex ) {
// Use defaults
}
var scriptExts = [ ] ;
try {
scriptExts = JSON . parse ( fs . readFileSync ( _ _dirname + "/../../../greenrhombus-scriptexts.json" ) ) ;
} catch ( ex ) {
}
function createFastCGIHandler ( options ) {
var requestID = options . requestID ;
if ( ! requestID ) requestID = 1 ;
var BEGIN _REQUEST = 1 ;
var ABORT _REQUEST = 2 ;
var END _REQUEST = 3 ;
var PARAMS = 4 ;
var STDIN = 5 ;
var STDOUT = 6 ;
var STDERR = 7 ;
var DATA = 8 ;
var GET _VALUES = 9 ;
var GET _VALUES _RESULT = 10 ;
var UNKNOWN _TYPE = 11 ;
var RESPONDER = 1 ;
var AUTHORIZER = 2 ;
var FILTER = 3 ;
// Response codes
var REQUEST _COMPLETE = 0 ;
var CANT _MPX _CONN = 1 ;
var OVERLOADED = 2 ;
var UNKNOWN _ROLE = 3 ;
function buildFastCGIPacket ( type , requestID , content ) {
var packet = Buffer . alloc ( content . length + ( content . length % 8 ) + 8 ) ;
packet . writeUInt8 ( 1 , 0 ) ; //version
packet . writeUInt8 ( type , 1 ) ; //type
packet . writeUInt16BE ( requestID , 2 ) ; //requestId
packet . writeUInt16BE ( content . length , 4 ) ; //contentLength
packet . writeUInt8 ( content . length % 8 , 6 ) ; //paddingLength
packet . writeUInt8 ( 0 , 7 ) ; //reserved
Buffer . from ( content ) . copy ( packet , 8 ) ; //content
return packet ;
}
function parseFastCGIPacket ( packet ) {
var data = { } ;
data . fcgiVersion = packet . readUInt8 ( 0 ) ; //version
data . type = packet . readUInt8 ( 1 ) ; //type
data . requestID = packet . readUInt16BE ( 2 ) ; //requestId
data . content = packet . subarray ( 8 , 8 + packet . readUInt16BE ( 4 ) ) ; //contentLength
return data ;
}
function writeFastCGIPacket ( socket , type , requestID , content ) {
if ( content . length != 0 ) {
var contentSlices = Math . ceil ( content . length / 65535 ) ;
var chunk = { } ;
for ( var i = 0 ; i < contentSlices ; i ++ ) {
var chunkOffset = 65536 * i ;
var chunkSize = Math . min ( 65535 , content . length - chunkOffset ) ;
chunk = Buffer . alloc ( chunkSize ) ;
content . copy ( chunk , 0 , chunkOffset , chunkOffset + chunkSize ) ;
socket . write ( buildFastCGIPacket ( type , requestID , chunk ) ) ;
}
} else {
socket . write ( buildFastCGIPacket ( type , requestID , Buffer . alloc ( 0 ) ) ) ;
}
}
function buildNameValuePair ( name , value ) {
if ( typeof name == "undefined" || typeof value == "undefined" ) return Buffer . alloc ( 0 ) ;
name = String ( name ) ;
value = String ( value ) ;
var nameLength = name . length ;
var valueLength = value . length ;
var nameLengthBytes = nameLength > 127 ? 4 : 1 ;
var valueLengthBytes = valueLength > 127 ? 4 : 1 ;
var pair = Buffer . alloc ( nameLength + valueLength + nameLengthBytes + valueLengthBytes ) ;
//nameLength
if ( nameLengthBytes == 4 ) {
pair . writeUInt32BE ( Math . min ( 0xFFFFFFFF , 0x80000000 + nameLength ) , 0 ) ;
} else {
pair . writeUInt8 ( nameLength , 0 ) ;
}
//valueLength
if ( valueLengthBytes == 4 ) {
pair . writeUInt32BE ( Math . min ( 0xFFFFFFFF , 0x80000000 + valueLength ) , nameLengthBytes ) ;
} else {
pair . writeUInt8 ( valueLength , nameLengthBytes ) ;
}
Buffer . from ( name ) . copy ( pair , nameLengthBytes + valueLengthBytes ) ; //nameData
Buffer . from ( value ) . copy ( pair , nameLengthBytes + valueLengthBytes + nameLength ) ; //valueData
return pair ;
}
function fastCGISocketHandler ( chunk ) {
var chunkIndex = 0 ;
2024-02-22 00:52:43 +01:00
2024-02-16 19:58:44 +01:00
while ( chunkIndex < chunk . length || ( headerIndex == 8 && bodyIndex == packetBody . length && paddingLength == 0 ) ) {
if ( headerIndex < 8 ) {
chunk . copy ( packetHeader , headerIndex , chunkIndex , Math . min ( chunk . length , chunkIndex + 8 - headerIndex ) ) ;
var ic = Math . min ( chunk . length - chunkIndex , 8 - headerIndex ) ;
headerIndex += ic ;
chunkIndex += ic ;
if ( headerIndex == 8 ) {
packetBody = Buffer . alloc ( packetHeader . readUInt16BE ( 4 ) ) ;
paddingLength = packetHeader . readUInt8 ( 6 ) ;
}
} else if ( headerIndex == 8 && bodyIndex < packetBody . length ) {
chunk . copy ( packetBody , bodyIndex , chunkIndex , Math . min ( chunk . length , chunkIndex + packetBody . length - bodyIndex ) ) ;
var ic = Math . min ( chunk . length - chunkIndex , packetBody . length - bodyIndex ) ;
bodyIndex += ic ;
chunkIndex += ic ;
} else if ( headerIndex == 8 && bodyIndex == packetBody . length && paddingIndex <= paddingLength ) {
var ic = Math . min ( chunk . length - chunkIndex , paddingLength - paddingIndex ) ;
paddingIndex += ic ;
chunkIndex += ic ;
if ( paddingIndex == paddingLength ) {
headerIndex = 0 ;
bodyIndex = 0 ;
paddingIndex = 0 ;
var packet = Buffer . alloc ( 8 + packetBody . length + paddingLength ) ;
packetHeader . copy ( packet ) ;
packetBody . copy ( packet , 8 ) ;
processFastCGIPacket ( packet ) ;
}
}
}
}
function processFastCGIPacket ( packet ) {
var processedPacket = parseFastCGIPacket ( packet ) ;
if ( processedPacket . requestID != requestID ) return ; //Drop the packet
if ( processedPacket . type == STDOUT ) {
2024-02-21 02:00:21 +01:00
try {
2024-02-22 00:52:43 +01:00
if ( processedPacket . content . length > 0 ) stdoutPush ( processedPacket . content ) ;
2024-02-21 02:00:21 +01:00
} catch ( err ) {
//STDOUT will be lost instead of crashing the server
}
2024-02-16 19:58:44 +01:00
} else if ( processedPacket . type == STDERR ) {
2024-02-20 00:10:19 +01:00
try {
if ( processedPacket . content . length > 0 ) emulatedStderr . push ( processedPacket . content ) ;
} catch ( err ) {
//STDERR will be lost anyway...
}
2024-02-16 19:58:44 +01:00
} else if ( processedPacket . type == END _REQUEST && processedPacket . content . length > 5 ) {
2024-02-21 02:00:21 +01:00
if ( typeof socket !== "undefined" ) {
socket . removeListener ( "data" , fastCGISocketHandler ) ;
processFastCGIPacket = function ( ) { } ;
2024-05-01 16:52:29 +02:00
try {
socket . end ( ) ; //Fixes connection not closing properly in Bun
} catch ( err ) {
//It is already closed
}
2024-02-21 02:00:21 +01:00
}
2024-02-16 19:58:44 +01:00
var appStatus = processedPacket . content . readUInt32BE ( 0 ) ;
var protocolStatus = processedPacket . content . readUInt8 ( 4 ) ;
if ( protocolStatus != REQUEST _COMPLETE ) {
var err = new Error ( "Unknown error" ) ;
if ( protocolStatus == OVERLOADED ) {
err = new Error ( "FastCGI server overloaded" ) ;
err . code = "EMFILE" ;
err . errno = 24 ;
} else if ( protocolStatus == UNKNOWN _ROLE ) {
err = new Error ( "Role not supported by the FastCGI application" ) ;
} else if ( protocolStatus == CANT _MPX _CONN ) {
err = new Error ( "Multiplexed connections not supported by the FastCGI application" ) ;
}
2024-02-22 00:52:43 +01:00
stdoutPush ( null ) ;
2024-02-21 02:00:21 +01:00
if ( emulatedStdout . _readableState && emulatedStdout . _readableState . flowing !== null && ! emulatedStdout . endEmitted ) {
2024-02-16 22:02:21 +01:00
emulatedStdout . on ( "end" , function ( ) {
2024-02-20 00:10:19 +01:00
emulatedStderr . push ( null ) ;
2024-02-16 22:02:21 +01:00
eventEmitter . emit ( "error" , err ) ;
} ) ;
} else {
2024-02-20 00:10:19 +01:00
emulatedStderr . push ( null ) ;
2024-02-16 22:02:21 +01:00
eventEmitter . emit ( "error" , err ) ;
}
2024-02-16 19:58:44 +01:00
} else {
2024-02-22 00:52:43 +01:00
stdoutPush ( null ) ;
2024-02-21 02:00:21 +01:00
if ( emulatedStdout . _readableState && emulatedStdout . _readableState . flowing !== null && ! emulatedStdout . endEmitted ) {
2024-02-16 22:02:21 +01:00
emulatedStdout . on ( "end" , function ( ) {
2024-02-20 00:10:19 +01:00
emulatedStderr . push ( null ) ;
2024-02-16 22:02:21 +01:00
eventEmitter . emit ( "exit" , appStatus , null ) ;
} ) ;
} else {
2024-02-20 00:10:19 +01:00
emulatedStderr . push ( null ) ;
2024-02-16 22:02:21 +01:00
eventEmitter . emit ( "exit" , appStatus , null ) ;
}
2024-02-16 19:58:44 +01:00
}
}
}
var eventEmitter = new EventEmitter ( ) ;
var packetHeader = Buffer . alloc ( 8 ) ;
var packetBody = { } ;
var paddingLength = 0 ;
var headerIndex = 0 ;
var bodyIndex = 0 ;
var paddingIndex = 0 ;
var emulatedStdin = new stream . Writable ( {
write : function ( chunk , encoding , callback ) {
try {
if ( chunk . length != 0 ) {
writeFastCGIPacket ( socket , STDIN , requestID , chunk ) ;
}
callback ( null ) ;
} catch ( err ) {
callback ( err ) ;
}
} ,
final : function ( callback ) {
try {
writeFastCGIPacket ( socket , STDIN , requestID , Buffer . alloc ( 0 ) ) ;
} catch ( err ) {
//writing failed
}
callback ( ) ;
}
} ) ;
2024-02-22 00:52:43 +01:00
function stdoutPush ( data ) {
if ( data === null ) {
stdoutToEnd = true ;
} else {
stdoutBuffer = Buffer . concat ( [ stdoutBuffer , Buffer . from ( data ) ] ) ;
}
var hpLength = hp . length ;
for ( var i = 0 ; i < hpLength ; i ++ ) {
var func = hp . shift ( ) ;
if ( func ) func ( ) ;
}
emulatedStdout . resume ( ) ;
}
var zeroed = false ;
var stdoutBuffer = Buffer . alloc ( 0 ) ;
var stdoutToEnd = false ;
var hp = [ ] ;
2024-02-16 19:58:44 +01:00
var emulatedStdout = new stream . Readable ( {
2024-02-22 00:52:43 +01:00
read : function ( n ) {
var s = this ;
var handler = function ( ) {
if ( stdoutBuffer . length == 0 ) {
if ( ! stdoutToEnd ) {
hp . push ( handler ) ;
s . pause ( ) ;
} else {
s . push ( null ) ;
}
} else {
var bytesToPush = Math . min ( stdoutBuffer . length , n ) ;
var bufferToPush = stdoutBuffer . subarray ( 0 , bytesToPush ) ;
stdoutBuffer = stdoutBuffer . subarray ( bytesToPush ) ;
s . push ( bufferToPush ) ;
if ( stdoutBuffer . length == 0 && ! stdoutToEnd ) s . pause ( ) ;
}
} ;
if ( n != 0 ) handler ( ) ;
}
2024-02-16 19:58:44 +01:00
} ) ;
2024-02-22 00:52:43 +01:00
emulatedStdout . pause ( ) ; //Reduce backpressure
2024-02-16 19:58:44 +01:00
var emulatedStderr = new stream . Readable ( {
read : function ( ) { }
} ) ;
function init ( ) {
//Begin the request
var beginPacket = Buffer . alloc ( 8 ) ;
beginPacket . writeUInt16BE ( RESPONDER , 0 ) ; //FastCGI responder
beginPacket . writeUInt8 ( 0 , 2 ) ; //Don't keep alive
writeFastCGIPacket ( socket , BEGIN _REQUEST , requestID , beginPacket ) ;
//Environment variables
var envPacket = Buffer . alloc ( 0 ) ;
Object . keys ( options . env ) . forEach ( function ( key ) {
envPacket = Buffer . concat ( [ envPacket , buildNameValuePair ( key , options . env [ key ] ) ] ) ;
} ) ;
if ( envPacket . length > 0 ) writeFastCGIPacket ( socket , PARAMS , requestID , envPacket ) ;
writeFastCGIPacket ( socket , PARAMS , requestID , Buffer . alloc ( 0 ) ) ;
}
eventEmitter . stdin = emulatedStdin ;
eventEmitter . stdout = emulatedStdout ;
eventEmitter . stderr = emulatedStderr ;
eventEmitter . init = init ;
//Create socket
var socket = net . createConnection ( options , function ( ) {
eventEmitter . emit ( "connect" ) ;
} ) . on ( "error" , function ( err ) {
2024-02-22 00:52:43 +01:00
stdoutPush ( null ) ;
2024-02-16 19:58:44 +01:00
emulatedStderr . push ( null ) ;
2024-02-22 00:52:43 +01:00
eventEmitter . removeAllListeners ( "exit" ) ;
2024-02-16 19:58:44 +01:00
eventEmitter . emit ( "error" , err ) ;
} ) . on ( "data" , fastCGISocketHandler ) ;
2024-02-21 02:00:21 +01:00
eventEmitter . socket = socket ;
2024-02-16 19:58:44 +01:00
return eventEmitter ;
}
// Load default configuration
if ( fastcgiConf . path !== undefined ) fastcgiConf . path = fastcgiConf . path . replace ( /([^\/])\/+$/ , "$1" ) ;
if ( fastcgiConf . socketPath === undefined ) {
if ( fastcgiConf . host === undefined ) fastcgiConf . host = "localhost" ;
if ( fastcgiConf . port === undefined ) fastcgiConf . port = 4000 ;
}
var disableModExposeSupported = process . versions . svrjs && process . versions . svrjs . match ( /^(?:Nightly-|(?:[4-9]|[123][0-9])[0-9]*\.|3\.(?:[1-9][0-9]+\.|9\.(?:[1-9])|4\.(?:(?:[3-9]|[12][0-9])[0-9]+|29)))/i ) ;
function Mod ( ) { }
Mod . prototype . callback = function ( req , res , serverconsole , responseEnd , href , ext , uobject , search , defaultpage , users , page404 , head , foot , fd , elseCallback , configJSON , callServerError , getCustomHeaders , origHref , redirect , parsePostData , authUser ) {
return function ( ) {
if ( ! configJSON ) {
configJSON = configJSONS ;
}
function checkIfThereIsA401Rule ( ) {
var actually401 = false ;
function createRegex ( regex ) {
var regexObj = regex . split ( "/" ) ;
if ( regexObj . length == 0 ) throw new Error ( "Invalid regex!" ) ;
var modifiers = regexObj . pop ( ) ;
regexObj . shift ( ) ;
var searchString = regexObj . join ( "/" ) ;
return new RegExp ( searchString , modifiers ) ;
}
if ( configJSON . nonStandardCodes ) {
configJSON . nonStandardCodes . every ( function ( nonscode ) {
if ( nonscode . scode == 401 ) {
if ( nonscode . regex && ( req . url . match ( createRegex ( nonscode . regex ) ) || href . match ( createRegex ( nonscode . regex ) ) ) ) {
actually401 = true ;
return true ;
} else if ( nonscode . url && ( nonStandardCodes [ i ] . url == href || ( os . platform ( ) == "win32" && nonStandardCodes [ i ] . url . toLowerCase ( ) == href . toLowerCase ( ) ) ) ) {
actually401 = true ;
return true ;
}
}
return false ;
} ) ;
}
return actually401 ;
}
if ( ! getCustomHeaders ) {
var bheaders = JSON . parse ( JSON . stringify ( configJSON . customHeaders ) ) ;
} else {
var bheaders = getCustomHeaders ( ) ;
}
bheaders [ "Content-Type" ] = "text/html" ; // HTML output
if ( ! getCustomHeaders ) {
bheaders [ "Server" ] =
"SVR.JS/" +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
")" ; // Add Server header
}
var abheaders = JSON . parse ( JSON . stringify ( bheaders ) ) ;
var socket = { } ;
function executeFastCGI ( req , res , dh , nEnv ) {
// Function to execute FastCGI scripts
var env = JSON . parse ( JSON . stringify ( process . env ) ) ;
var nEnvKeys = Object . keys ( nEnv ) ;
for ( var i = 0 ; i < nEnvKeys . length ; i ++ ) {
env [ nEnvKeys [ i ] ] = nEnv [ nEnvKeys [ i ] ] ;
}
var buffer = "" ;
var stderr = "" ;
var headerendline = - 1 ;
var cned = false ;
var dataHandler = function ( data ) {
if ( ! cned ) buffer += data . toString ( "latin1" ) ;
var m = null ;
if ( ! cned ) m = buffer . match ( /(?:\r\n\r\n|\n\r\n\r|\n\n|\r\r)/ ) ;
if ( ! cned && m ) {
cned = true ;
eol = m [ 0 ] ;
headerendline = m . index ;
var bheaders = buffer . substr ( 0 , headerendline ) . split ( /(?:\r\n|\n\r|\n|\r)/ ) ;
var bheaderso = { } ;
if ( dh ) bheaderso = dh ;
var code = 200 ;
var msg = "OK" ;
if ( bheaders [ 0 ] . indexOf ( "HTTP/" ) == 0 ) {
var heada = bheaders . shift ( ) ;
var hso = heada . split ( " " ) ;
code = hso [ 1 ] ;
if ( hso [ 2 ] !== undefined ) msg = heada . split ( " " ) . splice ( 2 ) . join ( " " ) ;
} else if ( bheaders [ 0 ] . indexOf ( ":" ) == - 1 ) {
var heada = bheaders . shift ( ) ;
var hso = heada . split ( " " ) ;
if ( hso [ 0 ] . match ( /^[0-9]{3}$/ ) ) {
code = hso [ 0 ] ;
if ( hso [ 1 ] !== undefined ) msg = heada . split ( " " ) . splice ( 1 ) . join ( " " ) ;
}
}
var hasLocation = false ;
for ( var i = 0 ; i < bheaders . length ; i ++ ) {
var headerp = bheaders [ i ] . split ( ": " ) ;
var headern = headerp . shift ( ) ;
var headerv = headerp . join ( ": " ) ;
if ( headern . toLowerCase ( ) == "status" ) {
code = headerv . split ( " " ) [ 0 ] ;
if ( headerv . split ( " " ) [ 1 ] !== undefined ) msg = headerv . split ( " " ) . splice ( 1 ) . join ( " " ) ;
} else if ( headern . toLowerCase ( ) == "set-cookie" ) {
if ( ! bheaderso [ "Set-Cookie" ] ) bheaderso [ "Set-Cookie" ] = [ ] ;
bheaderso [ "Set-Cookie" ] . push ( headerv ) ;
} else {
if ( headern . toLowerCase ( ) == "location" ) hasLocation = true ;
bheaderso [ headern ] = headerv ;
}
}
if ( ( code < 300 || code > 399 ) && hasLocation ) {
code = 302 ;
msg = "Found" ;
}
try {
res . writeHead ( code , msg , bheaderso ) ;
2024-05-03 16:11:24 +02:00
res . write ( Buffer . from ( buffer . substr ( headerendline + eol . length ) , "latin1" ) ) ;
2024-02-16 19:58:44 +01:00
} catch ( ex ) {
handler . removeAllListeners ( "exit" ) ;
2024-02-22 00:52:43 +01:00
handler . stdout . removeAllListeners ( "data" ) ;
2024-02-16 19:58:44 +01:00
if ( ! callServerError ) {
res . writeHead ( 500 ) ;
res . end ( ex . stack ) ;
} else {
callServerError ( 500 , "GreenRhombus/" + version , ex ) ;
}
return ;
}
} else {
if ( cned && ! res . finished ) {
res . write ( data ) ;
handler . stdout . removeListener ( "data" , dataHandler ) ;
handler . stdout . pipe ( res , {
end : false
} ) ;
2024-02-22 00:52:43 +01:00
dataHandler = function ( ) { } ; //Prevent event listener memory leaks
2024-02-16 19:58:44 +01:00
}
}
} ;
var options = {
host : fastcgiConf . host ,
port : fastcgiConf . port ,
path : fastcgiConf . socketPath ,
env : env
} ;
2024-04-24 18:03:17 +02:00
var handler = createFastCGIHandler ( options ) ;
2024-02-16 19:58:44 +01:00
handler . on ( "error" , function ( error ) {
2024-02-18 15:15:47 +01:00
var errorcode = 0 ;
if ( error . code == "ENOTFOUND" || error . code == "EHOSTUNREACH" || error . code == "ECONNREFUSED" ) {
errorcode = 503 ;
} else if ( error . code == "EMFILE" ) {
errorcode = 429 ;
} else {
errorcode = 500 ;
}
2024-02-16 19:58:44 +01:00
if ( ! callServerError ) {
res . writeHead ( errorcode , {
"Content-Type" : "text/html" ,
"Server" : "GreenRhombus/" + version
} ) ;
res . end ( "<html><head></head><body><h1>GreenRhombus Error!</h1><p>Reason: " + error . message + "</p></body></html>" ) ;
} else {
callServerError ( errorcode , "GreenRhombus/" + version , error ) ;
}
} ) ;
2024-02-22 00:52:43 +01:00
res . prependListener ( "close" , function ( ) {
2024-02-21 02:00:21 +01:00
if ( handler . stdout ) handler . stdout . unpipe ( res ) ; //Prevent server crashes with write after the end
} ) ;
2024-02-22 00:52:43 +01:00
res . on ( "error" , function ( ) { } ) ; //Suppress response stream errors
2024-02-21 02:00:21 +01:00
function handlerConnection ( ) {
2024-02-16 19:58:44 +01:00
handler . stdout . on ( "data" , dataHandler ) ;
handler . stderr . on ( "data" , function ( data ) {
stderr += data . toString ( ) ;
} ) ;
2024-02-22 00:52:43 +01:00
handler . init ( ) ;
2024-02-16 19:58:44 +01:00
req . pipe ( handler . stdin ) ;
2024-02-16 22:02:21 +01:00
handler . on ( "exit" , function ( code , signal ) {
2024-02-16 19:58:44 +01:00
if ( ! cned && ( signal || code !== 0 ) ) {
var ex = new Error ( "Process execution failed!" + ( stderr ? " Reason: " + stderr . trim ( ) : "" ) ) ;
if ( ! callServerError ) {
res . writeHead ( 500 ) ;
res . end ( ex . stack ) ;
} else {
callServerError ( 500 , "GreenRhombus/" + version , ex ) ;
}
} else {
var preparedStderr = stderr . trim ( ) ;
if ( preparedStderr ) {
serverconsole . errmessage ( "There were FastCGI application errors:" ) ;
serverconsole . errmessage ( preparedStderr ) ;
}
2024-02-22 00:52:43 +01:00
handler . stdout . removeListener ( "data" , dataHandler ) ;
2024-02-16 19:58:44 +01:00
handler . stdout . unpipe ( res ) ; //Prevent server crashes with write after the end
2024-02-21 02:00:21 +01:00
if ( ! res . finished ) res . end ( ) ;
2024-02-16 19:58:44 +01:00
}
} ) ;
2024-02-21 02:00:21 +01:00
}
if ( typeof handler . socket . connecting == "undefined" || handler . socket . connecting ) handler . on ( "connect" , handlerConnection ) ;
else if ( ! handler . socket . destroyed ) handlerConnection ( ) ;
2024-02-16 19:58:44 +01:00
}
function executeFastCGIWithEnv ( a , b , req , res , pubip , port , software , dh , user , cPath ) {
// Function to set up environment variables and execute sCGI scripts
var nEnv = { } ;
if ( typeof user != "undefined" ) {
if ( user !== null ) {
if ( req . headers . authorization ) nEnv [ "AUTH_TYPE" ] = req . headers . authorization . split ( " " ) [ 0 ] ;
nEnv [ "REMOTE_USER" ] = user ;
}
} else if ( req . headers . authorization && ( typeof checkIfThereIsA401Rule == "undefined" || checkIfThereIsA401Rule ( ) ) ) {
nEnv [ "AUTH_TYPE" ] = req . headers . authorization . split ( " " ) [ 0 ] ;
if ( nEnv [ "AUTH_TYPE" ] == "Basic" ) {
var remoteCred = req . headers . authorization . split ( " " ) [ 1 ] ;
if ( ! remoteCred ) {
nEnv [ "REMOTE_USER" ] = "greenrhombus_cgi_invalid_user" ;
} else {
var remoteCredDecoded = Buffer . from ( remoteCred , "base64" ) . toString ( "utf8" ) ;
nEnv [ "REMOTE_USER" ] = remoteCredDecoded . split ( ":" ) [ 0 ] ;
}
} else {
nEnv [ "REMOTE_USER" ] = "svrjs_this_property_is_not_yet_supported_by_greenrhombus_cgi" ;
}
}
nEnv [ "QUERY_STRING" ] = req . url . split ( "?" ) [ 1 ] ;
if ( nEnv [ "QUERY_STRING" ] == undefined || nEnv [ "QUERY_STRING" ] == "undefined" ) nEnv [ "QUERY_STRING" ] = "" ;
nEnv [ "SERVER_SOFTWARE" ] = software ;
nEnv [ "SERVER_PROTOCOL" ] = "HTTP/" + req . httpVersion ;
if ( pubip && ( port !== null && port !== undefined ) ) {
nEnv [ "SERVER_PORT" ] = port ;
nEnv [ "SERVER_ADDR" ] = pubip . replace ( /^::ffff:/i , "" ) ;
if ( nEnv [ "SERVER_ADDR" ] . indexOf ( ":" ) != - 1 ) nEnv [ "SERVER_ADDR" ] = "[" + nEnv [ "SERVER_ADDR" ] + "]" ;
}
if ( configJSON . serverAdministratorEmail && configJSON . serverAdministratorEmail != "[no contact information]" ) {
nEnv [ "SERVER_ADMIN" ] = configJSON . serverAdministratorEmail ;
}
nEnv [ "SERVER_NAME" ] = req . headers . host ;
nEnv [ "DOCUMENT_ROOT" ] = process . cwd ( ) ;
nEnv [ "SCRIPT_NAME" ] = cPath ;
nEnv [ "SCRIPT_FILENAME" ] = ( process . cwd ( ) + ( require ( "os" ) . platform == "win32" ? cPath . replace ( /\//g , "\\" ) : cPath ) ) . replace ( ( require ( "os" ) . platform == "win32" ? /\\\\/g : /\/\//g ) , ( require ( "os" ) . platform == "win32" ? "\\" : "/" ) ) ;
nEnv [ "PATH_INFO" ] = decodeURIComponent ( b ) ;
nEnv [ "PATH_TRANSLATED" ] = b ? ( ( process . cwd ( ) + decodeURIComponent ( require ( "os" ) . platform == "win32" ? b . replace ( /\//g , "\\" ) : b ) ) . replace ( ( require ( "os" ) . platform == "win32" ? /\\\\/g : /\/\//g ) , ( require ( "os" ) . platform == "win32" ? "\\" : "/" ) ) ) : "" ;
nEnv [ "REQUEST_METHOD" ] = req . method ;
2024-11-21 00:19:14 +01:00
nEnv [ "REQUEST_URI" ] = ( ! origHref || origHref == href ) ? req . url : ( origHref + ( uobject . search ? ( ( uobject . search [ 0 ] == "?" ? "" : "?" ) + uobject . search ) : "" ) ) ;
2024-02-16 19:58:44 +01:00
nEnv [ "REMOTE_ADDR" ] = ( req . socket . realRemoteAddress ? req . socket . realRemoteAddress : ( ( req . headers [ "x-forwarded-for" ] && configJSON . enableIPSpoofing ) ? req . headers [ "x-forwarded-for" ] . split ( "," ) [ 0 ] . replace ( / /g , "" ) : req . socket . remoteAddress ) ) . replace ( /^::ffff:/i , "" ) ;
if ( req . socket . realRemoteAddress && req . socket . realRemotePort ) {
nEnv [ "REMOTE_PORT" ] = req . socket . realRemotePort ;
} else if ( ! ( req . socket . realRemoteAddress && ! req . socket . realRemotePort ) ) {
nEnv [ "REMOTE_PORT" ] = req . socket . remotePort ;
}
if ( req . socket . encrypted ) nEnv [ "HTTPS" ] = "ON" ;
if ( req . headers [ "content-type" ] ) nEnv [ "CONTENT_TYPE" ] = req . headers [ "content-type" ] ;
nEnv [ "CONTENT_LENGTH" ] = "0" ;
if ( req . headers [ "content-length" ] ) nEnv [ "CONTENT_LENGTH" ] = req . headers [ "content-length" ] ;
var nh = JSON . parse ( JSON . stringify ( req . headers ) ) ;
delete nh [ "content-type" ] ;
delete nh [ "content-length" ] ;
var nhKeys = Object . keys ( nh ) ;
for ( var i = 0 ; i < nhKeys . length ; i ++ ) {
nEnv [ "HTTP_" + nhKeys [ i ] . replace ( /[^0-9A-Za-z]+/g , "_" ) . toUpperCase ( ) ] = req . headers [ nhKeys [ i ] ] ;
}
executeFastCGI ( req , res , dh , nEnv ) ;
}
var isScriptExt = scriptExts . indexOf ( "." + ext ) != - 1 ;
if ( fastcgiConf . path !== undefined && ( href == fastcgiConf . path || href . indexOf ( fastcgiConf . path + "/" ) == 0 ) ) {
try {
executeFastCGIWithEnv (
decodeURIComponent ( href ) ,
href . replace ( fastcgiConf . path , "" ) ,
req ,
res ,
req . socket . localAddress ,
req . socket . localPort ,
getCustomHeaders ?
getCustomHeaders ( ) [ "Server" ] +
( disableModExposeSupported && ( configJSON . exposeModsInErrorPages || configJSON . exposeModsInErrorPages === undefined ) ?
" GreenRhombus/" +
version : "" ) :
"SVR.JS/" +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version ,
bheaders ,
authUser ,
fastcgiConf . path
) ;
} catch ( ex ) {
if ( ! callServerError ) {
res . writeHead ( 500 , "Internal Server Error" , abheaders ) ;
res . write (
"<html><head><title>500 Internal Server Error</title></head><body><h1>500 Internal Server Error</h1><p>A server had unexpected exception. Below, the stack trace of the error is shown:</p><code>" +
ex . stack . replace ( /\r\n/g , "<br/>" ) . replace ( /\n/g , "<br/>" ) . replace ( /\r/g , "<br/>" ) . replace ( / /g , " " ) +
"</code><p>Please contact the developer/administrator of the website.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS " +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version +
" " +
( req . headers . host == undefined ? "" : " on " + req . headers . host ) +
"</p></body></html>"
) ;
res . end ( ) ;
} else {
callServerError ( 500 , "GreenRhombus/" + version , ex ) ;
}
}
2024-02-16 20:19:32 +01:00
} else if ( ( ( href == "/greenrhombus-config.json" || ( os . platform ( ) == "win32" && href . toLowerCase ( ) == "/greenrhombus-config.json" ) ) && path . normalize ( _ _dirname + "/../../.." ) == process . cwd ( ) ) || ( ( href == "/greenrhombus-scriptexts.json" || ( os . platform ( ) == "win32" && href . toLowerCase ( ) == "/greenrhombus-scriptexts.json" ) ) && path . normalize ( _ _dirname + "/../../.." ) == process . cwd ( ) ) ) {
2024-02-16 19:58:44 +01:00
if ( ! callServerError ) {
res . writeHead ( 200 , "OK" , {
"Content-Type" : "application/json" ,
"Server" : "GreenRhombus/" + version
} ) ;
res . end ( JSON . stringify ( exttointerpreteruser , null , 2 ) ) ;
} else {
callServerError ( 200 , "GreenRhombus/" + version , exttointerpreteruser ) ;
}
} else {
fs . stat ( "." + decodeURIComponent ( href ) , function ( err , stats ) {
if ( ! err ) {
if ( ! stats . isFile ( ) ) {
if ( scriptExts . indexOf ( ".php" ) != - 1 ) {
fs . stat ( "." + decodeURIComponent ( href ) + "/index.php" , function ( e2 , s2 ) {
if ( ! e2 && s2 . isFile ( ) ) {
try {
executeFastCGIWithEnv (
( decodeURIComponent ( href ) + "/index.php" ) . replace ( /\/+/g , "/" ) ,
"" ,
req ,
res ,
req . socket . localAddress ,
req . socket . localPort ,
getCustomHeaders ?
getCustomHeaders ( ) [ "Server" ] +
( disableModExposeSupported && ( configJSON . exposeModsInErrorPages || configJSON . exposeModsInErrorPages === undefined ) ?
" GreenRhombus/" +
version : "" ) :
"SVR.JS/" +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version ,
bheaders ,
authUser ,
( decodeURIComponent ( href ) + "/index.php" ) . replace ( /\/+/g , "/" )
) ;
} catch ( ex ) {
if ( ! callServerError ) {
res . writeHead ( 500 , "Internal Server Error" , abheaders ) ;
res . write (
"<html><head><title>500 Internal Server Error</title></head><body><h1>500 Internal Server Error</h1><p>A server had unexpected exception. Below, the stack trace of the error is shown:</p><code>" +
ex . stack . replace ( /\r\n/g , "<br/>" ) . replace ( /\n/g , "<br/>" ) . replace ( /\r/g , "<br/>" ) . replace ( / /g , " " ) +
"</code><p>Please contact the developer/administrator of the website.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS " +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version +
" " +
( req . headers . host == undefined ? "" : " on " + req . headers . host ) +
"</p></body></html>"
) ;
res . end ( ) ;
} else {
callServerError ( 500 , "GreenRhombus/" + version , ex ) ;
}
}
} else {
elseCallback ( ) ;
}
} ) ;
} else if ( scriptExts . indexOf ( ".cgi" ) != - 1 ) {
fs . stat ( "." + decodeURIComponent ( href ) + "/index.cgi" , function ( e3 , s3 ) {
if ( ! e3 && s3 . isFile ( ) ) {
try {
executeFastCGIWithEnv (
( decodeURIComponent ( href ) + "/index.cgi" ) . replace ( /\/+/g , "/" ) ,
"" ,
req ,
res ,
req . socket . localAddress ,
req . socket . localPort ,
getCustomHeaders ?
getCustomHeaders ( ) [ "Server" ] +
( disableModExposeSupported && ( configJSON . exposeModsInErrorPages || configJSON . exposeModsInErrorPages === undefined ) ?
" GreenRhombus/" +
version : "" ) :
"SVR.JS/" +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version ,
bheaders ,
authUser ,
( decodeURIComponent ( href ) + "/index.cgi" ) . replace ( /\/+/g , "/" )
) ;
} catch ( ex ) {
if ( ! callServerError ) {
res . writeHead ( 500 , "Internal Server Error" , abheaders ) ;
res . write (
"<html><head><title>500 Internal Server Error</title></head><body><h1>500 Internal Server Error</h1><p>A server had unexpected exception. Below, the stack trace of the error is shown:</p><code>" +
ex . stack . replace ( /\r\n/g , "<br/>" ) . replace ( /\n/g , "<br/>" ) . replace ( /\r/g , "<br/>" ) . replace ( / /g , " " ) +
"</code><p>Please contact the developer/administrator of the website.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS " +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version +
" " +
( req . headers . host == undefined ? "" : " on " + req . headers . host ) +
"</p></body></html>"
) ;
res . end ( ) ;
} else {
callServerError ( 500 , "GreenRhombus/" + version , ex ) ;
}
}
} else {
elseCallback ( ) ;
}
} ) ;
} else {
elseCallback ( ) ;
}
} else {
if ( isScriptExt ) {
try {
executeFastCGIWithEnv (
decodeURIComponent ( href ) ,
"" ,
req ,
res ,
req . socket . localAddress ,
req . socket . localPort ,
getCustomHeaders ?
getCustomHeaders ( ) [ "Server" ] +
( disableModExposeSupported && ( configJSON . exposeModsInErrorPages || configJSON . exposeModsInErrorPages === undefined ) ?
" GreenRhombus/" +
version : "" ) :
"SVR.JS/" +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version ,
bheaders ,
authUser ,
decodeURIComponent ( href )
) ;
} catch ( ex ) {
if ( ! callServerError ) {
res . writeHead ( 500 , "Internal Server Error" , abheaders ) ;
res . write (
"<html><head><title>500 Internal Server Error</title></head><body><h1>500 Internal Server Error</h1><p>A server had unexpected exception. Below, the stack trace of the error is shown:</p><code>" +
ex . stack . replace ( /\r\n/g , "<br/>" ) . replace ( /\n/g , "<br/>" ) . replace ( /\r/g , "<br/>" ) . replace ( / /g , " " ) +
"</code><p>Please contact the developer/administrator of the website.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS " +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version +
" " +
( req . headers . host == undefined ? "" : " on " + req . headers . host ) +
"</p></body></html>"
) ;
res . end ( ) ;
} else {
callServerError ( 500 , "GreenRhombus/" + version , ex ) ;
}
}
} else {
elseCallback ( ) ;
}
}
} else if ( err && err . code == "ENOTDIR" ) {
function checkPath ( pth , cb , a ) {
// Function to check the path of the file and execute CGI script
var cpth = pth . split ( "/" ) ;
if ( cpth . length < 2 ) {
cb ( false ) ;
return ;
}
if ( ! a ) b = [ ] ;
else var b = a . split ( "/" ) ;
var isFile = false ;
fs . stat ( pth , function ( err , stats ) {
if ( ! err && stats . isFile ( ) ) {
cb ( {
fpth : pth ,
rpth : ( a !== undefined ? "/" + a : "" )
} )
} else {
b . unshift ( cpth . pop ( ) ) ;
return checkPath ( cpth . join ( "/" ) , cb , b . join ( "/" ) ) ;
}
} ) ;
}
checkPath ( "." + decodeURIComponent ( href ) , function ( pathp ) {
if ( ! pathp ) {
elseCallback ( ) ;
} else {
var newext = path . extname ( pathp . fpth ) ;
if ( scriptExts . indexOf ( newext ) != - 1 ) {
try {
executeFastCGIWithEnv (
pathp . fpth . substr ( 1 ) ,
pathp . rpth ,
req ,
res ,
req . socket . localAddress ,
req . socket . localPort ,
getCustomHeaders ?
getCustomHeaders ( ) [ "Server" ] +
( disableModExposeSupported && ( configJSON . exposeModsInErrorPages || configJSON . exposeModsInErrorPages === undefined ) ?
" GreenRhombus/" +
version : "" ) :
"SVR.JS/" +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version ,
bheaders ,
authUser ,
pathp . fpth . substr ( 1 )
) ;
} catch ( ex ) {
if ( ! callServerError ) {
res . writeHead ( 500 , "Internal Server Error" , abheaders ) ;
res . write (
"<html><head><title>500 Internal Server Error</title></head><body><h1>500 Internal Server Error</h1><p>A server had unexpected exception. Below, the stack trace of the error is shown:</p><code>" +
ex . stack . replace ( /\r\n/g , "<br/>" ) . replace ( /\n/g , "<br/>" ) . replace ( /\r/g , "<br/>" ) . replace ( / /g , " " ) +
"</code><p>Please contact the developer/administrator of the website.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS " +
configJSON . version +
" (" +
os . platform ( ) [ 0 ] . toUpperCase ( ) +
os . platform ( ) . slice ( 1 ) +
"; Node.JS/" +
process . version +
") GreenRhombus/" +
version +
" " +
( req . headers . host == undefined ? "" : " on " + req . headers . host ) +
"</p></body></html>"
) ;
res . end ( ) ;
} else {
callServerError ( 500 , "GreenRhombus/" + version , ex ) ;
}
}
} else {
elseCallback ( ) ;
}
}
} ) ;
} else {
elseCallback ( ) ; //Invoke default error handler
}
} ) ;
}
}
}
module . exports = Mod ;