[njs] Modules: added js_preload_object directive.
Vadim Zhestikov
v.zhestikov at f5.com
Fri Sep 16 19:29:24 UTC 2022
details: https://hg.nginx.org/njs/rev/52b3e1f2a3e1
branches:
changeset: 1957:52b3e1f2a3e1
user: Vadim Zhestikov <v.zhestikov at f5.com>
date: Fri Sep 16 12:27:40 2022 -0700
description:
Modules: added js_preload_object directive.
diffstat:
nginx/ngx_http_js_module.c | 100 +++++++++++++++++++-
nginx/ngx_js.c | 200 +++++++++++++++++++++++++++++++++++++++++++
nginx/ngx_js.h | 7 +-
nginx/ngx_stream_js_module.c | 100 +++++++++++++++++++-
4 files changed, 392 insertions(+), 15 deletions(-)
diffs (599 lines):
diff -r 82f41f43abd4 -r 52b3e1f2a3e1 nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c Thu Sep 15 20:20:11 2022 -0700
+++ b/nginx/ngx_http_js_module.c Fri Sep 16 12:27:40 2022 -0700
@@ -294,6 +294,13 @@ static ngx_command_t ngx_http_js_comman
0,
NULL },
+ { ngx_string("js_preload_object"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13,
+ ngx_js_preload_object,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
{ ngx_string("js_path"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
@@ -1212,8 +1219,12 @@ ngx_http_js_init_vm(ngx_http_request_t *
{
njs_int_t rc;
ngx_str_t exception;
+ njs_str_t key;
+ ngx_uint_t i;
ngx_http_js_ctx_t *ctx;
+ njs_opaque_value_t retval;
ngx_pool_cleanup_t *cln;
+ ngx_js_named_path_t *preload;
ngx_http_js_loc_conf_t *jlcf;
jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
@@ -1253,6 +1264,27 @@ ngx_http_js_init_vm(ngx_http_request_t *
cln->handler = ngx_http_js_cleanup_ctx;
cln->data = ctx;
+ /* bind objects from preload vm */
+
+ if (jlcf->preload_objects != NGX_CONF_UNSET_PTR) {
+ preload = jlcf->preload_objects->elts;
+
+ for (i = 0; i < jlcf->preload_objects->nelts; i++) {
+ key.start = preload[i].name.data;
+ key.length = preload[i].name.len;
+
+ rc = njs_vm_value(jlcf->preload_vm, &key, njs_value_arg(&retval));
+ if (rc != NJS_OK) {
+ return NGX_ERROR;
+ }
+
+ rc = njs_vm_bind(ctx->vm, &key, njs_value_arg(&retval), 0);
+ if (rc != NJS_OK) {
+ return NGX_ERROR;
+ }
+ }
+ }
+
if (njs_vm_start(ctx->vm) == NJS_ERROR) {
ngx_js_retval(ctx->vm, NULL, &exception);
@@ -1288,9 +1320,13 @@ ngx_http_js_cleanup_ctx(void *data)
static void
ngx_http_js_cleanup_vm(void *data)
{
- njs_vm_t *vm = data;
-
- njs_vm_destroy(vm);
+ ngx_http_js_loc_conf_t *jlcf = data;
+
+ njs_vm_destroy(jlcf->vm);
+
+ if (jlcf->preload_objects != NGX_CONF_UNSET_PTR) {
+ njs_vm_destroy(jlcf->preload_vm);
+ }
}
@@ -4172,8 +4208,8 @@ ngx_http_js_merge_vm(ngx_conf_t *cf, ngx
{
ngx_str_t *path, *s;
ngx_uint_t i;
- ngx_array_t *imports, *paths;
- ngx_js_named_path_t *import, *pi;
+ ngx_array_t *imports, *preload_objects, *paths;
+ ngx_js_named_path_t *import, *pi, *pij, *preload;
if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) {
if (ngx_http_js_init_conf_vm(cf, prev) != NGX_OK) {
@@ -4182,16 +4218,58 @@ ngx_http_js_merge_vm(ngx_conf_t *cf, ngx
}
if (conf->imports == NGX_CONF_UNSET_PTR
- && conf->paths == NGX_CONF_UNSET_PTR)
+ && conf->paths == NGX_CONF_UNSET_PTR
+ && conf->preload_objects == NGX_CONF_UNSET_PTR)
{
if (prev->vm != NULL) {
+ conf->preload_objects = prev->preload_objects;
conf->imports = prev->imports;
conf->paths = prev->paths;
conf->vm = prev->vm;
+
+ conf->preload_vm = prev->preload_vm;
+
return NGX_OK;
}
}
+ if (prev->preload_objects != NGX_CONF_UNSET_PTR) {
+ if (conf->preload_objects == NGX_CONF_UNSET_PTR) {
+ conf->preload_objects = prev->preload_objects;
+
+ } else {
+ preload_objects = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_js_named_path_t));
+ if (preload_objects == NULL) {
+ return NGX_ERROR;
+ }
+
+ pij = prev->preload_objects->elts;
+
+ for (i = 0; i < prev->preload_objects->nelts; i++) {
+ preload = ngx_array_push(preload_objects);
+ if (preload == NULL) {
+ return NGX_ERROR;
+ }
+
+ *preload = pij[i];
+ }
+
+ pij = conf->preload_objects->elts;
+
+ for (i = 0; i < conf->preload_objects->nelts; i++) {
+ preload = ngx_array_push(preload_objects);
+ if (preload == NULL) {
+ return NGX_ERROR;
+ }
+
+ *preload = pij[i];
+ }
+
+ conf->preload_objects = preload_objects;
+ }
+ }
+
if (prev->imports != NGX_CONF_UNSET_PTR) {
if (conf->imports == NGX_CONF_UNSET_PTR) {
conf->imports = prev->imports;
@@ -4291,6 +4369,12 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf,
static const njs_str_t line_number_key = njs_str("lineNumber");
static const njs_str_t file_name_key = njs_str("fileName");
+ if (conf->preload_objects != NGX_CONF_UNSET_PTR) {
+ if (ngx_js_init_preload_vm(cf, (ngx_js_conf_t *)conf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
size = 0;
import = conf->imports->elts;
@@ -4352,7 +4436,7 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf,
}
cln->handler = ngx_http_js_cleanup_vm;
- cln->data = conf->vm;
+ cln->data = conf;
path.start = ngx_cycle->conf_prefix.data;
path.length = ngx_cycle->conf_prefix.len;
@@ -4619,6 +4703,7 @@ ngx_http_js_create_loc_conf(ngx_conf_t *
* set by ngx_pcalloc():
*
* conf->vm = NULL;
+ * conf->preload_vm = NULL;
* conf->content = { 0, NULL };
* conf->header_filter = { 0, NULL };
* conf->body_filter = { 0, NULL };
@@ -4630,6 +4715,7 @@ ngx_http_js_create_loc_conf(ngx_conf_t *
conf->paths = NGX_CONF_UNSET_PTR;
conf->imports = NGX_CONF_UNSET_PTR;
+ conf->preload_objects = NGX_CONF_UNSET_PTR;
conf->buffer_size = NGX_CONF_UNSET_SIZE;
conf->max_response_body_size = NGX_CONF_UNSET_SIZE;
diff -r 82f41f43abd4 -r 52b3e1f2a3e1 nginx/ngx_js.c
--- a/nginx/ngx_js.c Thu Sep 15 20:20:11 2022 -0700
+++ b/nginx/ngx_js.c Fri Sep 16 12:27:40 2022 -0700
@@ -493,3 +493,203 @@ ngx_js_import(ngx_conf_t *cf, ngx_comman
return NGX_CONF_OK;
}
+
+char *
+ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_js_conf_t *jscf = conf;
+
+ u_char *p, *end, c;
+ ngx_int_t from;
+ ngx_str_t *value, name, path;
+ ngx_js_named_path_t *preload;
+
+ value = cf->args->elts;
+ from = (cf->args->nelts == 4);
+
+ if (from) {
+ if (ngx_strcmp(value[2].data, "from") != 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ name = value[1];
+ path = (from ? value[3] : value[1]);
+
+ if (!from) {
+ end = name.data + name.len;
+
+ for (p = end - 1; p >= name.data; p--) {
+ if (*p == '/') {
+ break;
+ }
+ }
+
+ name.data = p + 1;
+ name.len = end - p - 1;
+
+ if (name.len < 5
+ || ngx_memcmp(&name.data[name.len - 5], ".json", 5) != 0)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "cannot extract export name from file path "
+ "\"%V\", use extended \"from\" syntax", &path);
+ return NGX_CONF_ERROR;
+ }
+
+ name.len -= 5;
+ }
+
+ if (name.len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty global name");
+ return NGX_CONF_ERROR;
+ }
+
+ p = name.data;
+ end = name.data + name.len;
+
+ while (p < end) {
+ c = ngx_tolower(*p);
+
+ if (*p != '_' && (c < 'a' || c > 'z')) {
+ if (p == name.data) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot start "
+ "with \"%c\" in global name \"%V\"", *p,
+ &name);
+ return NGX_CONF_ERROR;
+ }
+
+ if (*p < '0' || *p > '9' || *p == '.') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character "
+ "\"%c\" in global name \"%V\"", *p,
+ &name);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ p++;
+ }
+
+ if (ngx_strchr(path.data, '\'') != NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"'\" "
+ "in file path \"%V\"", &path);
+ return NGX_CONF_ERROR;
+ }
+
+ if (jscf->preload_objects == NGX_CONF_UNSET_PTR) {
+ jscf->preload_objects = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_js_named_path_t));
+ if (jscf->preload_objects == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ preload = ngx_array_push(jscf->preload_objects);
+ if (preload == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ preload->name = name;
+ preload->path = path;
+ preload->file = cf->conf_file->file.name.data;
+ preload->line = cf->conf_file->line;
+
+ return NGX_CONF_OK;
+}
+
+
+ngx_int_t
+ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_conf_t *conf)
+{
+ u_char *p, *start;
+ size_t size;
+ njs_vm_t *vm;
+ njs_int_t ret;
+ ngx_uint_t i;
+ njs_vm_opt_t options;
+ ngx_js_named_path_t *preload;
+
+ njs_vm_opt_init(&options);
+
+ options.init = 1;
+
+ vm = njs_vm_create(&options);
+ if (vm == NULL) {
+ goto error;
+ }
+
+ ret = ngx_js_core_init(vm, cf->log);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto error;
+ }
+
+ njs_str_t str = njs_str(
+ "import fs from 'fs';"
+
+ "let g = (function (np, no, nf, nsp, r) {"
+ "return function (n, p) {"
+ "p = (p[0] == '/') ? p : ngx.conf_prefix + p;"
+ "let o = r(p);"
+ "globalThis[n] = np("
+ "o,"
+ "function (k, v) {"
+ "if (v instanceof no) {"
+ "nf(nsp(v, null));"
+ "}"
+ "return v;"
+ "}"
+ ");"
+ "return;"
+ "}"
+ "})(JSON.parse,Object,Object.freeze,"
+ "Object.setPrototypeOf,fs.readFileSync);\n"
+ );
+
+ size = str.length;
+
+ preload = conf->preload_objects->elts;
+ for (i = 0; i < conf->preload_objects->nelts; i++) {
+ size += sizeof("g('','');\n") - 1 + preload[i].name.len
+ + preload[i].path.len;
+ }
+
+ start = ngx_pnalloc(cf->pool, size);
+ if (start == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(start, str.start, str.length);
+
+ preload = conf->preload_objects->elts;
+ for (i = 0; i < conf->preload_objects->nelts; i++) {
+ p = ngx_cpymem(p, "g('", sizeof("g('") - 1);
+ p = ngx_cpymem(p, preload[i].name.data, preload[i].name.len);
+ p = ngx_cpymem(p, "','", sizeof("','") - 1);
+ p = ngx_cpymem(p, preload[i].path.data, preload[i].path.len);
+ p = ngx_cpymem(p, "');\n", sizeof("');\n") - 1);
+ }
+
+ ret = njs_vm_compile(vm, &start, start + size);
+ if (ret != NJS_OK) {
+ goto error;
+ }
+
+ ret = njs_vm_start(vm);
+ if (ret != NJS_OK) {
+ goto error;
+ }
+
+ conf->preload_vm = vm;
+
+ return NGX_OK;
+
+error:
+
+ if (vm != NULL) {
+ njs_vm_destroy(vm);
+ }
+
+ return NGX_ERROR;
+}
diff -r 82f41f43abd4 -r 52b3e1f2a3e1 nginx/ngx_js.h
--- a/nginx/ngx_js.h Thu Sep 15 20:20:11 2022 -0700
+++ b/nginx/ngx_js.h Fri Sep 16 12:27:40 2022 -0700
@@ -49,7 +49,10 @@ typedef struct {
#define NGX_JS_COMMON_CONF \
njs_vm_t *vm; \
ngx_array_t *imports; \
- ngx_array_t *paths \
+ ngx_array_t *paths; \
+ \
+ njs_vm_t *preload_vm; \
+ ngx_array_t *preload_objects \
typedef struct {
NGX_JS_COMMON_CONF;
@@ -93,6 +96,8 @@ njs_int_t ngx_js_ext_log(njs_vm_t *vm, n
void ngx_js_logger(njs_vm_t *vm, njs_external_ptr_t external,
njs_log_level_t level, const u_char *start, size_t length);
char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+ngx_int_t ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_conf_t *conf);
njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
diff -r 82f41f43abd4 -r 52b3e1f2a3e1 nginx/ngx_stream_js_module.c
--- a/nginx/ngx_stream_js_module.c Thu Sep 15 20:20:11 2022 -0700
+++ b/nginx/ngx_stream_js_module.c Fri Sep 16 12:27:40 2022 -0700
@@ -169,6 +169,13 @@ static ngx_command_t ngx_stream_js_comm
0,
NULL },
+ { ngx_string("js_preload_object"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE13,
+ ngx_js_preload_object,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
{ ngx_string("js_path"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
@@ -858,8 +865,12 @@ static ngx_int_t
ngx_stream_js_init_vm(ngx_stream_session_t *s)
{
njs_int_t rc;
+ njs_str_t key;
ngx_str_t exception;
+ ngx_uint_t i;
+ njs_opaque_value_t retval;
ngx_pool_cleanup_t *cln;
+ ngx_js_named_path_t *preload;
ngx_stream_js_ctx_t *ctx;
ngx_stream_js_srv_conf_t *jscf;
@@ -898,6 +909,27 @@ ngx_stream_js_init_vm(ngx_stream_session
cln->handler = ngx_stream_js_cleanup;
cln->data = s;
+ /* bind objects from preload vm */
+
+ if (jscf->preload_objects != NGX_CONF_UNSET_PTR) {
+ preload = jscf->preload_objects->elts;
+
+ for (i = 0; i < jscf->preload_objects->nelts; i++) {
+ key.start = preload[i].name.data;
+ key.length = preload[i].name.len;
+
+ rc = njs_vm_value(jscf->preload_vm, &key, njs_value_arg(&retval));
+ if (rc != NJS_OK) {
+ return NGX_ERROR;
+ }
+
+ rc = njs_vm_bind(ctx->vm, &key, njs_value_arg(&retval), 0);
+ if (rc != NJS_OK) {
+ return NGX_ERROR;
+ }
+ }
+ }
+
if (njs_vm_start(ctx->vm) == NJS_ERROR) {
ngx_js_retval(ctx->vm, NULL, &exception);
@@ -953,9 +985,13 @@ ngx_stream_js_cleanup(void *data)
static void
ngx_stream_js_cleanup_vm(void *data)
{
- njs_vm_t *vm = data;
-
- njs_vm_destroy(vm);
+ ngx_stream_js_srv_conf_t *jscf = data;
+
+ njs_vm_destroy(jscf->vm);
+
+ if (jscf->preload_objects != NGX_CONF_UNSET_PTR) {
+ njs_vm_destroy(jscf->preload_vm);
+ }
}
@@ -1644,8 +1680,8 @@ ngx_stream_js_merge_vm(ngx_conf_t *cf, n
{
ngx_str_t *path, *s;
ngx_uint_t i;
- ngx_array_t *imports, *paths;
- ngx_js_named_path_t *import, *pi;
+ ngx_array_t *imports, *preload_objects, *paths;
+ ngx_js_named_path_t *import, *pi, *pij, *preload;
if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) {
if (ngx_stream_js_init_conf_vm(cf, prev) != NGX_OK) {
@@ -1654,16 +1690,58 @@ ngx_stream_js_merge_vm(ngx_conf_t *cf, n
}
if (conf->imports == NGX_CONF_UNSET_PTR
- && conf->paths == NGX_CONF_UNSET_PTR)
+ && conf->paths == NGX_CONF_UNSET_PTR
+ && conf->preload_objects == NGX_CONF_UNSET_PTR)
{
if (prev->vm != NULL) {
+ conf->preload_objects = prev->preload_objects;
conf->imports = prev->imports;
conf->paths = prev->paths;
conf->vm = prev->vm;
+
+ conf->preload_vm = prev->preload_vm;
+
return NGX_OK;
}
}
+ if (prev->preload_objects != NGX_CONF_UNSET_PTR) {
+ if (conf->preload_objects == NGX_CONF_UNSET_PTR) {
+ conf->preload_objects = prev->preload_objects;
+
+ } else {
+ preload_objects = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_js_named_path_t));
+ if (preload_objects == NULL) {
+ return NGX_ERROR;
+ }
+
+ pij = prev->preload_objects->elts;
+
+ for (i = 0; i < prev->preload_objects->nelts; i++) {
+ preload = ngx_array_push(preload_objects);
+ if (preload == NULL) {
+ return NGX_ERROR;
+ }
+
+ *preload = pij[i];
+ }
+
+ pij = conf->preload_objects->elts;
+
+ for (i = 0; i < conf->preload_objects->nelts; i++) {
+ preload = ngx_array_push(preload_objects);
+ if (preload == NULL) {
+ return NGX_ERROR;
+ }
+
+ *preload = pij[i];
+ }
+
+ conf->preload_objects = preload_objects;
+ }
+ }
+
if (prev->imports != NGX_CONF_UNSET_PTR) {
if (conf->imports == NGX_CONF_UNSET_PTR) {
conf->imports = prev->imports;
@@ -1763,6 +1841,12 @@ ngx_stream_js_init_conf_vm(ngx_conf_t *c
static const njs_str_t line_number_key = njs_str("lineNumber");
static const njs_str_t file_name_key = njs_str("fileName");
+ if (conf->preload_objects != NGX_CONF_UNSET_PTR) {
+ if (ngx_js_init_preload_vm(cf, (ngx_js_conf_t *)conf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
size = 0;
import = conf->imports->elts;
@@ -1823,7 +1907,7 @@ ngx_stream_js_init_conf_vm(ngx_conf_t *c
}
cln->handler = ngx_stream_js_cleanup_vm;
- cln->data = conf->vm;
+ cln->data = conf;
path.start = ngx_cycle->conf_prefix.data;
path.length = ngx_cycle->conf_prefix.len;
@@ -2026,6 +2110,7 @@ ngx_stream_js_create_srv_conf(ngx_conf_t
* set by ngx_pcalloc():
*
* conf->vm = NULL;
+ * conf->preload_vm = NULL;
* conf->access = { 0, NULL };
* conf->preread = { 0, NULL };
* conf->filter = { 0, NULL };
@@ -2040,6 +2125,7 @@ ngx_stream_js_create_srv_conf(ngx_conf_t
conf->buffer_size = NGX_CONF_UNSET_SIZE;
conf->max_response_body_size = NGX_CONF_UNSET_SIZE;
conf->timeout = NGX_CONF_UNSET_MSEC;
+ conf->preload_objects = NGX_CONF_UNSET_PTR;
#if (NGX_STREAM_SSL)
conf->ssl_verify = NGX_CONF_UNSET;
More information about the nginx-devel
mailing list