`. Command-line options:
* _-h_, _-?_, _/h_, _/?_ or _--help_ - displays help
* _-a_ or _--add_ - adds an user
* _-d_ or _--delete_ - deletes an user
* _-x_ - forces password hashing algorithm change
In SVR.JS 3.7.0 and newer, you can choose between 3 password hashing algorithms:
* **Salted SHA256** (1 iteration) - fastest and uses least memory, but less secure
* **PBKDF2** (PBKDF2-HMAC-SHA512, 36250 iterations) - more secure and uses less memory, but slower
* **scrypt** (N=214, r=8, p=1) - faster and more secure, but uses more memory
### HTTP authentication
You can add HTTP basic authentication by including a 401 code (with _scode_ property set to 401) entry in the _nonStandardCodes_ property of _config.json_. To enable HTTP basic authentication, you need to specify the URL you want to restrict in the _url_ or _regex_ property of the entry. Additionally, you can set the authentication realm in the _realm_ property. If the realm is not specified, the default realm is "_SVR.JS HTTP Basic Authorization_". The encoding used for authentication will always be UTF-8.
By default, SVR.JS enables brute force protection for HTTP authentication, so you don't need to worry about potential brute force attacks against the authentication mechanism.
From SVR.JS 3.8.0 onwards, you can specify a list of allowed users in the _userList_ property.
### Redirects
Setting up HTTP redirects is simple with SVR.JS. You can add a 301 or 302 code (with _scode_ property set to 301 or 302) entry to the _nonStandardCodes_ property in _config.json_. The entry should specify the source URL for the redirect in the _url_ or regular expression string for the redirect in _regex_ property (for example `"/\\/blog($|[#?\\/].*)/"`), and the destination URL in the _location_ property (for regular expressions, you can use for example "_$1_" for contents of first capturing group). Destination location can be relative to current site (for example _/blogs_) or a full URL (for example _https://blog.example.com_).
Please be cautious when setting up redirects to avoid redirect loops, as they can cause unintended behavior and potentially impact the performance of your server.
From SVR.JS 3.8.0 onwards, you can specify a hostname for which this redirect applies in _host_ property. If you want to move your website to new address, you can add redirect to new website and specify _host_ property to be host name of the old website (for example _oldsite.example_), _location_ property to be a location of new website with "_$1_" appended (for example "_https://newsite.example$1_") and _regex_ property to be `"/(.*)/"` (regular expression, which matches everything to capturing group).
### URL rewriting
You can set up URL rewriting by adding entries to _rewriteMap_ property in _config.json_ file. See ["_config.json_ properties" section](#config-json-properties) for URL rewrite rules syntax.
### Log viewing
To make log viewing easier, SVR.JS 3.x and later included it's log viewer utility under _logviewer.js_. SVR.JS log viewer is interactive.
You can also manually view logs, and highlight them using SVR.JS log highlighter utility under _loghighlight.js_ (SVR.JS 3.0.0 or newer). Usage: `<some process> | node loghighlight.js [-h] [--help] [-?] [/h] [/?]`.
SVR.JS stores it's logs in _log_ directory. Server logs look like this:
[2023-07-04T18:50:54.610Z] SERVER MESSAGE [Request Id: c0ffd0]: Somebody connected to port 80...
[2023-07-04T18:50:54.611Z] SERVER REQUEST MESSAGE [Request Id: c0ffd0]: Client ::ffff:127.0.0.1:33670 wants content in localhost/leak.svr
[2023-07-04T18:50:54.611Z] SERVER REQUEST MESSAGE [Request Id: c0ffd0]: Client uses Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: There was an error while processing the request!
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: Stack:
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: RangeError [ERR_INVALID_OPT_VALUE]: The value "4294967296" is invalid for option "size"
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: at Function.allocUnsafe (buffer.js:290:3)
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: at /home/ubuntu/svr.js.3.3.3/temp/serverSideScript.js:70:18
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: at /home/ubuntu/svr.js.3.3.3/temp/modloader/primitiveanalytics.tar.gz/index.js:28:23
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: at modExecute (/home/ubuntu/svr.js.3.3.3/svr.js:2981:9)
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: at Server.reqhandler (/home/ubuntu/svr.js.3.3.3/svr.js:3741:11)
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: at Server.emit (events.js:198:13)
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: at parserOnIncoming (_http_server.js:691:12)
[2023-07-04T18:50:54.625Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: at HTTPParser.parserOnHeadersComplete (_http_common.js:111:17)
[2023-07-04T18:50:54.626Z] SERVER RESPONSE ERROR MESSAGE [Request Id: c0ffd0]: Server responded with 500 code.
[2023-07-04T18:50:54.630Z] SERVER MESSAGE [Request Id: c0ffd0]: Client disconnected.
[2023-07-04T18:50:54.699Z] SERVER MESSAGE [Request Id: 1be453]: Somebody connected to port 80...
[2023-07-04T18:50:54.699Z] SERVER REQUEST MESSAGE [Request Id: 1be453]: Client ::ffff:127.0.0.1:33670 wants content in localhost/favicon.ico
[2023-07-04T18:50:54.700Z] SERVER REQUEST MESSAGE [Request Id: 1be453]: Client uses Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0
[2023-07-04T18:50:54.709Z] SERVER MESSAGE [Request Id: 1be453]: Client disconnected.
[2023-07-04T18:50:54.710Z] SERVER RESPONSE MESSAGE [Request Id: 1be453]: Server responded with 200 code.
[2023-07-04T18:50:54.712Z] SERVER RESPONSE MESSAGE [Request Id: 1be453]: Client successfully recieved content.
First on the line is timestamp. Second is message type, optionally with request ID. Last is message contents.
### Environment variables
#### SVR.JS 3.12.0 and newer
You can configure environment variables by configuring _environmentVariables_ property in _config.json_.
#### Older SVR.JS versions
SVR.JS seamlessly passes through externally set environment variables to mods and server-side JavaScript, allowing you to customize and control your application's behavior based on these variables.
If you have a start-up script or use the command line to run SVR.JS, you can easily set environment variables before launching the server. Here's an example of how you can do it in a bash script:
```bash
export NODE_ENV=production
export OPENAI_API_KEY=redacted
node svr.js
```
In this example, we're setting two environment variables, `NODE_ENV` and `OPENAI_API_KEY`, before running the `node svr.js` command. These environment variables will be accessible within your mods and server-side JavaScript, allowing you to utilize them to configure and adapt your application as needed.
If you have installed SVR.JS using SVR.JS installer then you may modify _/etc/init.d/svrjs_ script (_do_start_ method) like this:
```bash
do_start()
{
if [ ! -f "$lockfile" ] ; then
echo -n $"Starting $servicename: "
runuser -l "$user" -c "export GIT_HTTP_EXPORT_ALL=1; export GIT_PROJECT_ROOT=/var/lib/git; $nodejs $server > /dev/null &" && echo_success || echo_failure
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch "$lockfile"
else
echo "$servicename is locked."
RETVAL=1
fi
}
```
If you have used SVR.JS installer on GNU/Linux distribution that uses _systemd_, then you may add _Environment_ directives in _Service_ section in _systemd_ service file (_/etc/systemd/system/svrjs.service_) like this:
```ini
[Unit]
Description=SVR.JS web server
After=network.target
[Service]
Type=simple
User=svrjs
ExecStart=/usr/bin/env node /usr/lib/svrjs/svr.js
Environment=GIT_HTTP_EXPORT_ALL=1
Environment=GIT_PROJECT_ROOT=/var/lib/git
Restart=on-failure
[Install]
WantedBy=multi-user.target
```
Using environment variables can be a powerful way to manage different configurations for development, staging, and production environments, configure your CGI web applications, or to securely store sensitive information like API keys and passwords.
Remember that when you set environment variables externally, they will be available to all instances of SVR.JS running on your system. Exercise caution when using sensitive information as environment variables, and ensure that they are properly secured and protected.
By leveraging environment variables, you can enhance the flexibility and security of your SVR.JS application and streamline your deployment process.
### CGI/SCGI/JSGI/PHP
In order to use CGI with SVR.JS, you need to install RedBrick mod. For SCGI you need to install OrangeCircle, while for JSGI you need to install YellowSquare mod. [Download these mods.](https://svrjs.org/mods)
#### CGI and PHP via RedBrick
RedBrick supports running CGI programs and PHP files (RedBrick 2.3.0 and newer) in cgi-bin directory. RedBrick 2.5.0 and newer support running CGI programs and PHP files outside cgi-bin directory. You can configure file extensions outside of cgi-bin directory handled by RedBrick in _redbrick-scriptexts.json_ file in SVR.JS installation directory like this:
```json
[
".php",
".cgi"
]
```
RedBrick custom interpreters (from RedBrick 2.3.2; in earlier versions it is broken) can be configured in _redbrick-interpreters.json_ file in SVR.JS install directory like this:
```json
{
".pl": ["perl"],
".py": ["python"],
".sh": ["bash"],
".pyw": ["python"],
".rb": ["ruby"],
".php": ["php-cgi"]
}
```
RedBrick 2.3.0 and newer support PHP-CGI, while RedBrick 2.3.1 and newer support URL rewriting. RedBrick 2.3.6 and newer work with Windows (older ones always threw an 500 error while trying to execute CGI scripts on Windows). RedBrick 2.4.3 and newer work with web root outside SVR.JS installation directory (older ones need _config.json_ file in web root with valid JSON data; not necessarily related to _config.json_ in SVR.JS installation directory)
RedBrick 2.4.1 and newer allows to disable some default interpreter configuration using "null" like this:
```json
{
".pl": null,
".py": null,
".rb": null,
".exe": null
}
```
As of RedBrick 2.4.3, there are default interpreter configurations for _.pl_, _.py_, _.sh_, _.ksh_, _.csh_, _.rb_ and _.php_ files. If server is running on Windows, then there will be additional default interpreter configuration for _.exe_, _.bat_, _.cmd_ (dropped in RedBrick 2.5.0) and _.vbs_ files.
SVR.JS currently supports PHP-CGI through RedBrick mod. PHP is currently supported only inside _cgi-bin_ directory in SVR.JS web root. RedBrick 2.5.0 and newer supports PHP outside of _cgi-bin_ directory. You need to modify PHP configuration file (usually at _/etc/php/<php version>/cgi/php.ini_) and set _cgi.force_redirect_ property to _0_, otherwise PHP-CGI will not work and just display a warning about PHP-CGI binary being compiled with force-cgi-redirect enabled. **It's recommended to use directories outside of _cgi-bin_ for user uploads and downloads** (so that RedBrick will not treat uploaded scripts with shebang and ELF binary files as CGI applications and try to execute them, potentially resulting in hacker-uploaded malware infections, remote code execution vulnerabilities or 500 Internal Server Errors).
For security reasons, you may disable directory listing for _cgi-bin_ (and also other directories) through _disableDirectoryListing_ or _disableDirectoryListingVHost_ options in SVR.JS configuration.
#### SCGI via OrangeCircle
OrangeCircle can be configured in _orangecircle-config.json_ file in SVR.JS install directory like this:
```json
{
"path": "/scgi",
"host": "localhost",
"port": 4000
}
```
OrangeCircle 1.0.7 and newer work with web root outside SVR.JS installation directory (older ones need _config.json_ file in web root with valid JSON data; not necessarily related to _config.json_ in SVR.JS installation directory)
#### JSGI via YellowSquare
YellowSquare supports running JSGI scripts only in jsgi-bin directory. YellowSquare runs JSGI scripts, that are either with _.jsgi_ or _.jsgi.js_ extension. Every change in JSGI application requires a restart of SVR.JS in order to be applied.
YellowSquare 1.0.3 and newer work with web root outside SVR.JS installation directory (older ones need _config.json_ file in web root with valid JSON data; not necessarily related to _config.json_ in SVR.JS installation directory)
For security reasons, you may disable directory listing for _jsgi-bin_ (and also other directories) through _disableDirectoryListing_ or _disableDirectoryListingVHost_ options in SVR.JS configuration.
### FastCGI/PHP-FPM
In order to use FastCGI with SVR.JS, you need to install GreenRhombus mod. [Download the mod.](https://svrjs.org/mods)
#### GreenRhombus notes
GreenRhombus' path and FastCGI server address can be configured in _greenrhombus-config.json_ file in the SVR.JS install directory.
Example configuration (with FastCGI server listening with port):
```json
{
"path": "/fastcgi",
"host": "localhost",
"port": 7000
}
```
Example configuration (with FastCGI server listening on socket):
```json
{
"path": "/fastcgi",
"socketPath": "/run/fastcgi.sock"
}
```
You can configure file extensions outside of path specified in _greenrhombus-config.json_ file handled by GreenRhombus in _greenrhombus-scriptexts.json_ file in SVR.JS installation directory like this:
```json
[
".php"
]
```
#### PHP-FPM
GreenRhombus supports running PHP files through PHP-FPM. If you want to use GreenRhombus only for PHP-FPM, configure _greenrhombus-config.json_ like this (in this case we're using socket in _/run/php/php8.2-fpm.sock_; you can check it in PHP-FPM configuration file, e.g. _/etc/php/8.2/fpm/pool.d/www__.conf_; configure it without _path_ property):
```json
{
"socketPath": "/run/php/php8.2-fpm.sock"
}
```
And configure _greenrhombus-scriptexts.json_ like this:
```json
[
".php"
]
```
PHP-FPM may run on different user than SVR.JS web server, so you may need to set permissions for the user, which PHP-FPM runs on.
If you are using PHP-FPM only for SVR.JS, you can set the _listen.owner_ and _listen.group_ properties to _svrjs_ in the PHP-FPM configuration file (e.g. _/etc/php/8.2/fpm/pool.d/www__.conf_).
### Forward proxy notes
In order to use SVR.JS as a forward proxy, you need to install forward-proxy-mod SVR.JS mod. [Download this mod.](https://svrjs.org/mods)
If you are using forward-proxy-mod, then local IP addresses and _localhost_ are also accessible from the proxy. You may block these using a firewall, if you don't want these to be accessible from the proxy
### Reverse proxy configuration
In order to use SVR.JS as a reverse proxy, you need to install reverse-proxy-mod SVR.JS mod. [Download this mod.](https://svrjs.org/mods)
Configuration file of reverse-proxy-mod is _reverse-proxy-config.json_ inside SVR.JS installation directory. Keys of configuration object are domain names (or paths from reverse-proxy-mod 1.1.1), for which it's settings apply. Values are object with those properties:
* _hostname_ - Hostname of origin server.
* _port_ - Port of origin server.
* _secureHostname_ - Hostname of origin server (access via HTTPS).
* _securePort_ - Port of origin server (access via HTTPS).
reverse-proxy-mod 1.1.0 and newer support HTTP upgrades (including WebSocket).
If you're using per-host URL rewrite rules and running multiple sites on one SVR.JS instance (instead of proxying them all to specific web servers; assuming that you're using SVR.JS 3.8.0 or newer; shared hosting), use paths referring to URL rewrite destinations instead of domain names. However if you're planning to use VPSes (virtualized servers) or run different web server instances and use SVR.JS with reverse-proxy-mod as a reverse proxy for them, use domain names instead.
If you're using SVR.JS just as a reverse proxy (for VPSes or other web server instances, and not serving websites from proxy itself), set _disableServerSideScriptExpose_ to _false_, set web root to outside SVR.JS installation directory, empty out _rewriteMap_, _nonStandardCodes_, _enableDirectoryListingVHost_, _customHeadersVHost_, _wwwrootPostfixesVHost_, _wwwrootPostfixPrefixesVHost_ to `[]`, empty out _customHeaders_ to `{}`, set _disableTrailingSlashRedirects_ to _true_, set _allowDoubleSlashes_ to _true_, and set _disableToHTTPSRedirect_ to _true_, in order to avoid interference involving SVR.JS web server (use this configuration when proxy itself doesn't use SVR.JS server-side JavaScript not including SVR.JS mods).
### Virtual hosts
When you're not planning to use SVR.JS server-side JavaScript or SVR.JS mods implementing individual web applications and plan to use SVR.JS similarly to Apache, nginx or IIS (static-only, PHP, CGI or JSGI), you can use virtual host-like functionality (name-based; IP-based from SVR.JS 3.14.1 and newer) with SVR.JS 3.8.0 or newer.
You can set up custom error pages, URL rewriting rules and non-standard status code settings per-host like this (assuming that you want to also include CGI support through RedBrick):
```json
{
"users": [],
"port": 80,
"pubport": 80,
"page404": "404.html",
"blacklist": [],
"nonStandardCodes": [
{
"url": "/anothersite.example/dl",
"location": "/download",
"host": "anothersite.example",
"scode": 301
},
{
"url": "/anothersite.example/downloads",
"location": "/download",
"host": "anothersite.example",
"scode": 301
}
],
"enableCompression": true,
"customHeaders": {},
"enableHTTP2": false,
"enableLogging": true,
"enableDirectoryListing": true,
"enableDirectoryListingWithDefaultHead": false,
"serverAdministratorEmail": "[no contact information]",
"stackHidden": false,
"enableRemoteLogBrowsing": false,
"exposeServerVersion": true,
"disableServerSideScriptExpose": true,
"rewriteMap": [
{
"definingRegex": "/^(?!\\/(?:\\.dirimages|cgi-bin)(?:$|[\\/#?]))/",
"host": "website.example",
"replacements": [
{
"regex": "/(.*)/",
"replacement": "/website.example$1"
}
]
},
{
"definingRegex": "/^\\/cgi-bin(?:$|[\\/#?])/",
"host": "website.example",
"replacements": [
{
"regex": "/^\\/cgi-bin($|[\\/#?].*)/",
"replacement": "/cgi-bin/website.example$1"
}
]
},
{
"definingRegex": "/^(?!\\/(?:\\.dirimages|cgi-bin)(?:$|[\\/#?])|\\/index(?:$|[\\/#?]))/",
"host": "anothersite.example",
"replacements": [
{
"regex": "/(.*)/",
"replacement": "/anothersite.example$1"
}
]
},
{
"definingRegex": "/^\\/index(?:$|[\\/#?])/",
"host": "anothersite.example".
"replacements": [
{
"regex": "/^\\/index/",
"replacement": "/anothersite.example/"
}
]
},
{
"definingRegex": "/^\\/cgi-bin(?:$|[\\/#?])/",
"host": "anothersite.example",
"replacements": [
{
"regex": "/^\\/cgi-bin($|[\\/#?].*)/",
"replacement": "/cgi-bin/anothersite.example$1"
}
]
},
{
"definingRegex": "/^\\/(?:cgi-bin\\/)?(?:website\\.example|anothersite\\.example)(?:$|[\\/#?])/",
"replacements": [
{
"regex": "/(.*)/",
"replacement": "/NONEXISTENT_PAGE"
}
]
}
],
"allowStatus": true,
"dontCompress": [
"/.*\\.ipxe$/",
"/.*\\.img$/",
"/.*\\.iso$/"
],
"enableIPSpoofing": false,
"secure": false,
"sni": {},
"disableNonEncryptedServer": false,
"disableToHTTPSRedirect": false,
"enableETag": true,
"disableUnusedWorkerTermination": false,
"rewriteDirtyURLs": true,
"errorPages": [
{
"scode": 404,
"path": "oops.html",
"host": "website.example"
}
],
"customHeadersVHost": [
{
"host": "website.example",
"headers": {
"X-Some-Header": "some-value"
}
}
],
"enableDirectoryListingVHost": [
{
"host": "website.example",
"enable": false
}
]
}
```
If you're using SVR.JS 3.14.0 or newer, you can use this configuration:
```json
{
"users": [],
"port": 80,
"pubport": 80,
"page404": "404.html",
"blacklist": [],
"nonStandardCodes": [
{
"url": "/anothersite.example/dl",
"location": "/download",
"host": "anothersite.example",
"scode": 301
},
{
"url": "/anothersite.example/downloads",
"location": "/download",
"host": "anothersite.example",
"scode": 301
}
],
"enableCompression": true,
"customHeaders": {},
"enableHTTP2": false,
"enableLogging": true,
"enableDirectoryListing": true,
"enableDirectoryListingWithDefaultHead": false,
"serverAdministratorEmail": "[no contact information]",
"stackHidden": false,
"enableRemoteLogBrowsing": false,
"exposeServerVersion": true,
"disableServerSideScriptExpose": true,
"rewriteMap": [
{
"definingRegex": "/^\\/anothersite.example\\/index(?:$|[\\/#?])/",
"host": "anothersite.example".
"replacements": [
{
"regex": "/^\\/anothersite.example\\/index/",
"replacement": "/anothersite.example/"
}
]
},
],
"allowStatus": true,
"dontCompress": [
"/.*\\.ipxe$/",
"/.*\\.img$/",
"/.*\\.iso$/"
],
"enableIPSpoofing": false,
"secure": false,
"sni": {},
"disableNonEncryptedServer": false,
"disableToHTTPSRedirect": false,
"enableETag": true,
"disableUnusedWorkerTermination": false,
"rewriteDirtyURLs": true,
"errorPages": [
{
"scode": 404,
"path": "oops.html",
"host": "website.example"
}
],
"customHeadersVHost": [
{
"host": "website.example",
"headers": {
"X-Some-Header": "some-value"
}
}
],
"enableDirectoryListingVHost": [
{
"host": "website.example",
"enable": false
}
],
"wwwrootPostfixesVHost": [
{
"host": "website.example",
"postfix": "website.example",
"skipRegex": "/^\\/.dirimages(?:$|[\\/#?]))/"
},
{
"host": "anothersite.example",
"postfix": "anothersite.example",
"skipRegex": "/^\\/.dirimages(?:$|[\\/#?]))/"
}
{
"postfix": "NONEXISTENT_SITE"
}
],
"wwwrootPostfixPrefixesVHost": [
"/cgi-bin"
]
}
```
You need to then create this directory structure in SVR.JS web root:
* cgi-bin
* website.example
* anothersite.example
* website.example
* anothersite.example
If you want to still use SVR.JS server-side JavaScript (not including SVR.JS mods implementing specific web applications) and virtual hosts simultaneously, path checks need to correspond to rewritten URLs (those processed by URL rewriting engine).
For some SVR.JS mods, path settings may correspond to URLs processed by URL rewriting engine.
It is not recommended to use global custom head and foot (_head.html_, _foot.html_, _.head_, _.foot_) with this setup, because they will apply to all virtual hosts (doesn't include custom heads and feet for directory listings [_.dirhead_, _.dirfoot_], which apply to only one directory).
### Client-initiated secure renegotiation
Client-initiated secure renegotiation may pose DoS risks. However, Node.JS (JS runtime on which SVR.JS is running on) has built-in protection against DoS attacks caused by client-initiated secure renegotiation. Such attacks can be detected by looking for _ERR_TLS_SESSION_ATTACK_ errors in server log.
## Mods
Mods in SVR.JS are custom modules that can extend the server's functionality. Using mods, you can extend SVR.JS functionality to suit your specific requirements and customize the server's behavior to handle different types of requests.
### Installing mods
To install mod to SVR.JS, copy the mod to _mods_ directory inside SVR.JS installation directory. SVR.JS searches this directory for mods, loads and executes them in alphabetical order (by mod file name). If you want have mods to be executed in specific order, add numeric prefix to mod file name, for example "_01-redbrick.cgi.2.3.3.tar.gz_" and "_00-easywaf.integration.1.1.2.tar.gz_".
### Mod format
SVR.JS mods are JavaScript files, they work in SVR.JS 4.x and newer
Older SVR.JS mods are _tar_ archives with _gzip_ compression, they work in SVR.JS 2.x and newer.
SVR.JS 1.x used custom _svrmodpack_ archives with _gzip_ compression (they worked up to SVR.JS 3.13.0), but this format is **deprecated for new mods, and may be no longer supported in future versions of SVR.JS**, since _svrmodpack_ is not maintained anymore. All current SVR.JS mods are now in _tar.gz_ format. **SVR.JS 3.13.0 dropped support for _svrmodpack_.**
### Mod loading order
#### Startup
1. Search for mods
2. For each mod (sorted alphabetically by mod file name):
- _.js_ mods
1. Initialize mod, and add mod along with mod info to list
- _.tar.gz_ mods
1. Prepare temporary directory for extracted mod contents
2. Extract mod contents
3. Initialize mod, and add mod along with mod info to list
3. Load server-side JavaScript:
1. Create mod file from server-side JavaScript
2. Initialize "mod", and add "mod" to list
#### Execution (on each server request)
1. Initialize SVR.JS variables
2. Invoke mods and server-side JavaScript (mods sorted alphabetically by mod file name)
3. Load SVR.JS main callback (if it's not affected by mods and server-side JavaScript)
### Mod files (_.tar.gz_ mods)
1. _index.js_ - main script for mod
2. _mod.info_ - information about mod
3. other files necessary for the mod
### Mod development (_.js_ mods)
This section provides a comprehensive guide on developing `.js` mods for SVR.JS. Mods allow you to extend the functionality of SVR.JS by writing custom JavaScript code.
#### Mod callback
The main export of the mod is a callback function that handles HTTP requests. This function takes the following parameters:
- `req` - the request object.
- `res` - the response object.
- `logFacilities` - logging facilities provided by SVR.JS.
- `config` - the configuration object.
- `next` - a function to pass control to the next request handler.
**You should implement a proxy URL check in the callback, if you're going to use `proxy` callback (or set `proxySafe` in the exports to `true`) and main callback at once, or else your SVR.JS mod may be vulnerable to access control bypass attacks** (SVR.JS doesn't enforce URL rewriting, custom headers and non-standard codes for proxy requests to avoid interference of its access controls with proxy mods).
#### Commands
Mods can also export commands that can be invoked from the SVR.JS console. The `commands` object maps command names to functions that handle the command logic.
Each command takes the following parameters:
- `args` - the arguments for the command
- `log` - the logging function for the command
- `passCommand` - a function to pass control to the next command handler.
#### Proxy handling
Mods can handle proxy requests by exporting a `proxy` function. This function takes the following parameters:
- `req` - the request object.
- `socket` - the socket object.
- `head` - the head object.
- `logFacilities` - logging facilities provided by SVR.JS.
- `config` - the configuration object.
- `next`- a function to pass control to the next proxy handler.
Required in order for the main callback to be invoked for request URLs beginning with "_http://_" or with "_https://_" (proxy through GET or POST method, non-proxy requests have request URLs beginning with "_/_").
You can also set `proxySafe` in the exports to `true`, in order to have the same effect described above.
#### IPC listener
Mods can communicate with the main process using IPC (Inter-Process Communication). The `process.messageEventListeners` array allows you to add listeners for messages received by the main process.
You can add the wrapper for the listener for messages received by main process, which takes these parameters:
- `worker` - worker who sent the message
- `serverconsole` - logging facilities provided by SVR.JS.
The wrapper returns a function, which takes the `message` parameter, which means the message sent by the worker.
Control messages received by main process begin with 0x12 control character. Control messages sent by main process begin with 0x14 control character.
The reserved control messages, used internally by SVR.JS begin with:
- `\x12AUTHQ`
- `\x12AUTHR`
- `\x12AUTHW`
- `\x12CLOSE`
- `\x12END`
- `\x12ERRCRASH`
- `\x12ERRLIST`
- `\x12KILLOK`
- `\x12KILLTERMMSG`
- `\x12LISTEN`
- `\x12PINGOK`
- `\x12SAVEERR`
- `\x12SAVEGOOD`
- `\x14AUTHA`
- `\x14AUTHD`
- `\x14KILLPING`
- `\x14KILLREQ`
- `\x14PINGPING`
- `\x14SAVECONF`
#### Paths
`process.dirname` refers to the SVR.JS installation directory.
Current working directory (`process.cwd()`) is SVR.JS web root.
#### Get started
To get started with the development of the mod, clone the Git repository for the SVR.JS mod starter:
```bash
git clone https://git.svrjs.org/svrjs/svrjs-mod-starter.git
```
Further instructions can be found in the `README` file in the SVR.JS mod starter repository.
#### Example mod
Below is an example of `index.js` code for a simple mod from SVR.JS mod starter that handles a `/test.svr` endpoint and a `/ping.svr` endpoint:
```js
const cluster = require("./utils/clusterBunShim.js"); // Cluster shim for Bun
const { add } = require("./utils/helper.js"); // Require the addition module
const modInfo = require("../modInfo.json"); // SVR.JS mod information
// Exported SVR.JS mod callback
module.exports = (req, res, logFacilities, config, next) => {
if (req.parsedURL.pathname == "/test.svr") {
res.writeHead(200, "OK", {
"Content-Type": "text/plain"
});
res.end("2 + 2 = " + add(2,2));
} else if (req.parsedURL.pathname == "/ping.svr") {
if (!cluster.isWorker) {
// Invoke 500 Internal Server Error status code, if the process is not a worker
res.error(500, new Error("SVR.JS is running single-threaded, so this request is not supported"));
return;
}
// Ping OK message listener
const pingOKListener = (message) => {
if (message == "\x14MODPINGOK") {
process.removeListener("message", pingOKListener);
res.writeHead(200, "OK", {
"Content-Type": "text/plain"
});
res.end("OK");
}
};
// Listen to Ping OK messages
process.on("message", pingOKListener);
// Send Ping message
process.send("\x12MODPING");
} else {
next(); // Invoke other request handlers
}
};
// Exported command
module.exports.commands = {
somecmd: (args, log, passCommand) => {
log("Arguments: " + args.toString()); // Print arguments
passCommand(args, log) // Invoke other command handlers
}
};
// IPC listener for main process
// Control messages received by main process begin with 0x12 control character
// Control messages sent by main process begin with 0x14 control character
process.messageEventListeners.push((worker, serverconsole) => {
return (message) => {
if (message == "\x12MODPING") {
// Ping back
worker.send("\x14MODPINGOK");
}
}
});
module.exports.modInfo = modInfo;
```
The `modInfo.json` file would look like this:
```json
{
"name": "Example mod",
"version": "0.0.0"
}
```
### Mod development (_.tar.gz_ mods)
Mods in SVR.JS have two methods:
* _callback_ - Invoked on non-CONNECT requests (includes proxy requests using GET or POST methods). Parameters (must be in this particular order, argument names given to match SVR.JS API documentation):
* _req_
* _res_
* _serverconsole_
* _responseEnd_
* _href_
* _ext_
* _uobject_
* _search_
* _defaultpage_
* _users_
* _page404_
* _head_
* _foot_
* _fd_
* _elseCallback_
* _configJSON_
* _callServerError_
* _getCustomHeaders_
* _origHref_
* _redirect_
* _parsePostData_
* _authUser_
**This method is required** (if it is not present, SVR.JS will simply return 500 Internal Server Error on all requests with error message in error stack similar to "_TypeError: modO.callback is not a function_").
* _proxyCallback_ - Invoked on CONNECT requests (used for proxying). Parameters (must be in this particular order, argument names given to match SVR.JS API documentation):
* _req_
* _socket_
* _head_
* _configJSON_
* _serverconsole_
* _elseCallback_
Required in order for function returned from _callback_ method to be invoked for request URLs beginning with "_http://_" or with "_https://_" (proxy through GET or POST method, non-proxy requests have request URLs beginning with "_/_"). **You should implement a proxy URL check in _callback_ method, if you're going to use _proxyCallback_ and _callback_ methods at once, or else your SVR.JS mod may be vulnerable to access control bypass attacks** (SVR.JS doesn't enforce URL rewriting, custom headers and non-standard codes for proxy requests to avoid interference of its access controls with proxy mods).
These methods are defined inside _Mod.prototype_ object. Both methods return a function, which will be executed in SVR.JS.
`__dirname` and _._ in `require()` function both refer to directory, to which mod contents are extracted.
The reference to file in the SVR.JS installation directory is `__dirname + "/../../../filename"` (replace `filename` with your desired file name).
Current working directory (`process.cwd()`) is SVR.JS web root.
A typical _index.js_ file for a mod may look like this:
```js
//Requires go here
function Mod() {}
Mod.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) {
return function () {
//Mod contents go here
if (href == "/hello.svr") {
serverconsole.resmessage("Sent Hello World message!");
res.writeHead(200, "OK", {
"Content-Type": "text/plain"
});
res.end("Hello World!");
} else {
elseCallback();
}
}
}
//OPTIONAL: proxyCallback method
//Uncomment code below, if you want to use proxyCallback method.
//But then you'll need to implement proxy request URL check for callback method.
/*
Mod.prototype.proxyCallback = function proxyCallback(req, socket, head, configJSON, serverconsole, elseCallback) {
return function () {
//Just pass elseCallback
elseCallback();
}
}
*/
module.exports = Mod; //SVR.JS mod exports
```
The _mod.info_ file (in JSON format) contains metadata about the mod, such as its name and version:
```
{
"name": "The Example Mod",
"version": "0.1.0"
}
```
## Server-side JavaScript
Another way to expand SVR.JS functionality is through server-side JavaScript located in _serverSideScript.js_ file inside SVR.JS web root (or locaten in SVR.JS installation directory if you're running SVR.JS 3.9.0 or newer, and you have set _useWebRootServerSideScript_ property to _false_). Server-side JavaScript allows you to create various web applications using JavaScript, Node.JS and SVR.JS API.
### Predefined objects
When working with server-side JavaScript in SVR.JS, you have access to several predefined objects that can greatly enhance your scripting capabilities. These objects are available for use without requiring any additional imports or installations.
* _readline_: The _readline_ library for reading input streams.
* _os_: The _os_ library for accessing operating system-related information.
* _http_: The _http_ library for handling HTTP functionality.
* _url_: The _url_ library for parsing and formatting URLs.
* _fs_: The _fs_ library for interacting with the file system.
* _path_: The _path_ library for working with file and directory paths.
* _hexstrbase64_: The _hexstrbase64_ library for converting hexadecimal strings to base64 format.
* _crypto_: The _crypto_ library for cryptographic functionality, such as generating hashes and handling encryption.
* _https_: The _https_ library, an extension of the _http_ library for handling secure HTTPS connections.
* _stream_: The _stream_ library for working with streams of data.
* _customvar1_, _customvar2_, _customvar3_, _customvar4_: Custom variables meant to be reused in multiple requests. These variables are available starting from SVR.JS version 3.0.0.
Additionally, there is an option to control the automatic execution of the _elseCallback_ function. By default, automatic execution is enabled. You can use the _disableEndElseCallbackExecute_ option to disable this behavior if needed.
By leveraging these predefined objects, you can streamline your server-side JavaScript code and build powerful applications using SVR.JS.
### Predefined methods
_See methods in SVR.JS API in non-proxy section_
Additionally SVR.JS server-side JavaScript has `filterHeaders(headers)` method, that filters out invalid request headers.
SVR.JS 3.8.0 and newer have additionally two methods:
* `checkHref(destHref)` - checks if request path name matches the _destHref_.
* `checkHostname(hostname)` - checks if host name defined in the request matches the _hostname_ parameter.
### SSJS development
`__dirname` and _._ in `require()` function both refer to _temp_ directory in SVR.JS.
Current working directory (`process.cwd()`) is SVR.JS web root.
If you want to divide server-side JavaScript into several files, you can do one of those:
* `require(process.cwd() + "/someOtherScript.js")` for script located in the web root (**it's recommended to set up 403 Forbidden or 404 Not Found non-standard code in SVR.JS configuration to prevent source code leaks**).
* `require("../someOtherScript.js")` for script located in SVR.JS installation directory.
* `require("/usr/lib/webappfiles/someOtherScript.js")` for script located in _/usr/lib/webappfiles_.
Example code:
```js
disableEndElseCallbackExecute = true; //Without "var", else it will not work!!!
if (href == "/hello.svr") {
serverconsole.resmessage("Sent Hello World message!");
res.writeHead(200, "OK", {
"Content-Type": "text/plain"
});
res.end("Hello World!");
} else {
elseCallback();
}
```
### Migration to SVR.JS
If you have previously built your web application using the Node.JS http library, Express framework, or Koa framework, you can easily migrate that code to SVR.JS server-side JavaScript.
#### From Node.JS http library
For applications built with the Node.JS http library, you can simply copy the contents of the request event listener to the SVR.JS server-side JavaScript. However, make sure to consider the _disableEndElseCallbackExecute_ option to ensure proper execution flow.
```js
disableEndElseCallbackExecute = true; //Without "var", else it will not work!!!
// Node.JS http library request event listener code goes here.
if(req.url == "/" && req.method == "GET") {
res.writeHead(200, "OK", {
"Content-Type": "text/plain"
});
res.end("Hello World!");
return;
}
elseCallback(); // Optionally, invoke main SVR.JS callback.
```
#### From Express Framework
If your application is built using the Express framework, you can easily migrate it to SVR.JS. You can mix Express methods with SVR.JS methods for more flexibility.
```js
disableEndElseCallbackExecute = true; //Without "var", else it will not work!!!
var express = require("express");
// Other requires go here.
var app = express(); // Initialize express app
// Express application code goes here!
app.get("/", function (req, res) {
res.send("Hello World!");
});
app.use(elseCallback); // Optionally, if you want the main SVR.JS callback.
app(req, res); // Invoke Express handler
```
#### From Koa Framework
Migrating from the Koa framework to SVR.JS is also straightforward. Here's an example of how you can do it:
```js
disableEndElseCallbackExecute = true; //Without "var", else it will not work!!!
var koa = require("koa");
// Other requires go here.
var app = new koa(); // Initialize Koa app
// Koa application code goes here!
app.use(function (ctx, next) {
if (ctx.method != "GET" || ctx.path != "/") {
next(); // Koa router could be used...
} else {
ctx.body = "Hello World!";
}
});
// Optionally, if you want the main SVR.JS callback (not recommended by Koa, as it passes Node.JS req and res objects).
app.use(function (ctx) {
ctx.respond = false;
elseCallback(ctx.req, ctx.res);
});
(app.callback())(req, res); // Invoke Koa handler
```
By migrating your web application to SVR.JS, you can take advantage of its features and performance enhancements, while still preserving your existing codebase.
## SVR.JS API (_.js_ mods)
SVR.JS has its API for _.js_ mods 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:
```js
//XXX WARNING!!! IT WILL CRASH THE SVR.JS!!!
//It also contains path traversal vulnerability!
module.exports = (req, res, logFacilities, config, next) => {
let headers = config.getCustomHeaders();
headers["Content-Type"] = "text/html; charset=utf8";
if (req.parsedURL.pathname == "/page.svr") {
fs.readFile(".template", (err, data) => {
if (err) throw err; //EVIL!!!
let id = req.parsedURL.query.id;
if (!id) id = "index";
if (fs.existsSync("pages/" + id + ".html")) {
fs.readFile("pages/" + id + ".html", (err2, data2) => {
if (err2) throw err2; //EVIL TWO!!!
res.writeHead(200, "OK", headers);
res.end(data.toString().replace("{websiteContents}", data2.toString()));
});
} else {
res.error(404);
}
});
} else if (href == "/") {
res.redirect("/page.svr");
} else {
next();
}
};
module.exports.modInfo = {
name: "Custom server-side JavaScript",
version: "0.0.0"
};
```
Instead, you should handle errors gracefully using `res.error` function:
#### Correct Error Handling:
```js
//Much better!
module.exports = (req, res, logFacilities, config, next) => {
let headers = config.getCustomHeaders();
headers["Content-Type"] = "text/html; charset=utf8";
if (req.parsedURL.pathname == "/page.svr") {
fs.readFile(".template", (err, data) => {
if (err) {
res.error(500, err);
return;
}
let id = req.parsedURL.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", (err2, data2) => {
if (err2) {
res.error(500, err2);
return;
}
res.writeHead(200, "OK", headers);
res.end(data.toString().replace("{websiteContents}", data2.toString()));
});
} else {
res.error(404);
}
});
} else if (href == "/") {
res.redirect("/page.svr");
} else {
next();
}
};
module.exports.modInfo = {
name: "Custom server-side JavaScript",
version: "0.0.0"
};
```
By using `res.error`, you can handle errors effectively and provide appropriate error responses to the client, preventing SVR.JS from crashing due to unhandled exceptions.
### Main callback API (`module.exports`)
_Added in SVR.JS 4.0.0_
This API includes proxy requests, which don't use CONNECT method. It's possible to determine, if the request comes from the proxy with `req.isProxy` property.
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 _module.exports.proxy_ method is present or if _module.exports.proxySafe_ property is set to `true`.
#### _req_
_Added in SVR.JS 4.0.0_
_req_ object is almost same, as _req_ object in Node.JS
#### _req.socket.realRemoteAddress_
_Added in SVR.JS 4.0.0_
A property containing IP address, from which request originally went from, if request is sent through reverse proxy.
You can specify generic request IP variable using `const reqip = req.socket.realRemoteAddress ? req.socket.realRemoteAddress : req.socket.remoteAddress`
#### _req.socket.realRemotePort_
_Added in SVR.JS 4.0.0_
A property containing port number, from which request originally went from, if request is sent through reverse proxy. (for _X-Forwarded-For_ header, it will be _null_)
You can specify generic request IP variable using `const reqip = req.socket.realRemotePort ? req.socket.realRemotePort : req.socket.remotePort`
#### _req.socket.originalRemoteAddress_
_Added in SVR.JS 4.0.0_
A property containing IP address, from which proxy request came from. If the request isn't a proxy request, it will be `undefined`.
#### _req.socket.originalRemotePort_
_Added in SVR.JS 4.0.0_
A property containing port number, from which proxy request came from. If the request isn't a proxy request, it will be `undefined`.
#### _req.url_
_Added in SVR.JS 4.0.0_
A property containing request URL after all processing (URL rewriting too).
#### _req.parsedURL_
_Added in SVR.JS 4.0.0_
A property containing parsed request URL created by a custom URL parser (compatible with legacy URL parser: `url.parse()`)
#### _req.originalParsedURL_
_Added in SVR.JS 4.0.0_
A property containing parsed request URL (before URL rewriting) created by `new URL()` constructor (WHATWG URL parser)
#### _req.isProxy_
_Added in SVR.JS 4.0.0_
A property that determines if request is a proxy request or not.
#### _req.authUser_
_Added in SVR.JS 4.0.0_
The name of authenticated HTTP user. If the user wasn't authenticated, the property would be _null_.
#### _res_
_Added in SVR.JS 4.0.0_
_res_ object is almost same, as _res_ object in Node.JS
#### _res.socket.realRemoteAddress_
_Added in SVR.JS 4.0.0_
A property containing IP address, from which request originally went from, if request is sent through reverse proxy.
You can specify generic request IP variable using `const reqip = req.socket.realRemoteAddress ? req.socket.realRemoteAddress : req.socket.remoteAddress`
#### _res.socket.realRemotePort_
_Added in SVR.JS 4.0.0_
A property containing port number, from which request originally went from, if request is sent through reverse proxy. (for _X-Forwarded-For_ header, it will be _null_)
You can specify generic request IP variable using `const reqip = req.socket.realRemotePort ? req.socket.realRemotePort : req.socket.remotePort`
#### _res.socket.originalRemoteAddress_
_Added in SVR.JS 4.0.0_
A property containing IP address, from which proxy request came from. If the request isn't a proxy request, it will be `undefined`.
#### _res.socket.originalRemotePort_
_Added in SVR.JS 4.0.0_
A property containing port number, from which proxy request came from. If the request isn't a proxy request, it will be `undefined`.
#### _res.writeHead(statusCode[, statusMessage][, headers])_
_Added in SVR.JS 4.0.0_
Parameters:
* _statusCode_ - the response status code (_String_)
* _statusMessage_ - the response status message (optional; _String_)
* _headers_ - the response headers (optional; _Object_)
Returns: _res_ property.
The difference between _res.writeHead_ in Node.JS, and in SVR.JS is that in SVR.JS it writes into server log, doesn't invoke a warning about unused status code string, and if called multiple times will emit a warning, instead of throwing an error, which could crash SVR.JS.
#### _res.setHeader(name, value)_
_Added in SVR.JS 4.0.0_
Parameters:
* _name_ - the response header name (_String_)
* _value_ - the response header value (optional; _String_ or _Array_)
The difference between _res.setHeader_ in Node.JS, and in SVR.JS is that in SVR.JS it doesn't invoke a warning about HTTP/1.x headers being not allowed in HTTP/2.
Custom headers defined in _config.json_ are set by default.
#### _res.head_
_Added in SVR.JS 4.0.0_
HTML head read from either _.head_ or _head.html_ file.
#### _res.foot_
_Added in SVR.JS 4.0.0_
HTML foot read from either _.foot_ or _foot.html_ file.
#### _res.responseEnd(body)_
_Added in SVR.JS 4.0.0_
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.
#### _res.error(errorCode[, extName][, stack][, ch])_
_Added in SVR.JS 4.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.
#### _res.redirect(dest[, isTemporary][, keepMethod][, headers])_
_Added in SVR.JS 4.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.
#### _logFacilities_
_Added in SVR.JS 4.0.0_
The log facilities for SVR.JS.
#### _logFacilities.climessage(message)_
_Added in SVR.JS 4.0.0_
Parameters:
* _message_ - message you want to send to server console (_String_)
Sends CLI message to server console.
#### _logFacilities.reqmessage(message)_
_Added in SVR.JS 4.0.0_
Parameters:
* _message_ - message you want to send to server console (_String_)
Sends request message to server console.
#### _logFacilities.resmessage(message)_
_Added in SVR.JS 4.0.0_
Parameters:
* _message_ - message you want to send to server console (_String_)
Sends response message to server console.
#### _logFacilities.errmessage(message)_
_Added in SVR.JS 4.0.0_
Parameters:
* _message_ - message you want to send to server console (_String_)
Sends response error message to server console.
#### _logFacilities.locerrmessage(message)_
_Added in SVR.JS 4.0.0_
Parameters:
* _message_ - message you want to send to server console (_String_)
Sends local error message to server console.
#### _logFacilities.locwarnmessage(message)_
_Added in SVR.JS 4.0.0_
Parameters:
* _message_ - message you want to send to server console (_String_)
Sends local warning message to server console.
#### _logFacilities.locmessage(message)_
_Added in SVR.JS 4.0.0_
Parameters:
* _message_ - message you want to send to server console (_String_)
Sends local message to server console.
#### _config_
_Added in SVR.JS 4.0.0_
This object contains properties from _config.json_ file.
#### _config.getCustomHeaders()_
_Added in SVR.JS 4.0.0_
Returns: _Object_ property contains custom headers.
This methods retrieves custom headers from _config.json_ file. Returned object additionally includes _Server_ header.
#### _config.generateServerString()_
_Added in SVR.JS 4.0.0_
Returns: The generated server string.
This methods generated the string which is used to identify a web server (the same string as in the "Server" header).
#### _next()_
_Added in SVR.JS 4.0.0_
Invokes next SVR.JS mod callback, SVR.JS server-side JavaScript callback or main SVR.JS callback.
### Proxy callback API (`module.exports.proxy`)
_Added in SVR.JS 4.0.0_
#### _req_
_Added in SVR.JS 4.0.0_
_req_ object is the same, as _req_ object in Node.JS
#### _socket_
_Added in SVR.JS 4.0.0_
_socket_ object is the same, as _socket_ object in Node.JS
#### _head_
_Added in SVR.JS 4.0.0_
_head_ object is the same, as _head_ object in Node.JS
#### _logFacilities_
_Added in SVR.JS 4.0.0_
_See logFacilties in main callback API_
#### _config_
_Added in SVR.JS 4.0.0_
_See config in main callback API_
#### _next()_
_Added in SVR.JS 4.0.0_
_See next in main callback API_
### Global variables (for use in callback APIs)
#### _process.versions.svrjs_
_Added in SVR.JS 4.0.0_
A property containing SVR.JS version.
#### _process.serverConfiguration_
_Added in SVR.JS 4.0.0_
A property containing SVR.JS configuration from _config.json_ file.
#### _process.dirname_
_Added in SVR.JS 4.0.0_
A property containg the SVR.JS installation directory.
#### _process.filename_
_Added in SVR.JS 4.0.0_
A property containg the path to the SVR.JS script.
#### _process.err4xxcounter_
_Added in SVR.JS 4.0.0_
A property containg the count of 4xx HTTP errors.
#### _process.err5xxcounter_
_Added in SVR.JS 4.0.0_
A property containg the count of 5xx HTTP errors.
#### _process.reqcounter_
_Added in SVR.JS 4.0.0_
A property containg the count of HTTP requests.
#### _process.malformedcounter_
_Added in SVR.JS 4.0.0_
A property containg the count of malformed HTTP requests.
## 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:
```js
//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:
```js
//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:
```js
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:
```js
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):
```js
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_