12 KiB
title |
---|
SVR.JS API (.tar.gz mods and server-side JavaScript) |
SVR.JS API (.tar.gz mods and server-side JavaScript)
SVR.JS has its API for both .tar.gz mods and server-side JavaScript that expands its functionality. SVR.JS API extends vanilla Node.JS HTTP API.
Error handling
When a JavaScript error is thrown outside of event callbacks, SVR.JS will return a 500 error to the client. Inside event callbacks, SVR.JS will simply crash.
Incorrect Error Handling:
//XXX WARNING!!! IT WILL CRASH THE SVR.JS!!!
//It also contains path traversal vulnerability!
disableEndElseCallbackExecute = true; //Without "var", else it will not work!!!
var headers = getCustomHeaders();
headers["Content-Type"] = "text/html; charset=utf8";
if (href == "/page.svr") {
fs.readFile(".template", function (err, data) {
if (err) throw err; //EVIL!!!
var id = uobject.query.id;
if (!id) id = "index";
if (fs.existsSync("pages/" + id + ".html")) {
fs.readFile("pages/" + id + ".html", function (err2, data2) {
if (err2) throw err2; //EVIL TWO!!!
res.writeHead(200, "OK", headers);
res.end(data.toString().replace("{websiteContents}", data2.toString()));
});
} else {
callServerError(404);
}
});
} else if (href == "/") {
redirect("/page.svr");
} else {
elseCallback();
}
Instead, you should handle errors gracefully using callServerError function:
Correct Error Handling:
//Much better!
disableEndElseCallbackExecute = true; //Without "var", else it will not work!!!
var headers = getCustomHeaders();
headers["Content-Type"] = "text/html; charset=utf8";
if (href == "/page.svr") {
if (fs.existsSync(".template")) {
fs.readFile(".template", function (err, data) {
if (err) {
callServerError(500, err);
return;
}
var id = uobject.query.id;
if (!id) id = "index";
id = id
.replace(/\\/g, "/")
.replace(/(?:\/|^)\.\.(?=(\/|$))/g, "$1")
.replace(/\/+/g, "/"); //Poor mans path sanitiation
if (fs.existsSync("pages/" + id + ".html")) {
fs.readFile("pages/" + id + ".html", function (err2, data2) {
if (err2) {
callServerError(500, err2);
return;
}
res.writeHead(200, "OK", headers);
res.end(
data.toString().replace("{websiteContents}", data2.toString())
);
});
} else {
callServerError(404);
}
});
} else {
callServerError(
500,
new Error("Server is misconfigured - .template file missing")
);
}
} else if (href == "/") {
redirect("/page.svr");
} else {
elseCallback();
}
By using callServerError
, you can handle errors effectively and provide appropriate error responses to the client, preventing SVR.JS from crashing due to unhandled exceptions.
Non-proxy API
This API is exposed both to mods and server-side JavaScript. This API also includes proxy requests, which don't use CONNECT method. It's possible to determine, if the request comes from the proxy, by checking if req.url begins with "http://" or with "https://" (unlike non-proxy requests, for which req.url begins with "/") like this:
var isProxy = req.url && req.url.match(/^https?:\/\//);
SVR.JS applies mods for request URLs beginning with "http://" or with "https://" (proxy through GET or POST method, non-proxy requests have request URLs beginning with "/") only if Mod.prototype.proxyCallback method is present (not possible with SVR.JS server-side JavaScript).
req
req object is almost same, as req object in Node.JS
Differences:
- req.socket.realRemoteAddress and req.socket.realRemotePort will contain IP address, from which request originally went from, if request is sent through reverse proxy (for X-Forwarded-For header, req.socket.realRemotePort will be null). You can specify generic request IP variable using
var reqip = req.socket.realRemoteAddress ? req.socket.realRemoteAddress : req.socket.remoteAddress
(from SVR.JS 3.4.4) - req.socket.originalRemoteAddress and req.socket.originalRemotePort will contain IP address, from which proxy request came from.
- req.url refers to request URL after all processing (URL rewriting too)
res
res object is almost same, as res object in Node.JS
Differences:
- res.socket.realRemoteAddress and res.socket.realRemotePort will contain IP address, from which request originally went from, if request is sent through reverse proxy. (for X-Forwarded-For header, req.socket.realRemotePort will be null; from SVR.JS 3.4.4)
- res.socket.originalRemoteAddress and res.socket.originalRemotePort will contain IP address, from which proxy request came from.
- res.writeHead writes into server log.
- res.writeHead doesn't invoke a warning about unused status code string.
- res.setHeader doesn't invoke a warning about HTTP/1.x headers being not allowed in HTTP/2.
- res.writeHead called multiple times will emit a warning, instead of throwing an error, which could crash SVR.JS.
- Custom headers defined in config.json are set by default.
serverconsole.climessage(message)
Parameters:
- message - message you want to send to server console (String)
Sends CLI message to server console.
serverconsole.reqmessage(message)
Parameters:
- message - message you want to send to server console (String)
Sends request message to server console.
serverconsole.resmessage(message)
Parameters:
- message - message you want to send to server console (String)
Sends response message to server console.
serverconsole.errmessage(message)
Parameters:
- message - message you want to send to server console (String)
Sends response error message to server console.
serverconsole.locerrmessage(message)
Parameters:
- message - message you want to send to server console (String)
Sends local error message to server console.
serverconsole.locwarnmessage(message)
Parameters:
- message - message you want to send to server console (String)
Sends local warning message to server console.
serverconsole.locmessage(message)
Parameters:
- message - message you want to send to server console (String)
Sends local message to server console.
responseEnd(body)
Parameters:
- body - message you want to send before ending response (String or Buffer)
Sends response message (along with custom head and foot) specified by body parameter.
href
Path name of resource defined in the request. Alias for uobject.pathname.
ext
Extension of resource defined in the request.
uobject
Parsed Url object created by url.parse() method (includes parsed query string).
SVR.JS 3.3.1 and newer include hostname of the server (3.3.1 to 3.14.x use wrapper over WHATWG URL API; 3.15.0 and newer use custom URL parser), older versions don't.
search
Query string of URL. Alias for uobject.search
defaultpage
WARNING! DEPRECATED IN SVR.JS 3.X OR NEWER
In SVR.JS 2.x it was alias for configJSON.defaultpage. In SVR.JS 3.x for backward compability it's always "index.html".
users
WARNING! DEPRECATED
Alias for configJSON.users
page404
Alias for configJSON.page404
head
HTML head read from either .head or head.html file.
foot
HTML foot read from either .foot or foot.html file.
fd
WARNING! This property has currently no use and it's reserved for new SVR.JS functions. Currently this property is an empty string.
elseCallback()
Invokes next SVR.JS mod callback, SVR.JS server-side JavaScript callback or main SVR.JS callback.
configJSON
Added in SVR.JS 3.0.0
Parsed object of config.json file.
SVR.JS 3.4.0 and newer has version property, that corresponds to server version, and productName property, which always is "SVR.JS".
callServerError(errorCode[, extName][, stack][, ch])
Added in SVR.JS 3.0.0
Parameters:
- errorCode - HTTP error code (Number)
- extName - extension name, which caused an error (optional; String)
- stack - error stack (optional; String or Error)
- ch - custom HTTP headers for error (optional; Object)
Invokes HTTP error code. If it's unavailable, invokes 501 error code.
getCustomHeaders()
Added in SVR.JS 3.0.0
Returns: Object property contains custom headers.
This methods retrieves custom headers from config.json file. Returned object additionally includes Server header.
origHref
Added in SVR.JS 3.0.0
Original path name before URL rewriting.
redirect(dest[, isTemporary][, keepMethod][, headers])
Added in SVR.JS 3.0.0
Parameters:
- dest - destination of redirect (String)
- isTemporary - if true, then redirect with 302 code. Else redirect with 301 code. When keepMethod parameter is set to true, then redirect with 307 code, when isTemporary is true or with 308 code otherwise. (optional; Boolean)
- keepMethod - if true, then redirect with either 307 or 308 code. Else redirect with etiher 301 or 302 code. (optional; Boolean; SVR.JS 3.13.0 or later)
- headers - custom HTTP headers for redirect (optional; Object)
Redirects HTTP client to specific destination.
parsePostData([options], callback)
Added in SVR.JS 3.0.0
Parameters:
- options - options to be passed to formidable (optional; Object)
- callback - callback to execute after parsing URL. (Function)
- err - error, which occurred in POST data parsing. If not occured, it's null (Error or null)
- fields - POST fields (Object)
- files - Files sent (Object)
A wrapper over formidable library, which is used for parsing request bodies of POST requests.
authUser
Added in SVR.JS 3.14.2
The name of authenticated HTTP user. If the user wasn't authenticated, the property would be null.
If you want to check if the request is authenticated in SVR.JS versions older than 3.14.2, you can use function shown below, that checks for an applicable 401 non-standard code in the server configuration:
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;
}
Proxy API
Added in SVR.JS 3.4.21, 3.7.0
This API is exposed only to mods. It is invoked, when client connects with the server using CONNECT method.
This API was present from SVR.JS 3.0.0, however SVR.JS version older than 3.4.21 or 3.7.0 had a bug, which involves calling non-proxy callback, instead of proxy one. A workaround involves calling proxy callback over non-proxy one, when request uses CONNECT method (insert at beginning of non-proxy callback):
if (!res.writeHead) {
Mod.prototype.proxyCallback(
req,
res,
serverconsole,
responseEnd,
href,
ext
)();
return;
}
req
Added in SVR.JS 3.4.21, 3.7.0
req object is the same, as req object in Node.JS
socket
Added in SVR.JS 3.4.21, 3.7.0
socket object is the same, as socket object in Node.JS
head
Added in SVR.JS 3.4.21, 3.7.0
head object is the same, as head object in Node.JS
configJSON
Added in SVR.JS 3.4.21, 3.7.0
See configJSON in non-proxy API
serverconsole
Added in SVR.JS 3.4.21, 3.7.0
See serverconsole in non-proxy API
elseCallback()
Added in SVR.JS 3.4.21, 3.7.0
See elseCallback in non-proxy API