From ea90e10e58f2be4ccd93f8986f245923a69c3eec Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Tue, 6 Feb 2024 19:32:08 -0800 Subject: [PATCH] Reverted changes introduced in 7eaaa7d57636 (not released) back. Relative importing is again supported. --- external/njs_shell.c | 101 ++++++++++++++++++++++++++++-- nginx/ngx_js.c | 88 +++++++++++++++++++++++++- nginx/ngx_js.h | 1 + nginx/t/js_import_relative.t | 100 +++++++++++++++++++++++++++++ test/js/import_relative_path.t.js | 9 +++ test/js/module/libs/hash.js | 1 + test/js/module/sub/sub3.js | 1 + 7 files changed, 296 insertions(+), 5 deletions(-) create mode 100644 nginx/t/js_import_relative.t create mode 100644 test/js/import_relative_path.t.js create mode 100644 test/js/module/sub/sub3.js diff --git a/external/njs_shell.c b/external/njs_shell.c index 2a222238..9af73b73 100644 --- a/external/njs_shell.c +++ b/external/njs_shell.c @@ -115,6 +115,8 @@ typedef struct { njs_queue_t labels; + njs_str_t cwd; + njs_arr_t *rejected_promises; njs_bool_t suppress_stdout; @@ -693,6 +695,8 @@ njs_console_init(njs_vm_t *vm, njs_console_t *console) njs_queue_init(&console->posted_events); njs_queue_init(&console->labels); + njs_memzero(&console->cwd, sizeof(njs_str_t)); + console->rejected_promises = NULL; console->completion.completions = njs_vm_completions(vm, NULL); @@ -869,7 +873,7 @@ njs_module_path(const njs_str_t *dir, njs_module_info_t *info) if (dir != NULL) { length += dir->length; - if (length == 0) { + if (length == 0 || dir->length == 0) { return NJS_DECLINED; } @@ -915,7 +919,8 @@ njs_module_path(const njs_str_t *dir, njs_module_info_t *info) static njs_int_t -njs_module_lookup(njs_opts_t *opts, njs_module_info_t *info) +njs_module_lookup(njs_opts_t *opts, const njs_str_t *cwd, + njs_module_info_t *info) { njs_int_t ret; njs_str_t *path; @@ -925,6 +930,12 @@ njs_module_lookup(njs_opts_t *opts, njs_module_info_t *info) return njs_module_path(NULL, info); } + ret = njs_module_path(cwd, info); + + if (ret != NJS_DECLINED) { + return ret; + } + path = opts->paths; for (i = 0; i < opts->n_paths; i++) { @@ -980,23 +991,86 @@ fail: } +static void +njs_file_dirname(const njs_str_t *path, njs_str_t *name) +{ + const u_char *p, *end; + + if (path->length == 0) { + goto current_dir; + } + + p = path->start + path->length - 1; + + /* Stripping basename. */ + + while (p >= path->start && *p != '/') { p--; } + + end = p + 1; + + if (end == path->start) { + goto current_dir; + } + + /* Stripping trailing slashes. */ + + while (p >= path->start && *p == '/') { p--; } + + p++; + + if (p == path->start) { + p = end; + } + + name->start = path->start; + name->length = p - path->start; + + return; + +current_dir: + + *name = njs_str_value("."); +} + + +static njs_int_t +njs_console_set_cwd(njs_vm_t *vm, njs_console_t *console, njs_str_t *file) +{ + njs_str_t cwd; + + njs_file_dirname(file, &cwd); + + console->cwd.start = njs_mp_alloc(njs_vm_memory_pool(vm), cwd.length); + if (njs_slow_path(console->cwd.start == NULL)) { + return NJS_ERROR; + } + + memcpy(console->cwd.start, cwd.start, cwd.length); + console->cwd.length = cwd.length; + + return NJS_OK; +} + + static njs_mod_t * njs_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) { u_char *start; njs_int_t ret; - njs_str_t text; + njs_str_t text, prev_cwd; njs_mod_t *module; njs_opts_t *opts; + njs_console_t *console; njs_module_info_t info; opts = external; + console = njs_vm_external_ptr(vm); njs_memzero(&info, sizeof(njs_module_info_t)); info.name = *name; - ret = njs_module_lookup(opts, &info); + ret = njs_module_lookup(opts, &console->cwd, &info); if (njs_slow_path(ret != NJS_OK)) { return NULL; } @@ -1010,11 +1084,23 @@ njs_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) return NULL; } + prev_cwd = console->cwd; + + ret = njs_console_set_cwd(vm, console, &info.file); + if (njs_slow_path(ret != NJS_OK)) { + njs_vm_internal_error(vm, "while setting cwd for \"%V\" module", + &info.file); + return NULL; + } + start = text.start; module = njs_vm_compile_module(vm, &info.file, &start, &text.start[text.length]); + njs_mp_free(njs_vm_memory_pool(vm), console->cwd.start); + console->cwd = prev_cwd; + njs_mp_free(njs_vm_memory_pool(vm), text.start); return module; @@ -1025,6 +1111,7 @@ static njs_vm_t * njs_create_vm(njs_opts_t *opts) { njs_vm_t *vm; + njs_int_t ret; njs_vm_opt_t vm_options; njs_vm_opt_init(&vm_options); @@ -1068,6 +1155,12 @@ njs_create_vm(njs_opts_t *opts) njs_vm_external_ptr(vm)); } + ret = njs_console_set_cwd(vm, njs_vm_external_ptr(vm), &vm_options.file); + if (njs_slow_path(ret != NJS_OK)) { + njs_stderror("failed to set cwd\n"); + return NULL; + } + njs_vm_set_module_loader(vm, njs_module_loader, opts); return vm; diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index dd9d98b7..d1719951 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -1743,7 +1743,7 @@ ngx_js_module_path(const ngx_str_t *dir, njs_module_info_t *info) if (dir != NULL) { length += dir->len; - if (length == 0) { + if (length == 0 || dir->len == 0) { return NJS_DECLINED; } @@ -1799,6 +1799,12 @@ ngx_js_module_lookup(ngx_js_loc_conf_t *conf, njs_module_info_t *info) return ngx_js_module_path(NULL, info); } + ret = ngx_js_module_path(&conf->cwd, info); + + if (ret != NJS_DECLINED) { + return ret; + } + ret = ngx_js_module_path((const ngx_str_t *) &ngx_cycle->conf_prefix, info); if (ret != NJS_DECLINED) { @@ -1864,12 +1870,74 @@ fail: } +static void +ngx_js_file_dirname(const njs_str_t *path, ngx_str_t *name) +{ + const u_char *p, *end; + + if (path->length == 0) { + goto current_dir; + } + + p = path->start + path->length - 1; + + /* Stripping basename. */ + + while (p >= path->start && *p != '/') { p--; } + + end = p + 1; + + if (end == path->start) { + goto current_dir; + } + + /* Stripping trailing slashes. */ + + while (p >= path->start && *p == '/') { p--; } + + p++; + + if (p == path->start) { + p = end; + } + + name->data = path->start; + name->len = p - path->start; + + return; + +current_dir: + + ngx_str_set(name, "."); +} + + +static njs_int_t +ngx_js_set_cwd(njs_vm_t *vm, ngx_js_loc_conf_t *conf, njs_str_t *path) +{ + ngx_str_t cwd; + + ngx_js_file_dirname(path, &cwd); + + conf->cwd.data = njs_mp_alloc(njs_vm_memory_pool(vm), cwd.len); + if (conf->cwd.data == NULL) { + return NJS_ERROR; + } + + memcpy(conf->cwd.data, cwd.data, cwd.len); + conf->cwd.len = cwd.len; + + return NJS_OK; +} + + static njs_mod_t * ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) { u_char *start; njs_int_t ret; njs_str_t text; + ngx_str_t prev_cwd; njs_mod_t *module; ngx_js_loc_conf_t *conf; njs_module_info_t info; @@ -1894,11 +1962,23 @@ ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) return NULL; } + prev_cwd = conf->cwd; + + ret = ngx_js_set_cwd(vm, conf, &info.file); + if (ret != NJS_OK) { + njs_vm_internal_error(vm, "while setting cwd for \"%V\" module", + &info.file); + return NULL; + } + start = text.start; module = njs_vm_compile_module(vm, &info.file, &start, &text.start[text.length]); + njs_mp_free(njs_vm_memory_pool(vm), conf->cwd.data); + conf->cwd = prev_cwd; + njs_mp_free(njs_vm_memory_pool(vm), text.start); return module; @@ -1985,6 +2065,12 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, njs_vm_set_rejection_tracker(conf->vm, ngx_js_rejection_tracker, NULL); + rc = ngx_js_set_cwd(conf->vm, conf, &options->file); + if (rc != NJS_OK) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to set cwd"); + return NGX_ERROR; + } + njs_vm_set_module_loader(conf->vm, ngx_js_module_loader, conf); if (conf->paths != NGX_CONF_UNSET_PTR) { diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h index 1f95c7c3..a84058f3 100644 --- a/nginx/ngx_js.h +++ b/nginx/ngx_js.h @@ -83,6 +83,7 @@ struct ngx_js_event_s { #define _NGX_JS_COMMON_LOC_CONF \ njs_vm_t *vm; \ + ngx_str_t cwd; \ ngx_array_t *imports; \ ngx_array_t *paths; \ \ diff --git a/nginx/t/js_import_relative.t b/nginx/t/js_import_relative.t new file mode 100644 index 00000000..9f5f2fef --- /dev/null +++ b/nginx/t/js_import_relative.t @@ -0,0 +1,100 @@ +#!/usr/bin/perl + +# (C) Dmitry Volyntsev +# (c) Nginx, Inc. + +# Tests for http njs module, js_import directive, importing relative paths. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http/) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + js_import main from lib/main.js; + + server { + listen 127.0.0.1:8080; + server_name localhost; + + location /local { + js_content main.test_local; + } + + location /top { + js_content main.test_top; + } + } +} + +EOF + +my $d = $t->testdir(); + +mkdir("$d/lib"); +mkdir("$d/lib/sub"); + +$t->write_file('lib/main.js', <write_file('lib/sub/foo.js', <write_file('lib/foo.js', <write_file('foo.js', <try_run('no njs available')->plan(2); + +############################################################################### + +like(http_get('/local'), qr/LOCAL/s, 'local relative import'); +like(http_get('/top'), qr/TOP/s, 'local relative import 2'); + +############################################################################### diff --git a/test/js/import_relative_path.t.js b/test/js/import_relative_path.t.js new file mode 100644 index 00000000..2a297f2e --- /dev/null +++ b/test/js/import_relative_path.t.js @@ -0,0 +1,9 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module/] +---*/ + +import hash from 'libs/hash.js'; + +assert.sameValue(hash.name, "libs.name"); diff --git a/test/js/module/libs/hash.js b/test/js/module/libs/hash.js index 85f91ef8..2eb3728c 100644 --- a/test/js/module/libs/hash.js +++ b/test/js/module/libs/hash.js @@ -4,6 +4,7 @@ function hash() { return v; } +import sub from 'sub/sub3.js'; import name from 'name.js'; import crypto from 'crypto'; diff --git a/test/js/module/sub/sub3.js b/test/js/module/sub/sub3.js new file mode 100644 index 00000000..54ff7180 --- /dev/null +++ b/test/js/module/sub/sub3.js @@ -0,0 +1 @@ +export default { name: "SUB3" }; -- 2.47.3