[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