]> git.kaiwu.me - njs.git/commitdiff
HTTP: introduced promise support in r.subrequest().
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 16 Jan 2020 16:18:39 +0000 (19:18 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Thu, 16 Jan 2020 16:18:39 +0000 (19:18 +0300)
If callback is not provided r.subrequest() returns an ordinary
Promise object that resolves to subrequest response object.

nginx/ngx_http_js_module.c
src/njs.h
src/njs_function.c
src/njs_promise.c
src/njs_value.h

index 340887d0b8a8c3779a01c0554998f29b1cf1c8af..5771bd51eb62310d3feb4f4e96bc88b1890f97ce 100644 (file)
@@ -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;
 
index 0012325df9be1fe3a86db3e54a112169e77ac9ee..087cfbda496e96e021ae4809355e4f4ea4f0963b 100644 (file)
--- 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_ */
index 9e6cfba7b17ca27448c7968a1f7c577fa80aa8d7..ed59490295fc0471268e04811d7c0e0a3fd5fb7f 100644 (file)
@@ -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)
 {
index 240c8574524aae5a56f560a8b46c4e960c030a4b..a9a2a71845a9a3379ff4c39565f809d7f95f7319 100644 (file)
@@ -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)
 {
index daf08534aa12d4be60d9b838c08c176351bf9950..b29353a063b8d90a3724aeb5c513b086da93139e 100644 (file)
@@ -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"