--- /dev/null
+#!/usr/bin/perl
+
+# (C) Dmitry Volyntsev
+# (C) F5, Inc.
+
+# Tests for http njs module, fetch method error stack traces.
+
+###############################################################################
+
+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 test.js;
+
+ server {
+ listen 127.0.0.1:8080;
+ server_name localhost;
+
+ location /testAsync {
+ js_content test.testAsync;
+ }
+
+ location /testSync {
+ js_content test.testSync;
+ }
+
+ location /testAsyncThrow {
+ js_content test.testAsyncThrow;
+ }
+ }
+}
+
+EOF
+
+$t->write_file('test.js', <<'EOF');
+ async function testAsync(r) {
+ try {
+ await ngx.fetch('http://127.0.0.1:12345');
+
+ } catch(e) {
+ r.return(200, `message: ${e.message}\n stack: ${e.stack}\n`);
+ }
+ }
+
+ function testSync(r) {
+ try {
+ throw Error('oops');
+
+ } catch(e) {
+ r.return(200, `message: ${e.message}\n stack: ${e.stack}\n`);
+ }
+ }
+
+ async function testAsyncThrow(r) {
+ try {
+ throw Error('oops');
+
+ } catch(e) {
+ r.return(200, `message: ${e.message}\n stack: ${e.stack}\n`);
+ }
+ }
+
+ export default { testAsync, testSync, testAsyncThrow }
+EOF
+
+$t->try_run('no njs');
+
+$t->plan(6);
+
+###############################################################################
+
+like(http_get('/testSync'), qr/message: oops/s, 'sync stack exists');
+like(http_get('/testSync'), qr/at testSync \([^)]*test\.js:12/s,
+ 'sync stack line number');
+
+like(http_get('/testAsync'), qr/message: connect failed/s,
+ 'async stack exists');
+like(http_get('/testAsync'), qr/at testAsync \([^)]*test\.js:3/s,
+ 'async stack line number');
+
+like(http_get('/testAsyncThrow'), qr/message: oops/s,
+ 'async throw stack exists');
+like(http_get('/testAsyncThrow'), qr/at testAsyncThrow \([^)]*test\.js:21/s,
+ 'async throw stack line number');
+
+###############################################################################
vm->active_frame = async_frame;
if (exception) {
+ ctx->throw_flag = 1;
njs_vm_throw(vm, value);
} else {
njs_await_rejected(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused, njs_value_t *retval)
{
- njs_value_t *value;
- njs_async_ctx_t *ctx;
-
- ctx = vm->top_frame->function->context;
-
- value = njs_arg(args, nargs, 1);
-
- if (ctx->await->native.pc == ctx->pc) {
- /* No catch block was set before await. */
- (void) njs_function_call(vm, njs_function(&ctx->capability->reject),
- &njs_value_undefined, value, 1, retval);
-
- njs_async_context_free(vm, ctx);
-
- return NJS_ERROR;
- }
-
- /* ctx->await->native.pc points to a catch block here. */
-
- ctx->pc = ctx->await->native.pc;
-
return njs_await_fulfilled(vm, args, nargs, 1, retval);
}
#endif
+ if (async_ctx != NULL && ((njs_async_ctx_t *) async_ctx)->throw_flag) {
+ goto error;
+ }
+
vmcode = (njs_vmcode_generic_t *) pc;
NEXT_LBL;
{
size_t size;
njs_int_t ret;
- njs_frame_t *frame;
njs_value_t ctor, val, on_fulfilled, on_rejected, *value, retval;
njs_function_t *fulfilled, *rejected;
njs_native_frame_t *active;
ctx->pc = (u_char *) await + sizeof(njs_vmcode_await_t);
ctx->index = await->retval;
-
- frame = (njs_frame_t *) active;
-
- if (frame->exception.catch != NULL) {
- ctx->await->native.pc = frame->exception.catch;
-
- } else {
- ctx->await->native.pc = ctx->pc;
- }
+ ctx->throw_flag = 0;
fulfilled->context = ctx;
fulfilled->args_count = 1;