From a56840d62eee1e01c19cca44d8198c4552a9174e Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Thu, 16 Jan 2020 19:18:39 +0300 Subject: [PATCH] HTTP: introduced promise support in r.subrequest(). If callback is not provided r.subrequest() returns an ordinary Promise object that resolves to subrequest response object. --- nginx/ngx_http_js_module.c | 82 +++++++++++++++++++++++++++----------- src/njs.h | 10 +++++ src/njs_function.c | 18 +++++++++ src/njs_promise.c | 22 ++++++++++ src/njs_value.h | 2 - 5 files changed, 109 insertions(+), 25 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 340887d0..5771bd51 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -32,6 +32,7 @@ typedef struct { njs_opaque_value_t request; njs_opaque_value_t request_body; ngx_str_t redirect_uri; + njs_opaque_value_t promise_callbacks[2]; } ngx_http_js_ctx_t; @@ -1759,11 +1760,31 @@ ngx_http_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data, } +static njs_int_t +ngx_http_js_promise_trampoline(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ + njs_function_t *callback; + ngx_http_js_ctx_t *ctx; + ngx_http_request_t *r; + + r = njs_vm_external(vm, njs_argument(args, 1)); + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "js subrequest promise trampoline ctx: %p", ctx); + + callback = njs_value_function(njs_value_arg(&ctx->promise_callbacks[0])); + + return njs_vm_call(vm, callback, njs_argument(args, 1), 1); +} + + static njs_int_t ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - ngx_int_t rc; + ngx_int_t rc, promise; njs_str_t uri_arg, args_arg, method_name, body_arg; ngx_uint_t method, methods_max, has_body; njs_value_t *value, *arg, *options; @@ -1799,6 +1820,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r = njs_vm_external(vm, njs_arg(args, nargs, 0)); if (njs_slow_path(r == NULL)) { + njs_vm_error(vm, "this is not an external"); return NJS_ERROR; } @@ -1829,6 +1851,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, args_arg.length = 0; args_arg.start = NULL; has_body = 0; + promise = 0; arg = njs_arg(args, nargs, 2); @@ -1901,6 +1924,15 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } } + if (callback == NULL) { + callback = njs_vm_function_alloc(vm, ngx_http_js_promise_trampoline); + if (callback == NULL) { + goto memory_error; + } + + promise = 1; + } + rc = ngx_http_js_subrequest(r, &uri_arg, &args_arg, callback, &sr); if (rc != NGX_OK) { return NJS_ERROR; @@ -1916,7 +1948,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, sr->method_name.data = method_name.start; } - sr->header_only = (sr->method == NGX_HTTP_HEAD) || (callback == NULL); + sr->header_only = (sr->method == NGX_HTTP_HEAD); if (has_body) { rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); @@ -1949,6 +1981,18 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, sr->headers_in.chunked = 0; } + if (promise) { + ctx = ngx_pcalloc(sr->pool, sizeof(ngx_http_js_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(sr, ctx, ngx_http_js_module); + + return njs_vm_promise_create(vm, njs_vm_retval(vm), + njs_value_arg(&ctx->promise_callbacks)); + } + return NJS_OK; memory_error: @@ -1971,31 +2015,23 @@ ngx_http_js_subrequest(ngx_http_request_t *r, njs_str_t *uri_arg, ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - flags = NGX_HTTP_SUBREQUEST_BACKGROUND; - - if (callback != NULL) { - ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); - if (ps == NULL) { - njs_vm_error(ctx->vm, "internal error"); - return NJS_ERROR; - } + flags = NGX_HTTP_SUBREQUEST_BACKGROUND | NGX_HTTP_SUBREQUEST_IN_MEMORY; - vm_event = njs_vm_add_event(ctx->vm, callback, 1, NULL, NULL); - if (vm_event == NULL) { - njs_vm_error(ctx->vm, "internal error"); - return NJS_ERROR; - } - - ps->handler = ngx_http_js_subrequest_done; - ps->data = vm_event; - - flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY; + ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); + if (ps == NULL) { + njs_vm_error(ctx->vm, "internal error"); + return NJS_ERROR; + } - } else { - ps = NULL; - vm_event = NULL; + vm_event = njs_vm_add_event(ctx->vm, callback, 1, NULL, NULL); + if (vm_event == NULL) { + njs_vm_error(ctx->vm, "internal error"); + return NJS_ERROR; } + ps->handler = ngx_http_js_subrequest_done; + ps->data = vm_event; + uri.len = uri_arg->length; uri.data = uri_arg->start; diff --git a/src/njs.h b/src/njs.h index 0012325d..087cfbda 100644 --- a/src/njs.h +++ b/src/njs.h @@ -102,6 +102,9 @@ struct njs_external_s { }; +typedef njs_int_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t retval); + /* * NJS and event loops. * @@ -238,6 +241,9 @@ NJS_EXPORT njs_int_t njs_vm_external_create(njs_vm_t *vm, NJS_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm, const njs_value_t *value); +NJS_EXPORT njs_function_t *njs_vm_function_alloc(njs_vm_t *vm, + njs_function_native_t native); + NJS_EXPORT void njs_disassembler(njs_vm_t *vm); NJS_EXPORT void njs_disassemble(u_char *start, u_char *end); @@ -316,4 +322,8 @@ NJS_EXPORT njs_int_t njs_vm_json_parse(njs_vm_t *vm, njs_value_t *args, NJS_EXPORT njs_int_t njs_vm_json_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs); +NJS_EXPORT njs_int_t njs_vm_promise_create(njs_vm_t *vm, njs_value_t *retval, + njs_value_t *callbacks); + + #endif /* _NJS_H_INCLUDED_ */ diff --git a/src/njs_function.c b/src/njs_function.c index 9e6cfba7..ed594902 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -73,6 +73,24 @@ fail: } +njs_function_t * +njs_vm_function_alloc(njs_vm_t *vm, njs_function_native_t native) +{ + njs_function_t *function; + + function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t)); + if (njs_slow_path(function == NULL)) { + return NULL; + } + + function->native = 1; + function->args_offset = 1; + function->u.native = native; + + return function; +} + + njs_function_t * njs_function_value_copy(njs_vm_t *vm, njs_value_t *value) { diff --git a/src/njs_promise.c b/src/njs_promise.c index 240c8574..a9a2a718 100644 --- a/src/njs_promise.c +++ b/src/njs_promise.c @@ -140,6 +140,28 @@ njs_promise_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } +njs_int_t +njs_vm_promise_create(njs_vm_t *vm, njs_value_t *retval, njs_value_t *callbacks) +{ + njs_int_t ret; + njs_promise_t *promise; + + promise = njs_promise_alloc(vm); + if (njs_slow_path(promise == NULL)) { + return NJS_ERROR; + } + + ret = njs_promise_create_resolving_functions(vm, promise, callbacks); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + njs_set_promise(retval, promise); + + return NJS_OK; +} + + static njs_promise_t * njs_promise_constructor_call(njs_vm_t *vm, njs_function_t *function) { diff --git a/src/njs_value.h b/src/njs_value.h index daf08534..b29353a0 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -108,8 +108,6 @@ typedef struct njs_object_init_s njs_object_init_t; */ typedef njs_int_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); -typedef njs_int_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t retval); #if (!NJS_HAVE_GCC_ATTRIBUTE_ALIGNED) #error "aligned attribute is required" -- 2.47.3