From ed36e94242de38e88a6cc536404609fc72bf6456 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Fri, 18 Oct 2024 18:24:49 -0700 Subject: [PATCH] Improved error messages for module loading failures. There are several reasons why a file cannot be opened. Without extra information, especially in containerized environments, these problems are difficult to debug. Adding errno status to the error output helps identify the root cause. Additionally, error messages are now aligned between njs and QuickJS. --- nginx/ngx_js.c | 350 ++++++++++++++++++++++++++++++++++++++- nginx/ngx_js.h | 1 + src/njs.h | 2 + src/njs_module.c | 2 +- src/njs_parser.c | 9 +- src/test/njs_unit_test.c | 4 +- test/shell_test_njs.exp | 4 +- 7 files changed, 362 insertions(+), 10 deletions(-) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 152045f03..f70288cf1 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -1979,10 +1979,15 @@ ngx_qjs_module_loader(JSContext *cx, const char *module_name, void *opaque) info.name.start = (u_char *) module_name; info.name.length = njs_strlen(module_name); + errno = 0; ret = ngx_js_module_lookup(conf, &info); if (ret != NJS_OK) { - JS_ThrowReferenceError(cx, "could not load module filename '%s'", - module_name); + if (errno != 0) { + JS_ThrowReferenceError(cx, "Cannot load module \"%s\" " + "(%s:%s)", module_name, + ngx_js_errno_string(errno), strerror(errno)); + } + return NULL; } @@ -3764,8 +3769,14 @@ ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) info.name = *name; + errno = 0; ret = ngx_js_module_lookup(conf, &info); if (njs_slow_path(ret != NJS_OK)) { + if (errno != 0) { + njs_vm_ref_error(vm, "Cannot load module \"%V\" (%s:%s)", name, + ngx_js_errno_string(errno), strerror(errno)); + } + return NULL; } @@ -4076,6 +4087,341 @@ ngx_js_monotonic_time(void) } +#define ngx_js_errno_case(e) \ + case e: \ + return #e; + + +const char* +ngx_js_errno_string(int errnum) +{ + switch (errnum) { +#ifdef EACCES + ngx_js_errno_case(EACCES); +#endif + +#ifdef EADDRINUSE + ngx_js_errno_case(EADDRINUSE); +#endif + +#ifdef EADDRNOTAVAIL + ngx_js_errno_case(EADDRNOTAVAIL); +#endif + +#ifdef EAFNOSUPPORT + ngx_js_errno_case(EAFNOSUPPORT); +#endif + +#ifdef EAGAIN + ngx_js_errno_case(EAGAIN); +#endif + +#ifdef EWOULDBLOCK +#if EAGAIN != EWOULDBLOCK + ngx_js_errno_case(EWOULDBLOCK); +#endif +#endif + +#ifdef EALREADY + ngx_js_errno_case(EALREADY); +#endif + +#ifdef EBADF + ngx_js_errno_case(EBADF); +#endif + +#ifdef EBADMSG + ngx_js_errno_case(EBADMSG); +#endif + +#ifdef EBUSY + ngx_js_errno_case(EBUSY); +#endif + +#ifdef ECANCELED + ngx_js_errno_case(ECANCELED); +#endif + +#ifdef ECHILD + ngx_js_errno_case(ECHILD); +#endif + +#ifdef ECONNABORTED + ngx_js_errno_case(ECONNABORTED); +#endif + +#ifdef ECONNREFUSED + ngx_js_errno_case(ECONNREFUSED); +#endif + +#ifdef ECONNRESET + ngx_js_errno_case(ECONNRESET); +#endif + +#ifdef EDEADLK + ngx_js_errno_case(EDEADLK); +#endif + +#ifdef EDESTADDRREQ + ngx_js_errno_case(EDESTADDRREQ); +#endif + +#ifdef EDOM + ngx_js_errno_case(EDOM); +#endif + +#ifdef EDQUOT + ngx_js_errno_case(EDQUOT); +#endif + +#ifdef EEXIST + ngx_js_errno_case(EEXIST); +#endif + +#ifdef EFAULT + ngx_js_errno_case(EFAULT); +#endif + +#ifdef EFBIG + ngx_js_errno_case(EFBIG); +#endif + +#ifdef EHOSTUNREACH + ngx_js_errno_case(EHOSTUNREACH); +#endif + +#ifdef EIDRM + ngx_js_errno_case(EIDRM); +#endif + +#ifdef EILSEQ + ngx_js_errno_case(EILSEQ); +#endif + +#ifdef EINPROGRESS + ngx_js_errno_case(EINPROGRESS); +#endif + +#ifdef EINTR + ngx_js_errno_case(EINTR); +#endif + +#ifdef EINVAL + ngx_js_errno_case(EINVAL); +#endif + +#ifdef EIO + ngx_js_errno_case(EIO); +#endif + +#ifdef EISCONN + ngx_js_errno_case(EISCONN); +#endif + +#ifdef EISDIR + ngx_js_errno_case(EISDIR); +#endif + +#ifdef ELOOP + ngx_js_errno_case(ELOOP); +#endif + +#ifdef EMFILE + ngx_js_errno_case(EMFILE); +#endif + +#ifdef EMLINK + ngx_js_errno_case(EMLINK); +#endif + +#ifdef EMSGSIZE + ngx_js_errno_case(EMSGSIZE); +#endif + +#ifdef EMULTIHOP + ngx_js_errno_case(EMULTIHOP); +#endif + +#ifdef ENAMETOOLONG + ngx_js_errno_case(ENAMETOOLONG); +#endif + +#ifdef ENETDOWN + ngx_js_errno_case(ENETDOWN); +#endif + +#ifdef ENETRESET + ngx_js_errno_case(ENETRESET); +#endif + +#ifdef ENETUNREACH + ngx_js_errno_case(ENETUNREACH); +#endif + +#ifdef ENFILE + ngx_js_errno_case(ENFILE); +#endif + +#ifdef ENOBUFS + ngx_js_errno_case(ENOBUFS); +#endif + +#ifdef ENODATA + ngx_js_errno_case(ENODATA); +#endif + +#ifdef ENODEV + ngx_js_errno_case(ENODEV); +#endif + +#ifdef ENOENT + ngx_js_errno_case(ENOENT); +#endif + +#ifdef ENOEXEC + ngx_js_errno_case(ENOEXEC); +#endif + +#ifdef ENOLINK + ngx_js_errno_case(ENOLINK); +#endif + +#ifdef ENOLCK +#if ENOLINK != ENOLCK + ngx_js_errno_case(ENOLCK); +#endif +#endif + +#ifdef ENOMEM + ngx_js_errno_case(ENOMEM); +#endif + +#ifdef ENOMSG + ngx_js_errno_case(ENOMSG); +#endif + +#ifdef ENOPROTOOPT + ngx_js_errno_case(ENOPROTOOPT); +#endif + +#ifdef ENOSPC + ngx_js_errno_case(ENOSPC); +#endif + +#ifdef ENOSR + ngx_js_errno_case(ENOSR); +#endif + +#ifdef ENOSTR + ngx_js_errno_case(ENOSTR); +#endif + +#ifdef ENOSYS + ngx_js_errno_case(ENOSYS); +#endif + +#ifdef ENOTCONN + ngx_js_errno_case(ENOTCONN); +#endif + +#ifdef ENOTDIR + ngx_js_errno_case(ENOTDIR); +#endif + +#ifdef ENOTEMPTY +#if ENOTEMPTY != EEXIST + ngx_js_errno_case(ENOTEMPTY); +#endif +#endif + +#ifdef ENOTSOCK + ngx_js_errno_case(ENOTSOCK); +#endif + +#ifdef ENOTSUP + ngx_js_errno_case(ENOTSUP); +#else +#ifdef EOPNOTSUPP + ngx_js_errno_case(EOPNOTSUPP); +#endif +#endif + +#ifdef ENOTTY + ngx_js_errno_case(ENOTTY); +#endif + +#ifdef ENXIO + ngx_js_errno_case(ENXIO); +#endif + +#ifdef EOVERFLOW + ngx_js_errno_case(EOVERFLOW); +#endif + +#ifdef EPERM + ngx_js_errno_case(EPERM); +#endif + +#ifdef EPIPE + ngx_js_errno_case(EPIPE); +#endif + +#ifdef EPROTO + ngx_js_errno_case(EPROTO); +#endif + +#ifdef EPROTONOSUPPORT + ngx_js_errno_case(EPROTONOSUPPORT); +#endif + +#ifdef EPROTOTYPE + ngx_js_errno_case(EPROTOTYPE); +#endif + +#ifdef ERANGE + ngx_js_errno_case(ERANGE); +#endif + +#ifdef EROFS + ngx_js_errno_case(EROFS); +#endif + +#ifdef ESPIPE + ngx_js_errno_case(ESPIPE); +#endif + +#ifdef ESRCH + ngx_js_errno_case(ESRCH); +#endif + +#ifdef ESTALE + ngx_js_errno_case(ESTALE); +#endif + +#ifdef ETIME + ngx_js_errno_case(ETIME); +#endif + +#ifdef ETIMEDOUT + ngx_js_errno_case(ETIMEDOUT); +#endif + +#ifdef ETXTBSY + ngx_js_errno_case(ETXTBSY); +#endif + +#ifdef EXDEV + ngx_js_errno_case(EXDEV); +#endif + + default: + break; + } + + return "UNKNOWN CODE"; +} + + ngx_js_queue_t * ngx_js_queue_create(ngx_pool_t *pool, ngx_uint_t capacity) { diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h index 8b6fbc858..8319dc856 100644 --- a/nginx/ngx_js.h +++ b/nginx/ngx_js.h @@ -412,6 +412,7 @@ njs_int_t ngx_js_ext_flags(njs_vm_t *vm, njs_object_prop_t *prop, ngx_int_t ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str); ngx_int_t ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n); +const char *ngx_js_errno_string(int errnum); ngx_js_queue_t *ngx_js_queue_create(ngx_pool_t *pool, ngx_uint_t capacity); ngx_int_t ngx_js_queue_push(ngx_js_queue_t *queue, void *item); diff --git a/src/njs.h b/src/njs.h index 466932d82..14a245af6 100644 --- a/src/njs.h +++ b/src/njs.h @@ -72,6 +72,8 @@ extern const njs_value_t njs_value_undefined; njs_vm_error2(vm, 2, fmt, ##__VA_ARGS__) #define njs_vm_range_error(vm, fmt, ...) \ njs_vm_error2(vm, 3, fmt, ##__VA_ARGS__) +#define njs_vm_ref_error(vm, fmt, ...) \ + njs_vm_error2(vm, 4, fmt, ##__VA_ARGS__) #define njs_vm_syntax_error(vm, fmt, ...) \ njs_vm_error2(vm, 5, fmt, ##__VA_ARGS__) #define njs_vm_type_error(vm, fmt, ...) \ diff --git a/src/njs_module.c b/src/njs_module.c index 859d96a82..5d9c96ae6 100644 --- a/src/njs_module.c +++ b/src/njs_module.c @@ -148,7 +148,7 @@ njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, module = njs_module_find(vm, &name, 0); if (njs_slow_path(module == NULL)) { - njs_error(vm, "Cannot find module \"%V\"", &name); + njs_error(vm, "Cannot load module \"%V\"", &name); return NJS_ERROR; } diff --git a/src/njs_parser.c b/src/njs_parser.c index 7eb6292e4..1f16336fd 100644 --- a/src/njs_parser.c +++ b/src/njs_parser.c @@ -8114,7 +8114,7 @@ njs_parser_module(njs_parser_t *parser, njs_str_t *name) vm = parser->vm; if (name->length == 0) { - njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name); + njs_parser_ref_error(parser, "Cannot load module \"%V\"", name); return NULL; } @@ -8124,13 +8124,16 @@ njs_parser_module(njs_parser_t *parser, njs_str_t *name) } if (vm->module_loader == NULL) { - njs_parser_syntax_error(parser, "Cannot load module \"%V\"", name); + njs_parser_ref_error(parser, "Module loader callback is not provided"); return NULL; } module = vm->module_loader(vm, vm->module_loader_opaque, name); if (module == NULL) { - njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name); + if (!njs_is_valid(&vm->exception)) { + njs_parser_ref_error(parser, "Cannot load module \"%V\"", name); + } + return NULL; } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index c52753a7c..c4dc6ddef 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -18975,7 +18975,7 @@ static njs_unit_test_t njs_test[] = /* require(). */ { njs_str("require('unknown_module')"), - njs_str("Error: Cannot find module \"unknown_module\"") }, + njs_str("Error: Cannot load module \"unknown_module\"") }, { njs_str("require()"), njs_str("TypeError: missing path") }, @@ -19045,7 +19045,7 @@ static njs_unit_test_t njs_test[] = njs_str("SyntaxError: Unexpected token \"{\" in 1") }, { njs_str("import x from ''"), - njs_str("SyntaxError: Cannot find module \"\" in 1") }, + njs_str("ReferenceError: Cannot load module \"\" in 1") }, { njs_str("export"), njs_str("SyntaxError: Illegal export statement in 1") }, diff --git a/test/shell_test_njs.exp b/test/shell_test_njs.exp index 52ce48861..fac0fe3a9 100644 --- a/test/shell_test_njs.exp +++ b/test/shell_test_njs.exp @@ -173,13 +173,13 @@ njs_test { # quiet mode njs_run {"-q" "test/js/import_chain.t.js"} \ - "SyntaxError: Cannot find module \"lib2.js\" in 7" + "ReferenceError: Cannot load module \"lib2.js\" in 7" # sandboxing njs_test { {"var fs = require('fs')\r\n" - "Error: Cannot find module \"fs\"\r\n"} + "Error: Cannot load module \"fs\"\r\n"} } "-s" njs_test {