pixl-server-web 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,6 +36,7 @@ This module is a component for use in [pixl-server](https://www.github.com/jhuck
36
36
  * [brotli_opts](#brotli_opts)
37
37
  * [default_acl](#default_acl)
38
38
  * [blacklist](#blacklist)
39
+ * [whitelist](#whitelist)
39
40
  * [allow_hosts](#allow_hosts)
40
41
  * [rewrites](#rewrites)
41
42
  * [redirects](#redirects)
@@ -64,6 +65,7 @@ This module is a component for use in [pixl-server](https://www.github.com/jhuck
64
65
  * [startup_message](#startup_message)
65
66
  * [debug_ttl](#debug_ttl)
66
67
  * [debug_bind_local](#debug_bind_local)
68
+ * [chaos](#chaos)
67
69
  * [https](#https)
68
70
  * [https_port](#https_port)
69
71
  * [https_alt_ports](#https_alt_ports)
@@ -118,7 +120,23 @@ This module is a component for use in [pixl-server](https://www.github.com/jhuck
118
120
  * [Determining HTTP or HTTPS](#determining-http-or-https)
119
121
  * [Self-Referencing URLs](#self-referencing-urls)
120
122
  * [Custom Method Handlers](#custom-method-handlers)
121
- * [Let's Encrypt SSL Certificates](#lets-encrypt-ssl-certificates)
123
+ * [Let's Encrypt / ACME TLS Certificates](#lets-encrypt--acme-tls-certificates)
124
+ + [ACME clients](#acme-clients)
125
+ + [Point your domain at your server](#point-your-domain-at-your-server)
126
+ + [Install Certbot](#install-certbot)
127
+ - [Ubuntu / Debian](#ubuntu--debian)
128
+ - [RHEL / CentOS / Fedora](#rhel--centos--fedora)
129
+ + [Option A: HTTP-01 (webroot)](#option-a-http-01-webroot)
130
+ - [Ensure HTTP is working on port 80](#ensure-http-is-working-on-port-80)
131
+ - [Issue a certificate using webroot](#issue-a-certificate-using-webroot)
132
+ + [Configure pixl-server-web for HTTPS](#configure-pixl-server-web-for-https)
133
+ + [Automatic renewal](#automatic-renewal)
134
+ - [Check that renewal timers are installed](#check-that-renewal-timers-are-installed)
135
+ + [Option B: DNS-01 with DNS API (wildcards, advanced)](#option-b-dns-01-with-dns-api-wildcards-advanced)
136
+ - [Using Certbot DNS plugins](#using-certbot-dns-plugins)
137
+ - [Using acme.sh](#using-acmesh)
138
+ + [Where your certificates live](#where-your-certificates-live)
139
+ + [Troubleshooting](#troubleshooting)
122
140
  * [Request Max Dump](#request-max-dump)
123
141
  - [License](#license)
124
142
 
@@ -822,6 +840,29 @@ When set to `true` and running in debug mode (i.e. `--debug` CLI flag on startup
822
840
 
823
841
  This feature defaults to `false` (disabled).
824
842
 
843
+ ## chaos
844
+
845
+ Use the `chaos` feature to introduce optional and random fault injection into your web requests. Used for testing purposes, this feature can introduce a random delay on every request, and also hijack requests and inject random error responses based on probabilities you specify. Here is how to use it:
846
+
847
+ ```json
848
+ "chaos": {
849
+ "enabled": true,
850
+ "uri": ".+",
851
+ "delay": {
852
+ "min":0,
853
+ "max":2000
854
+ },
855
+ "errors": {
856
+ "503 Service Unavailable": 0.1
857
+ },
858
+ "headers": {
859
+ "Retry-After": 10
860
+ }
861
+ }
862
+ ```
863
+
864
+ Set the `chaos.enabled` flag to `true` to enable fault injection. By default, all URIs will be affected, unless you specify a `chaos.uri` (regular expression) to limit the requests. Set `chaos.delay.min` and `chaos.delay.max` to the range you want to delay requests (in milliseconds). Fill the `chaos.errors` object the HTTP repsonse codes (and status messages) you want to see, and how often. The values are interpreted as probabilities from `0.0` (never) to `1.0` (always). In the above example, the `HTTP 503` error code will be injected approximately 10% of the time. When errors are injected, you can include additional response headers in the `chaos.headers` object.
865
+
825
866
  ## https
826
867
 
827
868
  This boolean allows you to enable HTTPS (SSL) support in the web server. It defaults to `false`. Note that you must also set `https_port`, and possibly `https_cert_file` and `https_key_file` for this to work.
@@ -1017,6 +1058,8 @@ callback(
1017
1058
 
1018
1059
  The content body can be a string, a [Buffer](https://nodejs.org/api/buffer.html) object, or a [readable stream](https://nodejs.org/api/stream.html#class-streamreadable).
1019
1060
 
1061
+ Note that you can omit the status text and just return a code, e.g. `"200"`, and the web server will fill in the text.
1062
+
1020
1063
  ### Custom Response
1021
1064
 
1022
1065
  The second type of response is to send content directly to the underlying Node.js server by yourself, using `args.response` (see below). If you do this, you can pass `true` to the callback function, indicating to the web server that you "handled" the response, and it shouldn't do anything else. Example:
@@ -1895,78 +1938,246 @@ server.WebServer.addMethodHandler( "OPTIONS", "CORS Preflight", function(args, c
1895
1938
  } );
1896
1939
  ```
1897
1940
 
1898
- ## Let's Encrypt SSL Certificates
1941
+ ## Let's Encrypt / ACME TLS Certificates
1942
+
1943
+ These are instructions for using [Let's Encrypt](https://letsencrypt.org/) TLS certificates with **pixl-server-web**: how to get your certificate issued and how to keep it renewed automatically.
1944
+
1945
+ The examples below use the domain `mydomain.com`. Replace this with your own real domain.
1946
+
1947
+ ### ACME clients
1948
+
1949
+ Let's Encrypt issues certificates using the **ACME protocol**. To talk ACME, you need an ACME *client*.
1899
1950
 
1900
- Here are instructions for using [Let's Encrypt](https://letsencrypt.org/) SSL certificates with pixl-server-web, specifically how to get your certificate issued and how to setup automatic renewal.
1951
+ For most people, Let's Encrypt recommends **Certbot** as the default ACME client:
1901
1952
 
1902
- The first thing you should do is make sure your server has a public IP address, and point your domain name to it using a DNS "A" record. For these examples we will be using the domain `mydomain.com`.
1953
+ https://letsencrypt.org/docs/client-options/
1903
1954
 
1904
- Next, you will need to manually install [certbot](https://certbot.eff.org) on your server. The easiest way to do this is to use the wrapper script [certbot-auto](https://certbot.eff.org/docs/install.html#certbot-auto), like this:
1955
+ Other popular clients include:
1956
+
1957
+ * **acme.sh** – pure shell, great DNS-API support.
1958
+ * Various language-specific clients (Go, Rust, Node, etc.) – see the Let's Encrypt “ACME clients” list for a full catalog.
1959
+
1960
+ These docs focus on **Certbot**, with a short note about acme.sh for DNS-based / wildcard setups.
1961
+
1962
+ ### Point your domain at your server
1963
+
1964
+ Before you can get a certificate:
1965
+
1966
+ 1. Make sure your server has a **public IPv4 (and/or IPv6) address**.
1967
+ 2. In your DNS provider’s control panel, create an **A record** for your domain:
1968
+ - Name: `@` (or `mydomain.com`, provider-specific)
1969
+ - Type: `A`
1970
+ - Value: your server’s IPv4 address
1971
+ - Optionally create an **AAAA record** for IPv6.
1972
+ 3. Wait for DNS to propagate.
1973
+
1974
+ You should be able to open http://mydomain.com/ in a browser and hit your server.
1975
+
1976
+ ### Install Certbot
1977
+
1978
+ 1. Go to: https://certbot.eff.org/
1979
+ 2. Select:
1980
+ - Your OS (e.g. “Ubuntu 24.04” or “Debian 12”)
1981
+ - Web server: **“None of the above (or other)”**
1982
+ 3. Follow the instructions shown there.
1983
+
1984
+ Typical examples:
1985
+
1986
+ #### Ubuntu / Debian
1987
+
1988
+ Certbot’s maintainers (EFF) now publish current builds only through Snap:
1905
1989
 
1906
1990
  ```sh
1907
- mkdir -p /usr/local/bin
1908
- curl -s https://dl.eff.org/certbot-auto > /usr/local/bin/certbot-auto
1909
- chmod a+x /usr/local/bin/certbot-auto
1991
+ sudo snap install --classic certbot
1992
+ sudo ln -s /snap/bin/certbot /usr/local/bin/certbot
1910
1993
  ```
1911
1994
 
1912
- We'll be using the [Webroot](https://certbot.eff.org/docs/using.html#webroot) method for authorization. Make sure you have a web server running on your server and listening on port 80 (only plain HTTP is required at this point). Assuming your web server's document root path is `/var/www/html` issue this command:
1995
+ #### RHEL / CentOS / Fedora
1996
+
1997
+ DNF is the correct command to use on RedHat and family:
1913
1998
 
1914
1999
  ```sh
1915
- /usr/local/bin/certbot-auto certonly --webroot -w /var/www/html -d mydomain.com
2000
+ sudo dnf install certbot
1916
2001
  ```
1917
2002
 
1918
- If you need certificates for multiple subdomains, you can repeat the `-d` flag, e.g. `-d mydomain.com -d www.mydomain.com`.
2003
+ Verify installation:
1919
2004
 
1920
- Then follow the instructions on the console. Certbot will ask you a number of questions including asking you for your e-mail address, accepting terms of service, etc. When you are done, you should see a success message like this:
2005
+ ```sh
2006
+ certbot --version
2007
+ ```
2008
+
2009
+ ### Option A: HTTP-01 (webroot)
2010
+
2011
+ HTTP-01 requires port **80** open and a web root directory that pixl-server-web (or another HTTP server) uses.
2012
+
2013
+ #### Ensure HTTP is working on port 80
2014
+
2015
+ Configure pixl-server-web (or any HTTP server) to:
2016
+
2017
+ - Listen on **port 80**.
2018
+ - Serve static content from a directory, for example `/var/www/html`.
2019
+
2020
+ Then visit: http://mydomain.com/
1921
2021
 
2022
+ #### Issue a certificate using webroot
2023
+
2024
+ ```sh
2025
+ sudo certbot certonly --webroot -w /var/www/html -d mydomain.com
1922
2026
  ```
1923
- IMPORTANT NOTES:
1924
- - Congratulations! Your certificate and chain have been saved at:
1925
- /etc/letsencrypt/live/mydomain.com/fullchain.pem
1926
- Your key file has been saved at:
1927
- /etc/letsencrypt/live/mydomain.com/privkey.pem
1928
- Your cert will expire on 2019-06-19. To obtain a new or tweaked
1929
- version of this certificate in the future, simply run certbot-auto
1930
- again. To non-interactively renew *all* of your certificates, run
1931
- "certbot-auto renew"
1932
- - Your account credentials have been saved in your Certbot
1933
- configuration directory at /etc/letsencrypt. You should make a
1934
- secure backup of this folder now. This configuration directory will
1935
- also contain certificates and private keys obtained by Certbot so
1936
- making regular backups of this folder is ideal.
1937
- - If you like Certbot, please consider supporting our work by:
1938
2027
 
1939
- Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
1940
- Donating to EFF: https://eff.org/donate-le
2028
+ Add more hostnames if needed:
2029
+
2030
+ ```sh
2031
+ sudo certbot certonly --webroot -w /var/www/html -d mydomain.com -d www.mydomain.com
1941
2032
  ```
1942
2033
 
1943
- Your SSL certificates are now ready to use in pixl-server-web. Simply add the following properties to your `WebServer` configuration object, replacing `mydomain.com` with your own domain name:
2034
+ Certbot will create:
2035
+
2036
+ ```
2037
+ /etc/letsencrypt/live/mydomain.com/fullchain.pem
2038
+ /etc/letsencrypt/live/mydomain.com/privkey.pem
2039
+ /etc/letsencrypt/live/mydomain.com/cert.pem
2040
+ /etc/letsencrypt/live/mydomain.com/chain.pem
2041
+ ```
2042
+
2043
+ ### Configure pixl-server-web for HTTPS
2044
+
2045
+ In your pixl-server-web config:
1944
2046
 
1945
2047
  ```js
1946
2048
  "https": true,
1947
2049
  "https_port": 443,
1948
- "https_cert_file": "/etc/letsencrypt/live/mydomain.com/cert.pem",
1949
- "https_key_file": "/etc/letsencrypt/live/mydomain.com/privkey.pem",
1950
- "https_ca_file": "/etc/letsencrypt/live/mydomain.com/chain.pem"
2050
+ "https_cert_file": "/etc/letsencrypt/live/mydomain.com/cert.pem",
2051
+ "https_key_file": "/etc/letsencrypt/live/mydomain.com/privkey.pem",
2052
+ "https_ca_file": "/etc/letsencrypt/live/mydomain.com/chain.pem"
2053
+ ```
2054
+
2055
+ Then restart pixl-server-web so it can bind to port 443.
2056
+
2057
+ Visit: https://mydomain.com/
2058
+
2059
+ ### Automatic renewal
2060
+
2061
+ Let's Encrypt certificates are valid for **90 days**.
2062
+
2063
+ Certbot automatically installs:
2064
+
2065
+ - A **systemd timer**, or
2066
+ - A **cron job**
2067
+
2068
+ that runs `certbot renew` twice a day.
2069
+
2070
+ pixl-server-web will automatically reload certs when they change on disk. There is no need to trigger a restart.
2071
+
2072
+ #### Check that renewal timers are installed
2073
+
2074
+ ```sh
2075
+ systemctl list-timers | grep certbot
2076
+ ```
2077
+
2078
+ Test renewal:
2079
+
2080
+ ```sh
2081
+ sudo certbot renew --dry-run
2082
+ ```
2083
+
2084
+ ### Option B: DNS-01 with DNS API (wildcards, advanced)
2085
+
2086
+ Use DNS-01 if:
2087
+
2088
+ * You want a **wildcard cert** (`*.mydomain.com`).
2089
+ * Port 80 is blocked or server is not exposed to the internet.
2090
+ * You want a central ACME box managing certs.
2091
+
2092
+ DNS-01 works by creating a TXT record:
2093
+
1951
2094
  ```
2095
+ _acme-challenge.mydomain.com
2096
+ ```
2097
+
2098
+ #### Using Certbot DNS plugins
1952
2099
 
1953
- Then start your server as root and it should accept `https://` requests on port 443.
2100
+ Example: Cloudflare
1954
2101
 
1955
- The final step is to make sure your certificates auto-renew before they expire (every 90 days). The `certbot-auto` command takes care of this, but we have to take care of invoking it ourselves, i.e. from a [crontab](https://en.wikipedia.org/wiki/Cron). It is recommended that you run the command every night, noting that it takes no action unless your certificates are about to expire (i.e. within 30 days).
2102
+ Install plugin (package varies by distro):
1956
2103
 
1957
- If your certificates were renewed, you will also need to restart pixl-server-web. The `certbot-auto` command can also do this for you, using a special `--post-hook` command-line argument. Example:
2104
+ ```
2105
+ sudo apt install python3-certbot-dns-cloudflare
2106
+ ```
2107
+
2108
+ Create credentials file:
2109
+
2110
+ `~/.secrets/certbot/cloudflare.ini`
2111
+
2112
+ ```ini
2113
+ dns_cloudflare_api_token = YOUR_TOKEN
2114
+ ```
2115
+
2116
+ Lock permissions:
1958
2117
 
1959
2118
  ```sh
1960
- /usr/local/bin/certbot-auto renew --post-hook "/opt/myapp/bin/control.sh restart"
2119
+ chmod 600 ~/.secrets/certbot/cloudflare.ini
1961
2120
  ```
1962
2121
 
1963
- Toss that command into a shell script in `/etc/cron.daily/` and it'll run daily at 4 AM local server time. Note that the command does produce output, even if your certs are not renewed, so you may want to silence it:
2122
+ Issue wildcard cert:
1964
2123
 
1965
2124
  ```sh
1966
- /usr/local/bin/certbot-auto renew --post-hook "/opt/myapp/bin/control.sh restart" >/dev/null 2>&1
2125
+ sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini -d mydomain.com -d '*.mydomain.com'
1967
2126
  ```
1968
2127
 
1969
- Certbot produces its own log file here: `/var/log/letsencrypt/letsencrypt.log`
2128
+ #### Using acme.sh
2129
+
2130
+ Install:
2131
+
2132
+ ```sh
2133
+ curl https://get.acme.sh | sh
2134
+ ```
2135
+
2136
+ Issue DNS-based cert:
2137
+
2138
+ ```sh
2139
+ ~/.acme.sh/acme.sh --issue --dns dns_cf -d mydomain.com -d '*.mydomain.com'
2140
+ ```
2141
+
2142
+ Install certs into your preferred paths:
2143
+
2144
+ ```sh
2145
+ ~/.acme.sh/acme.sh --install-cert -d mydomain.com --key-file /etc/letsencrypt/live/mydomain.com/privkey.pem --fullchain-file /etc/letsencrypt/live/mydomain.com/fullchain.pem
2146
+ ```
2147
+
2148
+ ### Where your certificates live
2149
+
2150
+ Default Certbot paths:
2151
+
2152
+ ```
2153
+ /etc/letsencrypt/live/mydomain.com/privkey.pem
2154
+ /etc/letsencrypt/live/mydomain.com/fullchain.pem
2155
+ /etc/letsencrypt/live/mydomain.com/cert.pem
2156
+ /etc/letsencrypt/live/mydomain.com/chain.pem
2157
+ ```
2158
+
2159
+ ### Troubleshooting
2160
+
2161
+ Test renewal:
2162
+
2163
+ ```sh
2164
+ sudo certbot renew --dry-run
2165
+ ```
2166
+
2167
+ Logs:
2168
+
2169
+ ```
2170
+ /var/log/letsencrypt/letsencrypt.log
2171
+ ```
2172
+
2173
+ - DNS-01 issues:
2174
+ - Verify TXT record exists using `dig` or `nslookup`.
2175
+ - Ensure your API credentials have minimum DNS permissions.
2176
+
2177
+ For more information:
2178
+
2179
+ - https://letsencrypt.org/docs/
2180
+ - https://certbot.eff.org/docs/
1970
2181
 
1971
2182
  ## Request Max Dump
1972
2183
 
package/lib/http.js CHANGED
@@ -209,7 +209,7 @@ module.exports = class HTTP {
209
209
  if (self.config.get('log_socket_errors')) {
210
210
  self.logError(err.code || 'socket', "Client error: " + socket._pixl_data.id + ": " + msg, err_args);
211
211
  }
212
- else {
212
+ else if (err.code != 'ECONNRESET') {
213
213
  self.logDebug(5, "Client error: " + socket._pixl_data.id + ": " + msg, err_args);
214
214
  }
215
215
 
package/lib/https.js CHANGED
@@ -300,7 +300,7 @@ module.exports = class HTTP2 {
300
300
  if (self.config.get('log_socket_errors')) {
301
301
  self.logError(err.code || 'socket', "Client error: " + socket._pixl_data.id + ": " + msg, err_args);
302
302
  }
303
- else {
303
+ else if (err.code != 'ECONNRESET') {
304
304
  self.logDebug(5, "Client error: " + socket._pixl_data.id + ": " + msg, err_args);
305
305
  }
306
306
 
package/lib/response.js CHANGED
@@ -72,10 +72,16 @@ module.exports = class Response {
72
72
  // parse code and status
73
73
  var http_code = 200;
74
74
  var http_status = "OK";
75
+
75
76
  if (status.match(/^(\d+)\s+(.+)$/)) {
76
77
  http_code = parseInt( RegExp.$1 );
77
78
  http_status = RegExp.$2;
78
79
  }
80
+ else if (this.responseCodes[status]) {
81
+ http_code = parseInt(status);
82
+ http_status = this.responseCodes[status];
83
+ }
84
+
79
85
  args.http_code = http_code;
80
86
  args.http_status = http_status;
81
87
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixl-server-web",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "A web server component for the pixl-server framework.",
5
5
  "author": "Joseph Huckaby <jhuckaby@gmail.com>",
6
6
  "homepage": "https://github.com/jhuckaby/pixl-server-web",
package/web_server.js CHANGED
@@ -85,7 +85,68 @@ module.exports = Class({
85
85
  stats: null,
86
86
  recent: null,
87
87
 
88
- badHeaderCharPattern: /([\x7F-\xFF\x00-\x1F\u00FF-\uFFFF])/g
88
+ badHeaderCharPattern: /([\x7F-\xFF\x00-\x1F\u00FF-\uFFFF])/g,
89
+
90
+ responseCodes: {
91
+ "100": "Continue",
92
+ "101": "Switching Protocols",
93
+ "102": "Processing",
94
+ "103": "Early Hints",
95
+ "200": "OK",
96
+ "201": "Created",
97
+ "202": "Accepted",
98
+ "203": "Non Authoritative Information",
99
+ "204": "No Content",
100
+ "205": "Reset Content",
101
+ "206": "Partial Content",
102
+ "207": "Multi-Status",
103
+ "300": "Multiple Choices",
104
+ "301": "Moved Permanently",
105
+ "302": "Moved Temporarily",
106
+ "303": "See Other",
107
+ "304": "Not Modified",
108
+ "305": "Use Proxy",
109
+ "307": "Temporary Redirect",
110
+ "308": "Permanent Redirect",
111
+ "400": "Bad Request",
112
+ "401": "Unauthorized",
113
+ "402": "Payment Required",
114
+ "403": "Forbidden",
115
+ "404": "Not Found",
116
+ "405": "Method Not Allowed",
117
+ "406": "Not Acceptable",
118
+ "407": "Proxy Authentication Required",
119
+ "408": "Request Timeout",
120
+ "409": "Conflict",
121
+ "410": "Gone",
122
+ "411": "Length Required",
123
+ "412": "Precondition Failed",
124
+ "413": "Request Entity Too Large",
125
+ "414": "Request-URI Too Long",
126
+ "415": "Unsupported Media Type",
127
+ "416": "Requested Range Not Satisfiable",
128
+ "417": "Expectation Failed",
129
+ "418": "I'm a teapot",
130
+ "419": "Insufficient Space on Resource",
131
+ "420": "Method Failure",
132
+ "421": "Misdirected Request",
133
+ "422": "Unprocessable Entity",
134
+ "423": "Locked",
135
+ "424": "Failed Dependency",
136
+ "426": "Upgrade Required",
137
+ "428": "Precondition Required",
138
+ "429": "Too Many Requests",
139
+ "431": "Request Header Fields Too Large",
140
+ "451": "Unavailable For Legal Reasons",
141
+ "500": "Internal Server Error",
142
+ "501": "Not Implemented",
143
+ "502": "Bad Gateway",
144
+ "503": "Service Unavailable",
145
+ "504": "Gateway Timeout",
146
+ "505": "HTTP Version Not Supported",
147
+ "507": "Insufficient Storage",
148
+ "511": "Network Authentication Required"
149
+ }
89
150
 
90
151
  },
91
152
  class WebServer extends Component {
@@ -122,10 +183,40 @@ class WebServer extends Component {
122
183
  this.server.on('ready', function() { setTimeout( self.postStartupMessage.bind(self), 250 ); } );
123
184
  }
124
185
 
186
+ // optional chaos (fault injection)
187
+ if (this.config.getPath('chaos.enabled')) this.setupChaos();
188
+
125
189
  // start listeners
126
190
  this.startAll(callback);
127
191
  }
128
192
 
193
+ setupChaos() {
194
+ // setup chaos system (random delays, errors, etc.)
195
+ // chaos: { enabled, uri?, delay?: { min:0, max:250 }, errors?: { "503 Service Unavailable": 0.1 }, headers? }
196
+ var self = this;
197
+ var chaos = this.config.get('chaos');
198
+
199
+ this.addURIFilter( new RegExp(chaos.uri || '.+'), "Chaos", function(args, callback) {
200
+ var ms = chaos.delay ? Math.round( chaos.delay.min + (Math.random() * (chaos.delay.max - chaos.delay.min)) ) : 0;
201
+ if (ms) self.logDebug(9, `Chaos: Delaying request for ${ms}ms`);
202
+
203
+ setTimeout( function() {
204
+ if (!chaos.errors) return callback(false); // passthru
205
+ var chosen = false;
206
+
207
+ for (var status in chaos.errors) {
208
+ if (Math.random() <= chaos.errors[status]) { chosen = status; break; }
209
+ }
210
+
211
+ if (chosen) {
212
+ self.logDebug(9, `Chaos: Injecting fault: $(chosen)`);
213
+ callback( chosen, chaos.headers || {}, "Simulated Error: " + chosen );
214
+ }
215
+ else callback(false);
216
+ }, ms); // setTimeout
217
+ }); // addURIFilter
218
+ }
219
+
129
220
  prepConfig() {
130
221
  // prep config at startup, and when config is hot reloaded
131
222