commit c0fa6a6a74c7b960b736a3583f68b12ec500caba Author: Dorian Niemiec Date: Fri Aug 11 19:53:57 2023 +0200 Initial commit diff --git a/index.js b/index.js new file mode 100644 index 0000000..34258b2 --- /dev/null +++ b/index.js @@ -0,0 +1,364 @@ +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 stream = require("stream"); + +var version = "UNKNOWN"; +try { + version = JSON.parse(fs.readFileSync(__dirname + "/mod.info")).version; +} catch (ex) { + // Can't determine version +} +var configJSONS = JSON.parse(fs.readFileSync("config.json")); // Read configuration JSON + +class RequestBodyStream extends stream.Readable { + constructor(sourceStream) { + super(); + this.sourceStream = sourceStream; + + // When the source stream emits data, push it to the wrapper stream + this.sourceStream.on('data', (chunk) => { + this.push(chunk); + }); + + // When the source stream ends, push null to indicate the end of the wrapper stream + this.sourceStream.on('end', () => { + this.push(null); + }); + } + + // Implement the _read method to handle the read operation + _read() {} +} + +class ErrorStream extends stream.Writable { + constructor() { + super(); + this.buffer = Buffer.alloc(0); + } + + _write(chunk, encoding, callback) { + // Concatenate the incoming chunk to the buffer + this.buffer = Buffer.concat([this.buffer, chunk]); + + // Call the callback to indicate that the write operation is complete + callback(); + } +} + +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) { + return function () { + if (!configJSON) { + configJSON = configJSONS; + } + + 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)); + + function executeJSGI(fname, req, res, dh, jsgiRequestObject) { + // Function to execute JSGI scripts + if (!fname.match(/\.jsgi(?:\.js)?$/)) { + elseCallback(); + return; + } + try { + var JSGIApp = require(process.cwd() + "/" + fname); + var jsgiResponseObject = {}; + if (typeof JSGIApp === "object" && JSGIApp.app) { + jsgiResponseObject = JSGIApp.app(jsgiRequestObject); + } else { + throw new Error("JSGI app must have app key in exports objects!!!"); + } + if (jsgiRequestObject.jsgi.errors.readable) jsgiRequestObject.jsgi.errors.end(); + var errors = jsgiRequestObject.jsgi.errors.buffer.toString(); + if (errors.trim().length > 0) { + serverconsole.errmessage("There were JSGI application errors:"); + serverconsole.errmessage(errors); + } + if (!jsgiResponseObject.status) jsgiResponseObject.status = 200; + if (!getCustomHeaders) { + var aheaders = JSON.parse(JSON.stringify(configJSON.customHeaders)); + } else { + var aheaders = getCustomHeaders(); + } + if (jsgiResponseObject.headers) { + var hKeys = Object.keys(jsgiResponseObject.headers); + for (var i = 0; i < hKeys.length; i++) { + aheaders[hKeys[i]] = jsgiResponseObject.headers[hKeys[i]]; + } + } + if (!jsgiResponseObject.body) jsgiResponseObject.body = [""]; + if (typeof jsgiResponseObject.body === "string") { + res.writeHead(jsgiResponseObject.status, http.STATUS_CODES[jsgiResponseObject.status], aheaders); + res.write(jsgiResponseObject.body); + res.end(); + } else if (typeof jsgiResponseObject.body.forEach !== "function") { + throw new Error("JSGI app must return body, which has forEach function."); + } else { + res.writeHead(jsgiResponseObject.status, http.STATUS_CODES[jsgiResponseObject.status], aheaders); + jsgiResponseObject.body.forEach(function (chunk) { + res.write(chunk); + }); + res.end(); + } + } catch (error) { + if (!callServerError) { + res.writeHead(500, { + "Content-Type": "text/html", + "Server": "YellowSquare/" + version + }); + res.end("

YellowSquare Error!

Reason: " + error.message + "

"); + } else { + callServerError(500, "YellowSquare/" + version, error); + } + } + } + + function executeJSGIWithReqObj(a, b, req, res, pubip, port, software, dh) { + // Function to set up request object and execute JSGI scripts + var inputStream = new RequestBodyStream(req); + var errorStream = new ErrorStream(); + var jsgiRequestObject = { + version: req.httpVersion.split("."), + method: req.method, + headers: req.headers, + input: inputStream, + scriptName: a, + pathInfo: decodeURI(b), + pathTranslated: b ? decodeURI((process.cwd() + (require("os").platform == "win32" ? b.replace(/\//g, "\\") : b)).replace((require("os").platform == "win32" ? /\\\\/g : /\/\//g), (require("os").platform == "win32" ? "\\" : "/"))) : "", + scheme: req.socket.encrypted ? "https" : "http", + env: {}, + jsgi: { + version: [0, 3], + errors: errorStream, + multithread: false, + multiprocess: true, + runOnce: false, + async: false, + cgi: false, + ext: {} + }, + serverSoftware: software, + remoteAddr: (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, ""), + remotePost: (req.socket.realRemotePort ? req.socket.realRemotePort : req.socket.remotePort) + }; + if (req.headers.authorization) { + jsgiRequestObject.authType = req.headers.authorization.split(" ")[0]; + if (jsgiRequestObject.authType == "Basic") { + var remoteCred = req.headers.authorization.split(" ")[1]; + if (!remoteCred) { + jsgiRequestObject.remoteUser = "yellowsquare_jsgi_invalid_user"; + } else { + var remoteCredDecoded = Buffer.from(remoteCred, "base64").toString("utf8"); + jsgiRequestObject.remoteUser = remoteCredDecoded.split(":")[0]; + } + } else { + jsgiRequestObject.remoteUser = "svrjs_this_property_is_not_yet_supported_by_yellowsquare_jsgi"; + } + } + jsgiRequestObject.queryString = req.url.split("?")[1]; + if (jsgiRequestObject.queryString == undefined || jsgiRequestObject.queryString == "undefined") jsgiRequestObject.queryString = ""; + if (pubip && (port !== null && port !== undefined)) { + jsgiRequestObject.port = port; + jsgiRequestObject.host = pubip.replace(/^::ffff:/i, ""); + if (jsgiRequestObject.host.indexOf(":") != -1) jsgiRequestObject.host = "[" + jsgiRequestObject.host + "]"; + } + executeJSGI("." + a, req, res, dh, jsgiRequestObject); + } + + if (href.indexOf("/jsgi-bin") == 0) { + fs.stat("." + href, function (err, stats) { + if (!err) { + if (!stats.isFile()) { + elseCallback(); + } else { + try { + executeJSGIWithReqObj( + href, + "", + req, + res, + req.socket.localAddress, + req.socket.localPort, + getCustomHeaders ? + getCustomHeaders()["Server"] + + " YellowSquare/" + + version : + "SVR.JS/" + + configJSON.version + + " (" + + os.platform()[0].toUpperCase() + + os.platform().slice(1) + + "; Node.JS/" + + process.version + + ") YellowSquare/" + + version, + bheaders + ); + } catch (ex) { + if (!callServerError) { + res.writeHead(500, "Internal Server Error", abheaders); + res.write( + "500 Internal Server Error

500 Internal Server Error

A server had unexpected exception. Below, the stack trace of the error is shown:

" + + ex.stack.replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ /g, " ") + + "

Please contact the developer/administrator of the website.

SVR.JS " + + configJSON.version + + " (" + + os.platform()[0].toUpperCase() + + os.platform().slice(1) + + "; Node.JS/" + + process.version + + ") YellowSquare/" + + version + + " " + + (req.headers.host == undefined ? "" : " on " + req.headers.host) + + "

" + ); + res.end(); + } else { + callServerError(500, "YellowSquare/" + version, ex); + } + } + } + } 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 < 3) { + 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("." + href, function (pathp) { + if (!pathp) { + elseCallback(); + } else { + try { + executeJSGIWithReqObj( + pathp.fpth.substr(1), + pathp.rpth, + req, + res, + req.socket.localAddress, + req.socket.localPort, + getCustomHeaders ? + getCustomHeaders()["Server"] + + " YellowSquare/" + + version : + "SVR.JS/" + + configJSON.version + + " (" + + os.platform()[0].toUpperCase() + + os.platform().slice(1) + + "; Node.JS/" + + process.version + + ") YellowSquare/" + + version, + bheaders + ); + } catch (ex) { + if (!callServerError) { + res.writeHead(500, "Internal Server Error", abheaders); + res.write( + "500 Internal Server Error

500 Internal Server Error

A server had unexpected exception. Below, the stack trace of the error is shown:

" + + ex.stack.replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ /g, " ") + + "

Please contact the developer/administrator of the website.

SVR.JS " + + configJSON.version + + " (" + + os.platform()[0].toUpperCase() + + os.platform().slice(1) + + "; Node.JS/" + + process.version + + ") YellowSquare/" + + version + + " " + + (req.headers.host == undefined ? "" : " on " + req.headers.host) + + "

" + ); + res.end(); + } else { + callServerError(500, "YellowSquare/" + version, ex); + } + } + } + }); + } else if (err && err.code == "ENOENT") { + elseCallback(); //Invoke default error handler + } else { + if (!callServerError) { + res.writeHead(500, "Internal Server Error", abheaders); + res.write( + "500 Internal Server Error

500 Internal Server Error

A server had unexpected exception. Below, the stack trace of the error is shown:

" + + err.stack.replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ /g, " ") + + "

Please contact the developer/administrator of the website.

SVR.JS " + + configJSON.version + + " (" + + os.platform()[0].toUpperCase() + + os.platform().slice(1) + + "; Node.JS/" + + process.version + + ") YellowSquare/" + + version + + " " + + (req.headers.host == undefined ? "" : " on " + req.headers.host) + + "

" + ); + res.end(); + } else { + callServerError(500, "YellowSquare/" + version, err); + } + } + }); + } else if (href == "/YellowSquare-interpreters.json" && path.normalize(__dirname + "/../../..") == process.cwd()) { + if (!callServerError) { + res.writeHead(200, "OK", { + "Content-Type": "application/json", + "Server": "YellowSquare/" + version + }); + res.end(JSON.stringify(exttointerpreteruser, null, 2)); + } else { + callServerError(200, "YellowSquare/" + version, exttointerpreteruser); + } + } else { + elseCallback(); + } + } +} + +module.exports = Mod; diff --git a/mod.info b/mod.info new file mode 100755 index 0000000..19128fa --- /dev/null +++ b/mod.info @@ -0,0 +1,4 @@ +{ + "name": "DorianTech YellowSquare JSGI engine for SVR.JS", + "version": "Nightly-GitMain" +}