Added "js_load_http_native_module" and "js_load_stream_native_module"
main nginx.conf level directives. The directives load a dynamic
library. For security reason it is only allowed in the main context.
Later, JS code may import modules loaded with these directives
with standard import syntax.
example.conf:
...
js_load_http_native_module /path/to/lib.so;
js_load_http_native_module /path/to/lib2.so as lib2;
http {
js_import main.js;
...
main.js:
import * as lib from 'lib.so';
import * as lib2 from 'lib2';
...
See quickjs.h for the complete QuickJS API reference and
nginx/t/js_native_module.t for a working example.
Modules: extracted config-time merging into separate function.
This introduces ngx_js_merge_conftime_loc_conf() to handle merging
of configuration-time properties.
Normally ngx_js_merge_conf() does all the default value initialization
for child location configurations.
There is a special case for global "http" or "stream" configuration
where the parent configuration needs to be initialized (so it can be
reused by server configurations if no additional directives were
defined in them). But parent configurations are not initialized by
ngx_js_merge_conf().
Most of the ngx_js_loc_conf_t values are only used at runtime, so
only configuration-time values need to be merged in the parent.
The runtime values will be provided from the appropriate
ngx_js_loc_conf_t during request processing.
Previously, configuration-time merging was done inline. Extracting
it into a dedicated function simplifies adding new configuration-time
properties.
Dmitry Volyntsev [Sat, 10 Jan 2026 02:08:56 +0000 (18:08 -0800)]
QuickJS: fixed js_body_filter with multiple chunks.
Previously, last_key atoms was freed too early. Also, js_body_filter.t
is modified to ensure js_body_filter sees multiple data chains to catch
the issue.
Dmitry Volyntsev [Fri, 12 Dec 2025 05:57:18 +0000 (21:57 -0800)]
HTTP: fixed buffer_type inheritance in if blocks.
Previously, when js_body_filter was used inside an if block that
evaluated to true, the data parameter received Buffer type instead
of the expected String type. This happened because buffer_type field
in ngx_http_js_loc_conf_t was not properly initialized, causing the
configuration merge to fail when nginx created a new location context
for if blocks.
Previously, when upstream data was delivered from nginx cache
js_body_filter was not able to process it correctly. In particular,
it was treated as a chain of empty buffers.
The fix is to set r->filter_need_in_memory flag, which ensures
that ngx_http_core_module reads the data into memory before
js_body_filter sees it.
Dmitry Volyntsev [Fri, 24 Oct 2025 03:06:42 +0000 (20:06 -0700)]
Using printing and parsing library from QuickJS.
- Number.prototype.toString(radix)
Improved accuracy for edge cases
Reimplemented using njs_dtoa2() with JS_DTOA_FORMAT_FREE | JS_DTOA_EXP_DISABLED
- Number.prototype.toFixed(frac)
Reimplemented using njs_dtoa2() with JS_DTOA_FORMAT_FIXED
Removed old njs_fixed_dtoa() implementation
- Number.prototype.toPrecision(prec)
Reimplemented using njs_dtoa2() with precision format
Removed old njs_dtoa_precision() implementation
- Number.prototype.toExponential(frac)
Reimplemented using njs_dtoa2() with exponential format
Removed old njs_dtoa_exponential() implementation
- parseInt()
Simplified parsing implementation
Removed custom njs_number_radix_parse() helper
- parseFloat()
Simplified parsing implementation
Removed custom njs_number_bin_parse(), njs_number_oct_parse(),
njs_number_dec_parse() and njs_strtod.c module Better handling of large
numbers and denormal floats and invalid inputs.
Fetch: added forward proxy support with HTTPS tunneling.
Supports Basic authentication via Proxy-Authorization header.
- js_fetch_proxy - configures forward proxy URL. It takes proxy URL
as a parameter. The URL may optionally contain user and password.
Parameter value can contain variables. If value is empty,
forward proxy is disabled.
Dmitry Volyntsev [Tue, 14 Oct 2025 00:37:11 +0000 (17:37 -0700)]
QuickJS: fixed r.subrequest() to a location with JS handler.
Previously, when a subrequest location had a JS handler, an object of a
subrequest JS context was provided as an argument to a parent contexts.
This may cause all sorts of problems due to incorrect reference
counting.
After this change in bellard/quickjs@42eb2795 the bug became apparent.
The issue was introduced in commit 04f6dfb (0.9.2) by moving VM
destruction from the pool cleanup handler to the http cleanup handler.
Moving VM destruction to the http cleanup handler broke js_set variable
usage during the log phase, because these variables are called after the
VM has been destroyed.
The fix is to move VM destruction back to the pool cleanup handler, but
use a temporary pool while njs.on('exit', ...) is executing.
build/src/njs_object.dep -MT build/src/njs_object.o \ src/njs_object.c
In file included from src/njs_main.h:18, from src/njs_object.c:8: In
function ‘njs_utf8_copy’, inlined from ‘njs_object_enumerate_string’ at
src/njs_object.c:769:21: src/njs_utf8.h:115:20: error: writing 1 byte
into a region of size 0 [-Werror=stringop-overflow=] 115 |
*dst++ = c; | ~~~~~~~^~~ src/njs_object.c: In function
‘njs_object_enumerate_string’: src/njs_object.c:719:24: note: at offset
4 into destination object ‘buf’ of size 4 719 | u_char
buf[4], *c; | ^~~ In function ‘njs_utf8_copy’,
inlined from ‘njs_object_enumerate_string’ at src/njs_object.c:769:21:
src/njs_utf8.h:115:20: error: writing 1 byte into a region of size 0
[-Werror=stringop-overflow=]
GCC-15 does not know that the loop in njs_utf8_copy() is bounded
because the input is a valid UTF-8 here.
Dmitry Volyntsev [Thu, 28 Aug 2025 22:20:31 +0000 (15:20 -0700)]
QuickJS: fixed potential heap-use-after-free.
Previously in QuickJS engine, fields allocated from memory pool linked
to QuickJS engine lifetime were stored in nginx data structs.
This causes a heap-use-after-free if QuickJS engine is destroyed
earlier than a last access from nginx. For example, it becomes
visible when moving NJS cleanup handler from pool->cleanup to
r->cleanup.
The fix is to only store references in nginx objects allocated from
nginx memory pool.
Dmitry Volyntsev [Mon, 11 Aug 2025 23:25:47 +0000 (16:25 -0700)]
Change: increasing the default stack size to 160k.
This change allows EarleyBoyer benchmark from
arewefastyet/benchmarks/v8-v7 to pass with default settings.
Previous commit 5e9a6d5 (v0.7.9) reduced the stack size to prevent stack
overflow when compiling with -O0 in computed goto mode, where native
stack frames for njs_vmcode_interpreter() consume ~80KB each. To address
this, we now set a lower maximum stack size specifically for unit tests.
After bellard/quickjs@458c34d2 modules are treated as GC objects and
tracked in rt->gc_obj_list. Intermediary module object loaded in
ngx_qjs_clone() using JS_ReadObject() needed to be freed for proper
ref_count accounting.