pixl-server-web 1.3.25 → 1.3.26

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
@@ -111,26 +111,26 @@ This module is a component for use in [pixl-server](https://www.github.com/jhuck
111
111
 
112
112
  Use [npm](https://www.npmjs.com/) to install the module:
113
113
 
114
- ```
114
+ ```sh
115
115
  npm install pixl-server pixl-server-web
116
116
  ```
117
117
 
118
118
  Here is a simple usage example. Note that the component's official name is `WebServer`, so that is what you should use for the configuration key, and for gaining access to the component via your server object.
119
119
 
120
120
  ```js
121
- var PixlServer = require('pixl-server');
122
- var server = new PixlServer({
121
+ const PixlServer = require('pixl-server');
122
+ let server = new PixlServer({
123
123
 
124
124
  __name: 'MyServer',
125
125
  __version: "1.0",
126
126
 
127
127
  config: {
128
- "log_dir": "/var/log",
128
+ "log_dir": "/let/log",
129
129
  "debug_level": 9,
130
130
 
131
131
  "WebServer": {
132
132
  "http_port": 80,
133
- "http_htdocs_dir": "/var/www/html"
133
+ "http_htdocs_dir": "/let/www/html"
134
134
  }
135
135
  },
136
136
 
@@ -162,7 +162,7 @@ components: [
162
162
  ]
163
163
  ```
164
164
 
165
- This example is a very simple web server configuration, which will listen on port 80 and serve static files out of `/var/www/html`. However, if the URI is `/my/custom/uri`, a custom callback function is fired and can serve up any response it wants. This is a great way to implement an API.
165
+ This example is a very simple web server configuration, which will listen on port 80 and serve static files out of `/let/www/html`. However, if the URI is `/my/custom/uri`, a custom callback function is fired and can serve up any response it wants. This is a great way to implement an API.
166
166
 
167
167
  # Configuration
168
168
 
@@ -176,7 +176,7 @@ This is the main port to listen on. The standard web port is 80, but note that
176
176
 
177
177
  If you would like to have the server listen on additional ports, add them here as an array. Example:
178
178
 
179
- ```js
179
+ ```json
180
180
  {
181
181
  "http_port": 80,
182
182
  "http_alt_ports": [ 3000, 8080 ]
@@ -187,7 +187,7 @@ If you would like to have the server listen on additional ports, add them here a
187
187
 
188
188
  Optionally specify an exact local IP address to bind the listeners to. By default this binds to all available addresses on the machine. Example:
189
189
 
190
- ```js
190
+ ```json
191
191
  {
192
192
  "http_bind_address": "127.0.0.1"
193
193
  }
@@ -197,7 +197,7 @@ This example would cause the server to *only* listen on localhost, and not any e
197
197
 
198
198
  ## http_htdocs_dir
199
199
 
200
- This is the path to the directory to serve static files out of, e.g. `/var/www/html`.
200
+ This is the path to the directory to serve static files out of, e.g. `/let/www/html`.
201
201
 
202
202
  ## http_max_upload_size
203
203
 
@@ -239,7 +239,7 @@ This is a regular expression string used to determine if the incoming POST reque
239
239
 
240
240
  This param allows you to send back any additional custom HTTP headers with each response. Set the param to an object containing keys for each header, like this:
241
241
 
242
- ```js
242
+ ```json
243
243
  {
244
244
  "http_response_headers": {
245
245
  "X-My-Custom-Header": "12345",
@@ -252,7 +252,7 @@ This param allows you to send back any additional custom HTTP headers with each
252
252
 
253
253
  This property allows you to include *conditional* response headers, based on the HTTP response code. For example, you can instruct the web server to send back a custom header with `404` (File Not Found) responses, like this:
254
254
 
255
- ```js
255
+ ```json
256
256
  {
257
257
  "http_code_response_headers": {
258
258
  "404": {
@@ -264,7 +264,7 @@ This property allows you to include *conditional* response headers, based on the
264
264
 
265
265
  An actual useful case would be to include a [Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header with all `429` (Too Many Requests) responses, like this:
266
266
 
267
- ```js
267
+ ```json
268
268
  {
269
269
  "http_code_response_headers": {
270
270
  "429": {
@@ -280,7 +280,7 @@ This would give a hint to clients when they receive a `429` (Too Many Requests)
280
280
 
281
281
  This sets the idle socket timeout for all incoming HTTP requests, in seconds. If omitted, the Node.js default is 120 seconds. Example:
282
282
 
283
- ```js
283
+ ```json
284
284
  {
285
285
  "http_timeout": 120
286
286
  }
@@ -292,7 +292,7 @@ This only applies to reading from sockets when data is expected. It is an *idle
292
292
 
293
293
  This property sets an actual hard request timeout for all incoming requests. If the total combined request processing, handling and response time exceeds this value, specified in seconds, then the request is aborted and a `HTTP 408 Request Timeout` response is sent back to the client. This defaults to `0` (disabled). Example use:
294
294
 
295
- ```js
295
+ ```json
296
296
  {
297
297
  "http_request_timeout": 300
298
298
  }
@@ -306,7 +306,7 @@ This controls the [HTTP Keep-Alive](https://en.wikipedia.org/wiki/HTTP_persisten
306
306
 
307
307
  ### default
308
308
 
309
- ```js
309
+ ```json
310
310
  {
311
311
  "http_keep_alives": "default"
312
312
  }
@@ -316,7 +316,7 @@ This **enables** Keep-Alives for all incoming connections by default, unless the
316
316
 
317
317
  ### request
318
318
 
319
- ```js
319
+ ```json
320
320
  {
321
321
  "http_keep_alives": "request"
322
322
  }
@@ -326,7 +326,7 @@ This **disables** Keep-Alives for all incoming connections by default, unless th
326
326
 
327
327
  ### close
328
328
 
329
- ```js
329
+ ```json
330
330
  {
331
331
  "http_keep_alives": "close"
332
332
  }
@@ -336,9 +336,9 @@ This completely disables Keep-Alives for all connections. All requests result i
336
336
 
337
337
  ## http_keep_alive_timeout
338
338
 
339
- This sets the HTTP Keep-Alive idle timeout for all sockets, measured in seconds. If omitted, the Node.js default is 5 seconds. See [server.keepAliveTimeout](https://nodejs.org/api/http.html#http_server_keepalivetimeout) for details. Example:
339
+ This sets the HTTP Keep-Alive idle timeout for all sockets, measured in seconds. If omitted, the Node.js default is 5 seconds. See [server.keepAliveTimeout](https://nodejs.org/api/http.html#serverkeepalivetimeout) for details. Example:
340
340
 
341
- ```js
341
+ ```json
342
342
  {
343
343
  "http_keep_alive_timeout": 5
344
344
  }
@@ -348,7 +348,7 @@ This sets the HTTP Keep-Alive idle timeout for all sockets, measured in seconds.
348
348
 
349
349
  This sets a special preliminary timeout for brand new sockets when they are first connected, measured in seconds. If an HTTP request doesn't come over the socket within this timeout (specified in seconds), then the socket is hard closed. This timeout should always be set lower than the [http_timeout](#http_timeout) if used. This defaults to `0` (disabled). Example use:
350
350
 
351
- ```js
351
+ ```json
352
352
  {
353
353
  "http_socket_prelim_timeout": 3
354
354
  }
@@ -362,7 +362,7 @@ The idea here is to prevent certain DDoS-style attacks, where an attacker opens
362
362
 
363
363
  This allows you to set a maximum number of requests to allow per Keep-Alive connection. It defaults to `0` which means unlimited. If set, and the maximum is reached, a `Connection: close` header is returned, politely asking the client to close the connection. It does not actually hard-close the socket. Example:
364
364
 
365
- ```js
365
+ ```json
366
366
  {
367
367
  "http_max_requests_per_connection": 100
368
368
  }
@@ -372,16 +372,16 @@ This allows you to set a maximum number of requests to allow per Keep-Alive conn
372
372
 
373
373
  This allows you to set various options for the automatic GZip compression in HTTP responses. Example:
374
374
 
375
- ```js
375
+ ```json
376
376
  {
377
- http_gzip_opts: {
378
- level: 6,
379
- memLevel: 8
377
+ "http_gzip_opts": {
378
+ "level": 6,
379
+ "memLevel": 8
380
380
  }
381
381
  }
382
382
  ```
383
383
 
384
- Please see the Node [Zlib Class Options](https://nodejs.org/api/zlib.html#zlib_class_options) for more details on what can be set here.
384
+ Please see the Node [Zlib Class Options](https://nodejs.org/api/zlib.html#class-options) for more details on what can be set here.
385
385
 
386
386
  ## http_enable_brotli
387
387
 
@@ -393,26 +393,26 @@ Brotli is a newer compression format written by Google, which was added to Node.
393
393
 
394
394
  If [http_enable_brotli](#http_enable_brotli) is set to `true`, then you can set various options via the `http_brotli_opts` configuration property. Example:
395
395
 
396
- ```js
396
+ ```json
397
397
  {
398
- http_brotli_opts: {
399
- chunkSize: 16 * 1024,
400
- mode: "text",
401
- level: 4,
402
- hint: 0
398
+ "http_brotli_opts": {
399
+ "chunkSize": 16 * 1024,
400
+ "mode": "text",
401
+ "level": 4,
402
+ "hint": 0
403
403
  }
404
404
  }
405
405
  ```
406
406
 
407
- See the Node [Brotli Class Options](https://nodejs.org/api/zlib.html#zlib_class_brotlioptions) for more details on what can be set here. Note that `mode` is a convenience shortcut for `zlib.constants.BROTLI_PARAM_MODE` (which can set to `text`, `font` or `generic`), `level` is a shortcut for `zlib.constants.BROTLI_PARAM_QUALITY`, and `hint` is a shortcut for `zlib.constants.BROTLI_PARAM_SIZE_HINT`.
407
+ See the Node [Brotli Class Options](https://nodejs.org/api/zlib.html#class-brotlioptions) for more details on what can be set here. Note that `mode` is a convenience shortcut for `zlib.constants.BROTLI_PARAM_MODE` (which can set to `text`, `font` or `generic`), `level` is a shortcut for `zlib.constants.BROTLI_PARAM_QUALITY`, and `hint` is a shortcut for `zlib.constants.BROTLI_PARAM_SIZE_HINT`.
408
408
 
409
409
  ## http_default_acl
410
410
 
411
411
  This allows you to configure the default [ACL](https://en.wikipedia.org/wiki/Access_control_list), which is only used for URI handlers that register themselves as private. To customize it, specify an array of [IPv4](https://en.wikipedia.org/wiki/IPv4) and/or [IPv6](https://en.wikipedia.org/wiki/IPv6) addresses, partials or [CIDR blocks](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing). It defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) plus the [IPv4 private reserved](https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses) and [IPv6 private reserved ranges](https://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses). Example:
412
412
 
413
- ```js
413
+ ```json
414
414
  {
415
- http_default_acl: ['127.0.0.1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '::1/128', 'fd00::/8', '169.254.0.0/16', 'fe80::/10']
415
+ "http_default_acl": ["127.0.0.1", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "::1/128", "fd00::/8", "169.254.0.0/16", "fe80::/10"]
416
416
  }
417
417
  ```
418
418
 
@@ -422,6 +422,10 @@ See [Access Control Lists](#access-control-lists) below for more details.
422
422
 
423
423
  This boolean allows you to enable transaction logging in the web server. It defaults to `false` (disabled). See [Transaction Logging](#transaction-logging) below for details.
424
424
 
425
+ ## http_log_request_details
426
+
427
+ This boolean adds verbose detail in the transaction log. It defaults to `false` (disabled). See [Transaction Logging](#transaction-logging) below for details.
428
+
425
429
  ## http_regex_log
426
430
 
427
431
  If [http_log_requests](#http_log_requests) is enabled, this allows you to specify a regular expression to match against incoming request URIs. Only requests that match will be logged. It defaults to match all URIs (`.+`). See [Transaction Logging](#transaction-logging) below for details.
@@ -452,7 +456,7 @@ This integer specifies the maximum number of concurrent requests to allow. It d
452
456
 
453
457
  The idea here is that you can set [http_max_connections](#http_max_connections) to a much higher value, for things like load balancers pre-opening connections or clients using a pool of keep-alive connections, but then only allow your application code to process a smaller amount of requests in parallel. For example:
454
458
 
455
- ```js
459
+ ```json
456
460
  {
457
461
  "http_max_connections": 2048,
458
462
  "http_max_concurrent_requests": 64
@@ -494,7 +498,7 @@ With both `http_max_queue_length` and `http_max_queue_active` set to non-zero va
494
498
 
495
499
  The `http_queue_skip_uri_match` property is designed to work in conjunction with [http_max_concurrent_requests](#http_max_concurrent_requests). It allows you to specify a URI pattern match that will always skip over the queue and be processed immediately, regardless of limits. Using this feature you can allow things like health checks (possibly from a load balancer) to always be serviced, even during an overload situation. Example use:
496
500
 
497
- ```js
501
+ ```json
498
502
  {
499
503
  "http_queue_skip_uri_match": "^/server-status"
500
504
  }
@@ -510,7 +514,7 @@ This boolean enables HTTP response header cleansing. When set to `true` it will
510
514
 
511
515
  This boolean enables logging socket related errors, specifically sockets being closed unexpectedly (i.e. client closed socket, or some network error caused socket to abort). This defaults to `true`, meaning these will be logged as errors. If this generates too much log noise for your production stack, you can set the configuration property to `false`, which will only log a level 9 debug event. Example:
512
516
 
513
- ```js
517
+ ```json
514
518
  {
515
519
  "http_log_socket_errors": false
516
520
  }
@@ -526,7 +530,7 @@ Example error log entry:
526
530
 
527
531
  When this boolean is set to `true`, [Custom URI Handlers](#custom-uri-handlers) will match against the *full* incoming URI, including the query string. By default this is disabled, meaning URIs are only matched using their path. Example:
528
532
 
529
- ```js
533
+ ```json
530
534
  {
531
535
  "http_full_uri_match": true
532
536
  }
@@ -536,7 +540,7 @@ When this boolean is set to `true`, [Custom URI Handlers](#custom-uri-handlers)
536
540
 
537
541
  By default, we use the Node.js core [Query String](https://nodejs.org/api/querystring.html) module to parse query strings. This module handles duplicate query params by converting them to arrays. For example, an incoming URI such as `/something?foo=bar1&foo=bar2&name=joe` would produce the following `args.query` object:
538
542
 
539
- ```js
543
+ ```json
540
544
  {
541
545
  "foo": ["bar1", "bar2"],
542
546
  "name": "joe"
@@ -545,7 +549,7 @@ By default, we use the Node.js core [Query String](https://nodejs.org/api/querys
545
549
 
546
550
  However, if you set `http_flatten_query` to `true` in your configuration, the web server will "flatten" query string parameters, so that duplicate keys will be combined into one, with the latter prevailing. Example:
547
551
 
548
- ```js
552
+ ```json
549
553
  {
550
554
  "foo": "bar2",
551
555
  "name": "joe"
@@ -589,7 +593,7 @@ If HTTPS mode is enabled, this is the port to listen on for secure requests. Th
589
593
 
590
594
  If you would like to have the server listen on additional HTTPS ports, add them here as an array. Example:
591
595
 
592
- ```js
596
+ ```json
593
597
  {
594
598
  "https_port": 443,
595
599
  "https_alt_ports": [ 9000, 9001 ]
@@ -622,9 +626,9 @@ X-Forwarded-Proto: https
622
626
 
623
627
  The `https_header_detect` property allows you to define any number of header regular expression matches, that will "pseudo-enable" SSL mode in the web server. Meaning, the `args.request.headers.ssl` property will be set to `true`, and calls to `server.getSelfURL()` will have a `https://` prefix. Here is an example configuration, which detects many commonly used headers:
624
628
 
625
- ```js
629
+ ```json
626
630
  {
627
- https_header_detect: {
631
+ "https_header_detect": {
628
632
  "Front-End-Https": "^on$",
629
633
  "X-Url-Scheme": "^https$",
630
634
  "X-Forwarded-Protocol": "^https$",
@@ -710,15 +714,15 @@ Note that the `Content-Type` response header is automatically set based on the t
710
714
  If you would like to host static files in other places besides [http_htdocs_dir](#http_htdocs_dir), possibly with different options, then look no further than the `addDirectoryHandler()` method. This allows you to set up static file handling with a custom base URI, a custom base directory on disk, and apply other options as well. You can call this method as many times as you like to setup multiple static file directories. Example:
711
715
 
712
716
  ```js
713
- server.WebServer.addDirectoryHandler( /^\/mycustomdir/, '/var/www/custom' );
717
+ server.WebServer.addDirectoryHandler( /^\/mycustomdir/, '/let/www/custom' );
714
718
  ```
715
719
 
716
- The above example would catch all incoming requests starting with `/mycustomdir`, and serve up static files inside of the `/var/www/custom` directory on disk (and possibly nested directories as well). So a URL such as `http://MYSERVER/mycustomdir/foo/file1.txt` would map to the file `/var/www/custom/foo/file1.txt` on disk.
720
+ The above example would catch all incoming requests starting with `/mycustomdir`, and serve up static files inside of the `/let/www/custom` directory on disk (and possibly nested directories as well). So a URL such as `http://MYSERVER/mycustomdir/foo/file1.txt` would map to the file `/let/www/custom/foo/file1.txt` on disk.
717
721
 
718
722
  In this case a default TTL is applied to all files via [http_static_ttl](#http_static_ttl). If you would like to customize the TTL for your custom static directory, as well as specify other options, pass in an object as the 3rd argument to `addDirectoryHandler()`. Example of this:
719
723
 
720
724
  ```js
721
- server.WebServer.addDirectoryHandler( /^\/mycustomdir/, '/var/www/custom', {
725
+ server.WebServer.addDirectoryHandler( /^\/mycustomdir/, '/let/www/custom', {
722
726
  acl: true
723
727
  ttl: 3600,
724
728
  headers: {
@@ -751,7 +755,7 @@ callback(
751
755
  );
752
756
  ```
753
757
 
754
- 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#stream_class_stream_readable).
758
+ 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).
755
759
 
756
760
  ### Custom Response
757
761
 
@@ -760,7 +764,7 @@ The second type of response is to send content directly to the underlying Node.j
760
764
  ```js
761
765
  server.WebServer.addURIHandler( '/my/custom/uri', 'Custom Name', function(args, callback) {
762
766
  // send custom raw response
763
- var response = args.response;
767
+ let response = args.response;
764
768
  response.writeHead( 200, "OK", { 'Content-Type': "text/html" } );
765
769
  response.write( "Hello this is custom content!\n" );
766
770
  response.end();
@@ -854,7 +858,7 @@ Your URI handler function is passed an `args` object containing the following pr
854
858
 
855
859
  ### args.request
856
860
 
857
- This is a reference to the underlying [Node.js server request](https://nodejs.org/api/http.html#http_http_incomingmessage) object. From this you have access to things like:
861
+ This is a reference to the underlying [Node.js server request](https://nodejs.org/api/http.html#class-httpincomingmessage) object. From this you have access to things like:
858
862
 
859
863
  | Property | Description |
860
864
  |----------|-------------|
@@ -864,11 +868,11 @@ This is a reference to the underlying [Node.js server request](https://nodejs.or
864
868
  | `request.url` | The complete URI of the request (sans protocol and hostname). |
865
869
  | `request.socket` | A reference to the underlying socket connection for the request. |
866
870
 
867
- For more detailed documentation on the request object, see Node's [http.IncomingMessage](https://nodejs.org/api/http.html#http_http_incomingmessage).
871
+ For more detailed documentation on the request object, see Node's [http.IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage).
868
872
 
869
873
  ### args.response
870
874
 
871
- This is a reference to the underlying [Node.js server response](https://nodejs.org/api/http.html#http_class_http_serverresponse) object. From this you have access to things like:
875
+ This is a reference to the underlying [Node.js server response](https://nodejs.org/api/http.html#class-httpserverresponse) object. From this you have access to things like:
872
876
 
873
877
  | Property / Method() | Description |
874
878
  |----------|-------------|
@@ -880,14 +884,16 @@ This is a reference to the underlying [Node.js server response](https://nodejs.o
880
884
  | `response.write()` | This writes a chunk of data to the socket. |
881
885
  | `response.end()` | This indicates that the response has been completely sent. |
882
886
 
883
- For more detailed documentation on the response object, see Node's [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse).
887
+ For more detailed documentation on the response object, see Node's [http.ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse).
884
888
 
885
889
  ### args.ip
886
890
 
887
- This will be set to the user's remote IP address. Specifically, it will be set to the *first public IP address* if multiple addresses are provided via proxy HTTP headers and the socket.
891
+ This will be set to the user's remote IP address. Generally, it will be set to the *first public IP address* if multiple addresses are provided via proxy HTTP headers and the socket.
888
892
 
889
893
  Meaning, if the user is sitting behind one or more proxy servers, *or* your web server is behind a load balancer, this will attempt to locate the user's true public (non-private) IP address. If none is found, it'll just return the first IP address, honoring proxy headers before the socket (which is usually correct).
890
894
 
895
+ See [http_public_ip_offset](https://github.com/jhuckaby/pixl-server-web#http_public_ip_offset) for details on customizing the behavior of this property.
896
+
891
897
  If you just want the socket IP by itself, you can get it from `args.request.socket.remoteAddress`.
892
898
 
893
899
  ### args.ips
@@ -914,14 +920,14 @@ This will be an object containing key/value pairs from the URL query string, if
914
920
 
915
921
  Duplicate query params become an array. For example, an incoming URI such as `/something?foo=bar1&foo=bar2&name=joe` would produce the following `args.query` object:
916
922
 
917
- ```js
923
+ ```json
918
924
  {
919
925
  "foo": ["bar1", "bar2"],
920
926
  "name": "joe"
921
927
  }
922
928
  ```
923
929
 
924
- See [http_flatten_query](#http_flatten_query) if you would rather the query string be flattened.
930
+ See [http_flatten_query](#http_flatten_query) if you would rather duplicate query parameters be flattened (latter prevails).
925
931
 
926
932
  ### args.params
927
933
 
@@ -960,7 +966,7 @@ All temp files are automatically deleted at the end of the request.
960
966
  This is an object parsed from the incoming `Cookie` HTTP header, if present. The contents will be key/value pairs for each semicolon-separated cookie provided. For example, if the client sent in a `session_id` cookie, it could be accessed like this:
961
967
 
962
968
  ```js
963
- var session_id = args.cookies['session_id'];
969
+ let session_id = args.cookies['session_id'];
964
970
  ```
965
971
 
966
972
  ### args.perf
@@ -1043,13 +1049,16 @@ The log columns are configurable in pixl-server, but are typically the following
1043
1049
 
1044
1050
  The `data` column is a JSON document containing various bits of additional information about the request. Here is a formatted example:
1045
1051
 
1046
- ```js
1052
+ ```json
1047
1053
  {
1048
1054
  "id": "r4",
1049
1055
  "proto": "http",
1056
+ "ip": "::ffff:127.0.0.1",
1050
1057
  "ips": [
1051
1058
  "::ffff:127.0.0.1"
1052
1059
  ],
1060
+ "port": 3012,
1061
+ "socket": "c13",
1053
1062
  "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/601.6.17 (KHTML, like Gecko) Version/9.1.1 Safari/601.6.17",
1054
1063
  "host": "localhost",
1055
1064
  "perf": {
@@ -1071,30 +1080,145 @@ The `data` column is a JSON document containing various bits of additional infor
1071
1080
 
1072
1081
  Here are descriptions of the data JSON properties:
1073
1082
 
1074
- | Property | Description |
1075
- |----------|-------------|
1076
- | `id` | The internal ID for the request. |
1077
- | `proto` | The protocol of the request (`http` or `https`). |
1078
- | `ips` | All the client IPs as an array (includes those from proxy headers). |
1079
- | `ua` | The `User-Agent` string from the request headers. |
1080
- | `host` | The hostname from the request URL. |
1081
- | `perf` | Performance metrics, see below. |
1083
+ | Property | Type | Description |
1084
+ |----------|------|-------------|
1085
+ | `id` | String | The internal ID for the request. |
1086
+ | `proto` | String | The protocol of the request (`http` or `https`). |
1087
+ | `ip` | String | The first non-internal IP address (see [args.ip](#argsip)). |
1088
+ | `ips` | Array | All the client IPs as an array (includes those from proxy headers). |
1089
+ | `port` | Number | Which port number the request came in on. |
1090
+ | `socket` | String | The unique ID of the socket which served the request. |
1091
+ | `ua` | String | The `User-Agent` string from the request headers. |
1092
+ | `host` | String | The hostname from the request URL. |
1093
+ | `perf` | Object | Performance metrics, see below. |
1082
1094
 
1083
1095
  The `perf` object contains performance metrics for the request, as returned from the [pixl-perf](https://www.github.com/jhuckaby/pixl-perf) module. It includes a `scale` property denoting that all the metrics are displayed in milliseconds (i.e. `1000`). The metrics themselves are in the `perf` object, and counters such as the number of bytes in/out are in the `counters` object.
1084
1096
 
1085
1097
  If you only want to log *some* requests, but not all of them, you can specify a regular expression in the [http_regex_log](#http_regex_log) configuration property, which is matched against the incoming request URIs. Example:
1086
1098
 
1087
- ```js
1099
+ ```json
1088
1100
  {
1089
1101
  "http_regex_log": "^/my/special/path"
1090
1102
  }
1091
1103
  ```
1092
1104
 
1105
+ ## Request Detail Logging
1106
+
1107
+ If you set both the [http_log_requests](#http_log_requests) and [http_log_request_details](#http_log_request_details) configuration properties to `true`, pixl-server will include verbose details in the transaction logs, specifically in the JSON-formatted `data` column. It will include the raw request and raw response (if in text format), and extra details about both the request and the response. Example of the `data` column from the log, pretty-printed:
1108
+
1109
+ ```json
1110
+ {
1111
+ "id": "r10",
1112
+ "proto": "http",
1113
+ "ip": "::1",
1114
+ "ips": [
1115
+ "::1"
1116
+ ],
1117
+ "port": 3012,
1118
+ "socket": "c8",
1119
+ "perf": {
1120
+ "scale": 1000,
1121
+ "perf": {
1122
+ "total": 22.689,
1123
+ "queue": 0.261,
1124
+ "read": 15.176,
1125
+ "process": 1.791,
1126
+ "encode": 1.281,
1127
+ "write": 1.159
1128
+ },
1129
+ "counters": {
1130
+ "bytes_in": 975133,
1131
+ "bytes_out": 413,
1132
+ "num_requests": 1
1133
+ }
1134
+ },
1135
+ "files": {
1136
+ "file1": {
1137
+ "path": "/var/folders/11/r_0sz6s13cx1jn68l4m90zfr0000gn/T/f92cd259263698f0e19581400.LBM",
1138
+ "type": "application/octet-stream",
1139
+ "name": "V04.LBM",
1140
+ "size": 318742,
1141
+ "mtime": "2024-03-18T20:45:01.328Z"
1142
+ }
1143
+ },
1144
+ "headers": {
1145
+ "host": "localhost:3012",
1146
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
1147
+ "sec-fetch-site": "same-origin",
1148
+ "accept-language": "en-US,en;q=0.9",
1149
+ "accept-encoding": "gzip, deflate",
1150
+ "sec-fetch-mode": "navigate",
1151
+ "content-type": "multipart/form-data; boundary=----WebKitFormBoundaryAzquNdwdvTjj9ArR",
1152
+ "origin": "http://localhost:3012",
1153
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15",
1154
+ "referer": "http://localhost:3012/upload.html",
1155
+ "upgrade-insecure-requests": "1",
1156
+ "content-length": "319132",
1157
+ "connection": "keep-alive",
1158
+ "sec-fetch-dest": "document"
1159
+ },
1160
+ "cookies": {},
1161
+ "query": {
1162
+ "pretty": "1"
1163
+ },
1164
+ "params": {
1165
+ "key1": "value1",
1166
+ "key2": "value2"
1167
+ },
1168
+ "response": {
1169
+ "code": 200,
1170
+ "status": "OK",
1171
+ "headers": {
1172
+ "content-type": "application/json",
1173
+ "x-joetest": "9876",
1174
+ "server": "Test Server 1.0",
1175
+ "x-200": "YUP BRO",
1176
+ "content-length": "261",
1177
+ "content-encoding": "gzip"
1178
+ },
1179
+ "raw": "{\n\t\"code\": 0,\n\t\"query\": {\n\t\t\"pretty\": \"1\"\n\t},\n\t\"params\": {\n\t\t\"key1\": \"value1\",\n\t\t\"key2\": \"value2\"\n\t},\n\t\"cookies\": {},\n\t\"files\": {\n\t\t\"file1\": {\n\t\t\t\"path\": \"/var/folders/11/r_0sz6s13cx1jn68l4m90zfr0000gn/T/f92cd259263698f0e19581400.LBM\",\n\t\t\t\"type\": \"application/octet-stream\",\n\t\t\t\"name\": \"V04.LBM\",\n\t\t\t\"size\": 318742,\n\t\t\t\"mtime\": \"2024-03-18T20:45:01.328Z\"\n\t\t}\n\t}\n}\n"
1180
+ }
1181
+ }
1182
+ ```
1183
+
1184
+ As you can see, in addition to all the information logged with [http_log_requests](#http_log_requests), the `data` column now includes even more detail. Here is the full list of all JSON properties and their descriptions, logged with [http_log_request_details](#http_log_request_details) enabled:
1185
+
1186
+ | Property | Type | Description |
1187
+ |----------|------|-------------|
1188
+ | `id` | String | The internal ID for the request. |
1189
+ | `proto` | String | The protocol of the request (`http` or `https`). |
1190
+ | `ip` | String | The first non-internal IP address (see [args.ip](#argsip)). |
1191
+ | `ips` | Array | All the client IPs as an array (includes those from proxy headers). |
1192
+ | `port` | Number | Which port number the request came in on. |
1193
+ | `socket` | String | The unique ID of the socket which served the request. |
1194
+ | `perf` | Object | Performance metrics, in [pixl-perf](https://github.com/jhuckaby/pixl-perf) format. |
1195
+ | `files` | Object | If applicable, metadata about all file uploads (file names, sizes, types, and dates). |
1196
+ | `headers` | Object | All the HTTP request headers in key/value format (lower-cased keys). |
1197
+ | `cookies` | Object | Cookies from the request, parsed and in key/value form. |
1198
+ | `query` | Object | The query string from the request URL parsed into key/value pairs. |
1199
+ | `params` | Object | Key/value pairs from the request, i.e. parsed JSON or form POST data. |
1200
+ | `params.raw` | String | If applicable, the raw request body as a UTF-8 string (see below). |
1201
+ | `response` | Object | Details about the HTTP response sent to the client. |
1202
+ | `response.code` | Number | The HTTP response code (e.g. `200`). |
1203
+ | `response.status` | String | The HTTP response status (e.g. `OK`). |
1204
+ | `response.headers` | Object | All the HTTP response headers sent to the client (lower-cased keys). |
1205
+ | `response.raw` | String | If applicable, the raw response body as a UTF-8 string (see below). |
1206
+
1207
+ The raw request and response content will only be logged in certain cases:
1208
+
1209
+ - If the request was a JSON POST, then the parsed JSON document will be in the `params` object.
1210
+ - If the request was a non-JSON POST, but the content is recognized to be text, then the raw request body will be in `params.raw` as a UTF-8 string.
1211
+ - If the request was a form post, then the key/value pairs will be in the `params` object.
1212
+ - If the request contained file uploads, they will be summarized in the `files` object (see above for example).
1213
+ - If the response is recognized as text, it will be included in `response.raw` as a UTF-8 string.
1214
+ - If the response is non-text (binary), the raw content will not be included.
1215
+ - If the response is a stream, it will not be included.
1216
+
1093
1217
  ## Performance Threshold Logging
1094
1218
 
1095
1219
  In addition to [Transaction Logging](#transaction-logging), pixl-server-web can also log performance metrics for certain requests, if the total request elapsed time meets or exceeds a custom threshold. This allows you to log only "slow" requests, i.e. those possibly requiring investigation. This is an optional feature which is disabled by default. To enable it, set the [http_log_perf](#http_log_perf) configuration property to `true`, and then set the [http_perf_threshold_ms](#http_perf_threshold_ms) property to the desired logging threshold in milliseconds. Example:
1096
1220
 
1097
- ```js
1221
+ ```json
1098
1222
  {
1099
1223
  "http_log_perf": true,
1100
1224
  "http_perf_threshold_ms": 100
@@ -1122,7 +1246,7 @@ The log columns are configurable in [pixl-server](https://github.com/jhuckaby/pi
1122
1246
 
1123
1247
  The `data` column is a JSON document containing various bits of additional information about the request, including the performance metrics. Here is a formatted example:
1124
1248
 
1125
- ```js
1249
+ ```json
1126
1250
  {
1127
1251
  "id": "r4",
1128
1252
  "proto": "http",
@@ -1176,7 +1300,7 @@ The performance threshold system retroactively adjusts the log to represent the
1176
1300
 
1177
1301
  To include a partial or complete [Node.js Diagnostic Report](https://nodejs.org/docs/latest/api/report.html) in your performance log data, set the [http_perf_report](#http_perf_report) configuration property. For a full report, set it to `true`:
1178
1302
 
1179
- ```js
1303
+ ```json
1180
1304
  {
1181
1305
  "http_perf_report": true
1182
1306
  }
@@ -1184,7 +1308,7 @@ To include a partial or complete [Node.js Diagnostic Report](https://nodejs.org/
1184
1308
 
1185
1309
  However, please note that this is *very* verbose. For a partial report, you can set it to an array of report keys to include. Example:
1186
1310
 
1187
- ```js
1311
+ ```json
1188
1312
  {
1189
1313
  "http_perf_report": ["uvthreadResourceUsage"]
1190
1314
  }
@@ -1235,12 +1359,12 @@ See the [pixl-perf](https://www.github.com/jhuckaby/pixl-perf) documentation for
1235
1359
  The web server keeps internal statistics including all open sockets, all active and recently completed requests, and performance metrics. You can query for these by calling the `getStats()` method on the web server component. Example:
1236
1360
 
1237
1361
  ```js
1238
- var stats = server.WebServer.getStats();
1362
+ let stats = server.WebServer.getStats();
1239
1363
  ```
1240
1364
 
1241
1365
  The result is an object in this format:
1242
1366
 
1243
- ```js
1367
+ ```json
1244
1368
  {
1245
1369
  "server": {
1246
1370
  "uptime": 80,
@@ -1496,13 +1620,13 @@ See the [https_header_detect](#https_header_detect) configuration property for a
1496
1620
  To build a URL that points at the current server, call `getSelfURL()` and pass in the `args.request` object. This will produce a URL using the same protocol as the request (HTTP or HTTPS), the same hostname used on the request, and the port number if applicable. By default, the URL will point to the root path (`/`). Example:
1497
1621
 
1498
1622
  ```js
1499
- var url = server.WebServer.getSelfURL(args.request);
1623
+ let url = server.WebServer.getSelfURL(args.request);
1500
1624
  ```
1501
1625
 
1502
1626
  You can optionally pass in a URI path as the second argument. For example, to build a URL to the exact request URI that came in, pass in `args.request.url` as the second argument:
1503
1627
 
1504
1628
  ```js
1505
- var url = server.WebServer.getSelfURL(args.request, args.request.url);
1629
+ let url = server.WebServer.getSelfURL(args.request, args.request.url);
1506
1630
  ```
1507
1631
 
1508
1632
  ## Custom Method Handlers
@@ -1531,7 +1655,7 @@ server.WebServer.addMethodHandler( "OPTIONS", "CORS Preflight", function(args, c
1531
1655
 
1532
1656
  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.
1533
1657
 
1534
- 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 I will be using the domain `mydomain.com`.
1658
+ 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`.
1535
1659
 
1536
1660
  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:
1537
1661
 
@@ -1541,10 +1665,10 @@ curl -s https://dl.eff.org/certbot-auto > /usr/local/bin/certbot-auto
1541
1665
  chmod a+x /usr/local/bin/certbot-auto
1542
1666
  ```
1543
1667
 
1544
- 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:
1668
+ 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 `/let/www/html` issue this command:
1545
1669
 
1546
1670
  ```sh
1547
- /usr/local/bin/certbot-auto certonly --webroot -w /var/www/html -d mydomain.com
1671
+ /usr/local/bin/certbot-auto certonly --webroot -w /let/www/html -d mydomain.com
1548
1672
  ```
1549
1673
 
1550
1674
  If you need certificates for multiple subdomains, you can repeat the `-d` flag, e.g. `-d mydomain.com -d www.mydomain.com`.
@@ -1598,7 +1722,7 @@ Toss that command into a shell script in `/etc/cron.daily/` and it'll run daily
1598
1722
  /usr/local/bin/certbot-auto renew --post-hook "/opt/myapp/bin/control.sh restart" >/dev/null 2>&1
1599
1723
  ```
1600
1724
 
1601
- Certbot produces its own log file here: `/var/log/letsencrypt/letsencrypt.log`
1725
+ Certbot produces its own log file here: `/let/log/letsencrypt/letsencrypt.log`
1602
1726
 
1603
1727
  ## Request Max Dump
1604
1728
 
@@ -1612,30 +1736,32 @@ To enable this feature, set the [http_req_max_dump_enabled](#http_req_max_dump_e
1612
1736
 
1613
1737
  ```json
1614
1738
  "http_req_max_dump_enabled": true,
1615
- "http_req_max_dump_dir": "/var/log/web-server-dumps",
1739
+ "http_req_max_dump_dir": "/let/log/web-server-dumps",
1616
1740
  "http_req_max_dump_debounce": 10
1617
1741
  ```
1618
1742
 
1619
- This would generate dump files in the `/var/log/web-server-dumps` directory every 10 seconds, while one or more maximum limits are maxed out.
1743
+ This would generate dump files in the `/let/log/web-server-dumps` directory every 10 seconds, while one or more maximum limits are maxed out.
1620
1744
 
1621
- The dump files themselves are in JSON format, and contain everything from the [Stats API](#stats), as well as a list of all active and pending requests. For each request, the following information is dumped:
1745
+ The dump files themselves are in JSON format, and contain everything from the [Stats API](#stats), as well as a list of all active and pending requests. For each request, an object like the following is provided:
1622
1746
 
1623
1747
  ```json
1624
- "r2945": {
1625
- "uri": "/api/test/sleep?ms=1",
1626
- "ip": "127.0.0.1",
1627
- "ips": [
1628
- "127.0.0.1"
1629
- ],
1630
- "headers": {
1631
- "accept-encoding": "gzip, deflate, br",
1632
- "user-agent": "Mozilla/5.0; wperf/1.0.4",
1633
- "host": "localhost:3012",
1634
- "connection": "keep-alive"
1635
- },
1636
- "state": "writing",
1637
- "date": 1644617758.688,
1638
- "elapsed": 0.009999990463256836
1748
+ {
1749
+ "r2945": {
1750
+ "uri": "/api/test/sleep?ms=1",
1751
+ "ip": "127.0.0.1",
1752
+ "ips": [
1753
+ "127.0.0.1"
1754
+ ],
1755
+ "headers": {
1756
+ "accept-encoding": "gzip, deflate, br",
1757
+ "user-agent": "Mozilla/5.0; wperf/1.0.4",
1758
+ "host": "localhost:3012",
1759
+ "connection": "keep-alive"
1760
+ },
1761
+ "state": "writing",
1762
+ "date": 1644617758.688,
1763
+ "elapsed": 0.009999990463256836
1764
+ }
1639
1765
  }
1640
1766
  ```
1641
1767
 
package/lib/response.js CHANGED
@@ -102,6 +102,12 @@ module.exports = class Response {
102
102
  headers['Content-Length'] = body.length;
103
103
  }
104
104
 
105
+ // copy stuff into args for detail logging
106
+ if (this.logRequestDetails) {
107
+ args.resp_body = body;
108
+ args.resp_headers = headers;
109
+ }
110
+
105
111
  // track stream bytes, if applicable
106
112
  var meter = null;
107
113
 
@@ -322,16 +328,63 @@ module.exports = class Response {
322
328
 
323
329
  // write to access log
324
330
  if (this.logRequests && args.request.url.match(this.regexLogRequests)) {
325
- this.logTransaction( 'HTTP ' + args.http_code + ' ' + args.http_status, args.request.url, {
331
+ var data = {
326
332
  id: args.id,
327
333
  proto: args.request.headers['ssl'] ? 'https' : socket_data.proto,
334
+ ip: args.ip,
328
335
  ips: args.ips,
329
336
  port: socket_data.port,
330
337
  socket: socket_data.id,
331
- host: args.request.headers['host'] || '',
332
- ua: args.request.headers['user-agent'] || '',
333
338
  perf: metrics
334
- } );
339
+ };
340
+
341
+ if (this.logRequestDetails) {
342
+ // extra transaction log details
343
+ data.files = args.files || {};
344
+ data.headers = args.request.headers || {};
345
+ data.cookies = args.cookies || {};
346
+ data.query = args.query || {};
347
+ data.params = Object.assign( {}, args.params || {} );
348
+
349
+ if (data.params.raw && data.params.raw.buffer && data.params.raw.toString) {
350
+ if (args.request.headers['content-type'] && args.request.headers['content-type'].match(/(text|javascript|json|xml)/)) {
351
+ data.params.raw = data.params.raw.toString('utf8');
352
+ }
353
+ else data.params.raw = '(Binary Buffer)';
354
+ }
355
+
356
+ data.response = {
357
+ code: args.http_code,
358
+ status: args.http_status,
359
+ headers: {}
360
+ };
361
+
362
+ if (args.resp_headers) {
363
+ for (var key in args.resp_headers) {
364
+ data.response.headers[ key.toLowerCase() ] = args.resp_headers[key];
365
+ }
366
+ }
367
+
368
+ if (args.resp_body && args.resp_body.pipe) {
369
+ data.response.raw = '(Stream)';
370
+ }
371
+ else if (args.resp_body && args.resp_body.buffer && args.resp_body.toString) {
372
+ if (data.response.headers && data.response.headers['content-type'] && data.response.headers['content-type'].match(/(text|javascript|json|xml)/)) {
373
+ data.response.raw = args.resp_body.toString('utf8');
374
+ }
375
+ else data.response.raw = '(Binary Buffer)';
376
+ }
377
+
378
+ delete args.resp_body;
379
+ delete args.resp_headers;
380
+ }
381
+ else {
382
+ // standard transaction log
383
+ data.host = args.request.headers['host'] || '';
384
+ data.ua = args.request.headers['user-agent'] || '';
385
+ }
386
+
387
+ this.logTransaction( 'HTTP ' + args.http_code + ' ' + args.http_status, args.request.url, data );
335
388
  }
336
389
 
337
390
  // optional threshold-based perf log
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixl-server-web",
3
- "version": "1.3.25",
3
+ "version": "1.3.26",
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
@@ -47,6 +47,7 @@ module.exports = Class({
47
47
  "http_enable_brotli": false,
48
48
  "http_default_acl": ['127.0.0.1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '::1/128', 'fd00::/8', '169.254.0.0/16', 'fe80::/10'],
49
49
  "http_log_requests": false,
50
+ "http_log_request_details": false,
50
51
  "http_log_perf": false,
51
52
  "http_perf_threshold_ms": 100,
52
53
  "http_perf_report": false,
@@ -102,6 +103,7 @@ class WebServer extends Component {
102
103
  this.regexTextContent = new RegExp( this.config.get('http_regex_text'), "i" );
103
104
  this.regexJSONContent = new RegExp( this.config.get('http_regex_json'), "i" );
104
105
  this.logRequests = this.config.get('http_log_requests');
106
+ this.logRequestDetails = this.config.get('http_log_request_details');
105
107
  this.regexLogRequests = this.logRequests ? (new RegExp( this.config.get('http_regex_log') || '.+' )) : null;
106
108
  this.logPerfEnabled = this.config.get('http_log_perf');
107
109
  this.logPerfThreshold = this.config.get('http_perf_threshold_ms');