[njs] Modules: improved reporting of unhandled promise rejections.
noreply at nginx.com
noreply at nginx.com
Tue Feb 25 00:50:02 UTC 2025
details: https://github.com/nginx/njs/commit/24633b3766df99de3c138ca6dc37b0cb403e619d
branches: master
commit: 24633b3766df99de3c138ca6dc37b0cb403e619d
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Fri, 21 Feb 2025 20:40:14 -0800
description:
Modules: improved reporting of unhandled promise rejections.
Previously, some promise rejections were not reported.
For example:
async function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('timeout'));
}, ms);
});
}
async function handler(r) {
let v = await timeout(1000);
r.return(200);
}
---
nginx/ngx_js.c | 72 +++++++++++++++++-------------------------------------
nginx/t/js_async.t | 24 ++++++++++++++++--
2 files changed, 45 insertions(+), 51 deletions(-)
diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c
index 7f7b7362..383d2304 100644
--- a/nginx/ngx_js.c
+++ b/nginx/ngx_js.c
@@ -731,13 +731,6 @@ ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname,
}
}
- if (ngx_js_unhandled_rejection(ctx)) {
- ngx_js_exception(vm, &exception);
-
- ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception);
- return NGX_ERROR;
- }
-
return njs_rbtree_is_empty(&ctx->waiting_events) ? NGX_OK : NGX_AGAIN;
}
@@ -775,6 +768,7 @@ static void
ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
ngx_js_loc_conf_t *conf)
{
+ ngx_str_t exception;
ngx_js_event_t *event;
njs_rbtree_node_t *node;
@@ -791,6 +785,12 @@ ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
node = njs_rbtree_node_successor(&ctx->waiting_events, node);
}
+
+ if (ngx_js_unhandled_rejection(ctx)) {
+ ngx_js_exception(e->u.njs.vm, &exception);
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "js unhandled rejection: %V", &exception);
+ }
}
njs_vm_destroy(e->u.njs.vm);
@@ -1071,13 +1071,6 @@ ngx_engine_qjs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname,
}
}
- if (ngx_qjs_unhandled_rejection(ctx)) {
- ngx_qjs_exception(ctx->engine, &exception);
-
- ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception);
- return NGX_ERROR;
- }
-
return njs_rbtree_is_empty(&ctx->waiting_events) ? NGX_OK : NGX_AGAIN;
}
@@ -1129,16 +1122,16 @@ void
ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
ngx_js_loc_conf_t *conf)
{
- uint32_t i, length;
- JSRuntime *rt;
- JSContext *cx;
- JSClassID class_id;
- ngx_qjs_event_t *event;
- ngx_js_opaque_t *opaque;
- njs_rbtree_node_t *node;
- ngx_pool_cleanup_t *cln;
- ngx_js_code_entry_t *pc;
- ngx_js_rejected_promise_t *rejected_promise;
+ uint32_t i, length;
+ ngx_str_t exception;
+ JSRuntime *rt;
+ JSContext *cx;
+ JSClassID class_id;
+ ngx_qjs_event_t *event;
+ ngx_js_opaque_t *opaque;
+ njs_rbtree_node_t *node;
+ ngx_pool_cleanup_t *cln;
+ ngx_js_code_entry_t *pc;
cx = e->u.qjs.ctx;
@@ -1156,13 +1149,10 @@ ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
node = njs_rbtree_node_successor(&ctx->waiting_events, node);
}
- if (ctx->rejected_promises != NULL) {
- rejected_promise = ctx->rejected_promises->start;
-
- for (i = 0; i < ctx->rejected_promises->items; i++) {
- JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].promise));
- JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].message));
- }
+ if (ngx_qjs_unhandled_rejection(ctx)) {
+ ngx_qjs_exception(ctx->engine, &exception);
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "js unhandled rejection: %V", &exception);
}
class_id = JS_GetClassID(ngx_qjs_arg(ctx->args[0]));
@@ -2037,10 +2027,8 @@ ngx_qjs_module_loader(JSContext *cx, const char *module_name, void *opaque)
static int
ngx_qjs_unhandled_rejection(ngx_js_ctx_t *ctx)
{
- size_t len;
uint32_t i;
JSContext *cx;
- const char *str;
ngx_js_rejected_promise_t *rejected_promise;
if (ctx->rejected_promises == NULL
@@ -2052,13 +2040,7 @@ ngx_qjs_unhandled_rejection(ngx_js_ctx_t *ctx)
cx = ctx->engine->u.qjs.ctx;
rejected_promise = ctx->rejected_promises->start;
- str = JS_ToCStringLen(cx, &len, ngx_qjs_arg(rejected_promise->message));
- if (njs_slow_path(str == NULL)) {
- return -1;
- }
-
- JS_ThrowTypeError(cx, "unhandled promise rejection: %.*s", (int) len, str);
- JS_FreeCString(cx, str);
+ JS_Throw(cx, JS_DupValue(cx, ngx_qjs_arg(rejected_promise->message)));
for (i = 0; i < ctx->rejected_promises->items; i++) {
JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].promise));
@@ -3890,8 +3872,6 @@ static njs_int_t
ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx)
{
njs_vm_t *vm;
- njs_int_t ret;
- njs_str_t message;
ngx_js_rejected_promise_t *rejected_promise;
if (ctx->rejected_promises == NULL
@@ -3903,13 +3883,7 @@ ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx)
vm = ctx->engine->u.njs.vm;
rejected_promise = ctx->rejected_promises->start;
- ret = njs_vm_value_to_string(vm, &message,
- njs_value_arg(&rejected_promise->message));
- if (njs_slow_path(ret != NJS_OK)) {
- return -1;
- }
-
- njs_vm_error(vm, "unhandled promise rejection: %V", &message);
+ njs_vm_throw(vm, njs_value_arg(&rejected_promise->message));
njs_arr_destroy(ctx->rejected_promises);
ctx->rejected_promises = NULL;
diff --git a/nginx/t/js_async.t b/nginx/t/js_async.t
index 73e71851..32d1e0a4 100644
--- a/nginx/t/js_async.t
+++ b/nginx/t/js_async.t
@@ -84,6 +84,10 @@ http {
location /set_rv_var {
return 200 $test_set_rv_var;
}
+
+ location /await_reject {
+ js_content test.await_reject;
+ }
}
}
@@ -194,13 +198,26 @@ $t->write_file('test.js', <<EOF);
r.setReturnValue(`retval: \${a1 + a2}`);
}
+ async function timeout(ms) {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ reject(new Error('timeout'));
+ }, ms);
+ });
+ }
+
+ async function await_reject(r) {
+ let v = await timeout(1);
+ r.return(200);
+ }
+
export default {njs:test_njs, set_timeout, set_timeout_data,
set_timeout_many, context_var, shared_ctx, limit_rate,
- async_content, set_rv_var};
+ async_content, set_rv_var, await_reject};
EOF
-$t->try_run('no njs available')->plan(9);
+$t->try_run('no njs available')->plan(10);
###############################################################################
@@ -214,6 +231,7 @@ like(http_get('/async_content'), qr/retval: AB/, 'async content');
like(http_get('/set_rv_var'), qr/retval: 30/, 'set return value variable');
http_get('/async_var');
+http_get('/await_reject');
$t->stop();
@@ -221,5 +239,7 @@ ok(index($t->read_file('error.log'), 'pending events') > 0,
'pending js events');
ok(index($t->read_file('error.log'), 'async operation inside') > 0,
'async op in var handler');
+ok(index($t->read_file('error.log'), 'js unhandled rejection') > 0,
+ 'unhandled rejection');
###############################################################################
More information about the nginx-devel
mailing list