From 9c901630b3518065684c37056e96e1f9b68f88bb Mon Sep 17 00:00:00 2001 From: Vadim Zhestikov Date: Mon, 27 Oct 2025 14:14:02 -0700 Subject: [PATCH] Fetch: making sure catch handler is executed asynchronously. --- nginx/ngx_js_fetch.c | 10 ++++----- nginx/ngx_qjs_fetch.c | 9 +++++--- nginx/t/js_fetch.t | 48 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c index 09d0e194..fd40a5ba 100644 --- a/nginx/ngx_js_fetch.c +++ b/nginx/ngx_js_fetch.c @@ -668,13 +668,13 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, fail: - njs_vm_exception_get(vm, njs_value_arg(&lvalue)); - - ngx_js_fetch_done(fetch, &lvalue, NJS_ERROR); + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0, + "js http done fetch:%p rc:%d", fetch, NJS_ERROR); - njs_value_assign(retval, njs_value_arg(&fetch->promise)); + ngx_js_del_event(ngx_external_ctx(vm, njs_vm_external_ptr(vm)), + fetch->event); - return NJS_OK; + return ngx_js_fetch_promissified_result(vm, NULL, NJS_ERROR, retval); } diff --git a/nginx/ngx_qjs_fetch.c b/nginx/ngx_qjs_fetch.c index 0b3f3269..731fba44 100644 --- a/nginx/ngx_qjs_fetch.c +++ b/nginx/ngx_qjs_fetch.c @@ -401,11 +401,14 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc, fail: - fetch->response_value = JS_GetException(cx); + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, (&fetch->http)->log, 0, + "js http done fetch:%p rc:%d", fetch, NGX_ERROR); - ngx_qjs_fetch_done(fetch, fetch->response_value, NGX_ERROR); + ngx_js_del_event(ngx_qjs_external_ctx(cx, external), fetch->event); - return promise; + JS_FreeValue(cx, promise); + + return qjs_promise_result(cx, JS_EXCEPTION); } diff --git a/nginx/t/js_fetch.t b/nginx/t/js_fetch.t index 9bbacf5b..f3601b82 100644 --- a/nginx/t/js_fetch.t +++ b/nginx/t/js_fetch.t @@ -60,6 +60,10 @@ http { js_content test.broken_response; } + location /broken_catch { + js_content test.broken_catch; + } + location /body { js_content test.body; } @@ -234,6 +238,38 @@ $t->write_file('test.js', < { + ngx.fetch.apply(r, args) + .then(reply => { + r.return(400, '["unexpected then"]'); + }) + .catch(e => { + results.push(sync_catch); + + if (results.length == tests.length) { + r.return(200, JSON.stringify(results)); + } + }) + }) + + sync_catch = 'async'; + } + + function broken_catch(r) { + var tests = [ + ['http://127.0.0.1:1/loc'], + ['http://127.0.0.1:80800/loc'], + [Symbol.toStringTag], + ]; + + return process_errors_catch(r, tests); + } + function chain(r) { var results = []; var reqs = [ @@ -428,15 +464,15 @@ $t->write_file('test.js', <try_run('no njs.fetch'); -$t->plan(40); +$t->plan(41); $t->run_daemon(\&http_daemon, port(8082)); $t->waitforsocket('127.0.0.1:' . port(8082)); @@ -495,6 +531,8 @@ is(get_json('/multi'), like(http_get('/multi?throw=1'), qr/500/s, 'fetch destructor'); like(http_get('/broken'), qr/200/s, 'fetch broken'); like(http_get('/broken_response'), qr/200/s, 'fetch broken response'); +like(http_get('/broken_catch'), qr/\["async","async","async"]$/s, + 'fetch broken catch'); like(http_get('/chunked_ok'), qr/200/s, 'fetch chunked ok'); like(http_get('/chunked_fail'), qr/200/s, 'fetch chunked fail'); like(http_get('/chain'), qr/200 OK.*SUCCESS$/s, 'fetch chain'); -- 2.47.3