Skip to content

Instantly share code, notes, and snippets.

@xeioex
Created October 29, 2025 04:57
Show Gist options
  • Save xeioex/8aba0e4599b4e3484a7be544be4509a4 to your computer and use it in GitHub Desktop.
Save xeioex/8aba0e4599b4e3484a7be544be4509a4 to your computer and use it in GitHub Desktop.
commit 33a12604f3081cb68b77a91b2b04ce6023b2e1d1
Author: Vadim Zhestikov <[email protected]>
Date: Mon Oct 27 14:14:02 2025 -0700
Fetch: making sure catch handler is executed asynchronously.
diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c
index 09d0e194..91698d11 100644
--- a/nginx/ngx_js_fetch.c
+++ b/nginx/ngx_js_fetch.c
@@ -668,11 +668,20 @@ 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_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "js http done fetch:%p rc:%i", fetch, NJS_ERROR);
- ngx_js_fetch_done(fetch, &lvalue, NJS_ERROR);
+ ngx_js_http_close_peer(&fetch->http);
- njs_value_assign(retval, njs_value_arg(&fetch->promise));
+ if (fetch->event != NULL) {
+ ret = ngx_js_fetch_promissified_result(vm, njs_value_arg(&lvalue),
+ NJS_ERROR, retval);
+
+ ngx_js_del_event(ngx_external_ctx(vm, njs_vm_external_ptr(vm)),
+ fetch->event);
+
+ ngx_external_event_finalize(vm)(njs_vm_external_ptr(vm), ret);
+ }
return NJS_OK;
}
diff --git a/nginx/ngx_qjs_fetch.c b/nginx/ngx_qjs_fetch.c
index 0b3f3269..2c2ef18c 100644
--- a/nginx/ngx_qjs_fetch.c
+++ b/nginx/ngx_qjs_fetch.c
@@ -401,9 +401,20 @@ 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:%i", fetch, NGX_ERROR);
- ngx_qjs_fetch_done(fetch, fetch->response_value, NGX_ERROR);
+ ngx_js_http_close_peer(&fetch->http);
+
+ if (fetch->event != NULL) {
+ JS_FreeValue(cx, promise);
+ promise = qjs_promise_result(cx, JS_EXCEPTION);
+
+ ctx = ngx_qjs_external_ctx(cx, external);
+ ngx_js_del_event(ctx, fetch->event);
+
+ ngx_qjs_external_event_finalize(cx)(external, rc);
+ }
return promise;
}
diff --git a/nginx/t/js_fetch.t b/nginx/t/js_fetch.t
index 9bbacf5b..bc9ee4c5 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', <<EOF);
return process_errors(r, tests);
}
+ function process_errors_catch(r, tests) {
+ var results = [];
+
+ var sync_catch = 'sync';
+
+ tests.forEach(args => {
+ 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', <<EOF);
r.return(c, `\${v.request_method}:\${bar}:\${body}`);
}
- export default {njs: test_njs, body, broken, broken_response, body_special,
- chain, chunked_ok, chunked_fail, header, header_iter,
- host_header, multi, loc, property, body_content_length,
- user_agent_header };
+ export default {njs: test_njs, body, broken, broken_response, broken_catch,
+ body_special,chain, chunked_ok, chunked_fail, header,
+ header_iter, host_header, multi, loc, property,
+ body_content_length, user_agent_header };
EOF
$t->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');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment