[njs] Modules: added ngx.fetch().
Dmitry Volyntsev
xeioex at nginx.com
Thu Jan 21 18:45:29 UTC 2021
details: https://hg.nginx.org/njs/rev/81040de6b085
branches:
changeset: 1593:81040de6b085
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Thu Jan 21 18:44:58 2021 +0000
description:
Modules: added ngx.fetch().
This is an initial implementation of Fetch API.
The following init options are supported:
body, headers, buffer_size (nginx specific), max_response_body_size
(nginx specific), method.
The following properties and methods of Response object are implemented:
arrayBuffer(), bodyUsed, json(), headers, ok, redirect, status, statusText,
text(), type, url.
The following properties and methods of Header object are implemented:
get(), getAll(), has().
Notable limitations: only http:// scheme is supported, redirects
are not handled.
In collaboration with 洪志道 (Hong Zhi Dao).
diffstat:
nginx/config | 3 +-
nginx/ngx_http_js_module.c | 65 +-
nginx/ngx_js.c | 28 +
nginx/ngx_js.h | 24 +-
nginx/ngx_js_fetch.c | 2212 ++++++++++++++++++++++++++++++++++++++++++
nginx/ngx_js_fetch.h | 18 +
nginx/ngx_stream_js_module.c | 40 +-
7 files changed, 2378 insertions(+), 12 deletions(-)
diffs (truncated from 2551 to 1000 lines):
diff -r dc7d94c05669 -r 81040de6b085 nginx/config
--- a/nginx/config Mon Jan 11 19:53:10 2021 +0000
+++ b/nginx/config Thu Jan 21 18:44:58 2021 +0000
@@ -1,7 +1,8 @@
ngx_addon_name="ngx_js_module"
NJS_DEPS="$ngx_addon_dir/ngx_js.h"
-NJS_SRCS="$ngx_addon_dir/ngx_js.c"
+NJS_SRCS="$ngx_addon_dir/ngx_js.c \
+ $ngx_addon_dir/ngx_js_fetch.c"
if [ $HTTP != NO ]; then
ngx_module_type=HTTP
diff -r dc7d94c05669 -r 81040de6b085 nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c Mon Jan 11 19:53:10 2021 +0000
+++ b/nginx/ngx_http_js_module.c Thu Jan 21 18:44:58 2021 +0000
@@ -179,6 +179,13 @@ static njs_host_event_t ngx_http_js_set_
static void ngx_http_js_clear_timer(njs_external_ptr_t external,
njs_host_event_t event);
static void ngx_http_js_timer_handler(ngx_event_t *ev);
+static ngx_pool_t *ngx_http_js_pool(njs_vm_t *vm, ngx_http_request_t *r);
+static ngx_resolver_t *ngx_http_js_resolver(njs_vm_t *vm,
+ ngx_http_request_t *r);
+static ngx_msec_t ngx_http_js_resolver_timeout(njs_vm_t *vm,
+ ngx_http_request_t *r);
+static void ngx_http_js_handle_vm_event(ngx_http_request_t *r,
+ njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs);
static void ngx_http_js_handle_event(ngx_http_request_t *r,
njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs);
@@ -576,11 +583,15 @@ static njs_vm_ops_t ngx_http_js_ops = {
static uintptr_t ngx_http_js_uptr[] = {
offsetof(ngx_http_request_t, connection),
+ (uintptr_t) ngx_http_js_pool,
+ (uintptr_t) ngx_http_js_resolver,
+ (uintptr_t) ngx_http_js_resolver_timeout,
+ (uintptr_t) ngx_http_js_handle_event,
};
static njs_vm_meta_t ngx_http_js_metas = {
- .size = 1,
+ .size = 5,
.values = ngx_http_js_uptr
};
@@ -2754,7 +2765,7 @@ ngx_http_js_subrequest_done(ngx_http_req
return NGX_ERROR;
}
- ngx_http_js_handle_event(r->parent, vm_event, njs_value_arg(&reply), 1);
+ ngx_http_js_handle_vm_event(r->parent, vm_event, njs_value_arg(&reply), 1);
return NGX_OK;
}
@@ -2895,7 +2906,6 @@ ngx_http_js_clear_timer(njs_external_ptr
static void
ngx_http_js_timer_handler(ngx_event_t *ev)
{
- ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_js_event_t *js_event;
@@ -2903,16 +2913,41 @@ ngx_http_js_timer_handler(ngx_event_t *e
r = js_event->request;
- c = r->connection;
-
ngx_http_js_handle_event(r, js_event->vm_event, NULL, 0);
-
- ngx_http_run_posted_requests(c);
+}
+
+
+static ngx_pool_t *
+ngx_http_js_pool(njs_vm_t *vm, ngx_http_request_t *r)
+{
+ return r->pool;
+}
+
+
+static ngx_resolver_t *
+ngx_http_js_resolver(njs_vm_t *vm, ngx_http_request_t *r)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ return clcf->resolver;
+}
+
+
+static ngx_msec_t
+ngx_http_js_resolver_timeout(njs_vm_t *vm, ngx_http_request_t *r)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ return clcf->resolver_timeout;
}
static void
-ngx_http_js_handle_event(ngx_http_request_t *r, njs_vm_event_t vm_event,
+ngx_http_js_handle_vm_event(ngx_http_request_t *r, njs_vm_event_t vm_event,
njs_value_t *args, njs_uint_t nargs)
{
njs_int_t rc;
@@ -2925,6 +2960,10 @@ ngx_http_js_handle_event(ngx_http_reques
rc = njs_vm_run(ctx->vm);
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js post event handler rc: %i event: %p",
+ (ngx_int_t) rc, vm_event);
+
if (rc == NJS_ERROR) {
njs_vm_retval_string(ctx->vm, &exception);
@@ -2940,6 +2979,16 @@ ngx_http_js_handle_event(ngx_http_reques
}
+static void
+ngx_http_js_handle_event(ngx_http_request_t *r, njs_vm_event_t vm_event,
+ njs_value_t *args, njs_uint_t nargs)
+{
+ ngx_http_js_handle_vm_event(r, vm_event, args, nargs);
+
+ ngx_http_run_posted_requests(r->connection);
+}
+
+
static char *
ngx_http_js_init_main_conf(ngx_conf_t *cf, void *conf)
{
diff -r dc7d94c05669 -r 81040de6b085 nginx/ngx_js.c
--- a/nginx/ngx_js.c Mon Jan 11 19:53:10 2021 +0000
+++ b/nginx/ngx_js.c Thu Jan 21 18:44:58 2021 +0000
@@ -9,6 +9,7 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_js.h"
+#include "ngx_js_fetch.h"
static njs_external_t ngx_js_ext_core[] = {
@@ -50,6 +51,17 @@ static njs_external_t ngx_js_ext_core[]
.magic32 = NGX_LOG_ERR,
}
},
+
+ {
+ .flags = NJS_EXTERN_METHOD,
+ .name.string = njs_str("fetch"),
+ .writable = 1,
+ .configurable = 1,
+ .enumerable = 1,
+ .u.method = {
+ .native = ngx_js_ext_fetch,
+ }
+ },
};
@@ -117,10 +129,16 @@ ngx_js_string(njs_vm_t *vm, njs_value_t
ngx_int_t
ngx_js_core_init(njs_vm_t *vm, ngx_log_t *log)
{
+ ngx_int_t rc;
njs_int_t ret, proto_id;
njs_str_t name;
njs_opaque_value_t value;
+ rc = ngx_js_fetch_init(vm, log);
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
proto_id = njs_vm_external_prototype(vm, ngx_js_ext_core,
njs_nitems(ngx_js_ext_core));
if (proto_id < 0) {
@@ -178,6 +196,16 @@ ngx_js_ext_constant(njs_vm_t *vm, njs_ob
njs_int_t
+ngx_js_ext_boolean(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ njs_value_boolean_set(retval, njs_vm_prop_magic32(prop));
+
+ return NJS_OK;
+}
+
+
+njs_int_t
ngx_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t level)
{
diff -r dc7d94c05669 -r 81040de6b085 nginx/ngx_js.h
--- a/nginx/ngx_js.h Mon Jan 11 19:53:10 2021 +0000
+++ b/nginx/ngx_js.h Thu Jan 21 18:44:58 2021 +0000
@@ -20,10 +20,28 @@
#define NGX_JS_BUFFER 2
#define NGX_JS_PROTO_MAIN 0
+#define NGX_JS_PROTO_RESPONSE 1
-#define ngx_external_connection(vm, ext) \
- (*((ngx_connection_t **) ((u_char *) ext + njs_vm_meta(vm, 0))))
+typedef ngx_pool_t *(*ngx_external_pool_pt)(njs_vm_t *vm, njs_external_ptr_t e);
+typedef void (*ngx_js_event_handler_pt)(njs_external_ptr_t e,
+ njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs);
+typedef ngx_resolver_t *(*ngx_external_resolver_pt)(njs_vm_t *vm,
+ njs_external_ptr_t e);
+typedef ngx_msec_t (*ngx_external_resolver_timeout_pt)(njs_vm_t *vm,
+ njs_external_ptr_t e);
+
+
+#define ngx_external_connection(vm, e) \
+ (*((ngx_connection_t **) ((u_char *) (e) + njs_vm_meta(vm, 0))))
+#define ngx_external_pool(vm, e) \
+ ((ngx_external_pool_pt) njs_vm_meta(vm, 1))(vm, e)
+#define ngx_external_resolver(vm, e) \
+ ((ngx_external_resolver_pt) njs_vm_meta(vm, 2))(vm, e)
+#define ngx_external_resolver_timeout(vm, e) \
+ ((ngx_external_resolver_timeout_pt) njs_vm_meta(vm, 3))(vm, e)
+#define ngx_external_event_handler(vm, e) \
+ ((ngx_js_event_handler_pt) njs_vm_meta(vm, 4))
#define ngx_js_prop(vm, type, value, start, len) \
@@ -41,6 +59,8 @@ njs_int_t ngx_js_ext_string(njs_vm_t *vm
njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
njs_int_t ngx_js_ext_constant(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+njs_int_t ngx_js_ext_boolean(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
ngx_int_t ngx_js_core_init(njs_vm_t *vm, ngx_log_t *log);
diff -r dc7d94c05669 -r 81040de6b085 nginx/ngx_js_fetch.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nginx/ngx_js_fetch.c Thu Jan 21 18:44:58 2021 +0000
@@ -0,0 +1,2212 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) hongzhidao
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include "ngx_js.h"
+
+
+typedef struct ngx_js_http_s ngx_js_http_t;
+
+
+typedef struct {
+ ngx_uint_t state;
+ ngx_uint_t code;
+ u_char *status_text;
+ u_char *status_text_end;
+ ngx_uint_t count;
+ ngx_flag_t chunked;
+ off_t content_length_n;
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+} ngx_js_http_parse_t;
+
+
+typedef struct {
+ u_char *pos;
+ uint64_t chunk_size;
+ uint8_t state;
+ uint8_t last;
+} ngx_js_http_chunk_parse_t;
+
+
+struct ngx_js_http_s {
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+
+ njs_vm_t *vm;
+ njs_external_ptr_t external;
+ njs_vm_event_t vm_event;
+ ngx_js_event_handler_pt event_handler;
+
+ ngx_resolver_ctx_t *ctx;
+ ngx_addr_t addr;
+ ngx_addr_t *addrs;
+ ngx_uint_t naddrs;
+ ngx_uint_t naddr;
+ in_port_t port;
+
+ ngx_peer_connection_t peer;
+ ngx_msec_t timeout;
+
+ ngx_int_t buffer_size;
+ ngx_int_t max_response_body_size;
+
+ njs_str_t url;
+ ngx_array_t headers;
+
+ ngx_buf_t *buffer;
+ ngx_buf_t *chunk;
+ njs_chb_t chain;
+
+ njs_opaque_value_t reply;
+ njs_opaque_value_t promise;
+ njs_opaque_value_t promise_callbacks[2];
+
+ uint8_t done;
+ uint8_t body_used;
+ ngx_js_http_parse_t http_parse;
+ ngx_js_http_chunk_parse_t http_chunk_parse;
+ ngx_int_t (*process)(ngx_js_http_t *http);
+};
+
+
+#define ngx_js_http_error(http, err, fmt, ...) \
+ do { \
+ njs_vm_value_error_set((http)->vm, njs_value_arg(&(http)->reply), \
+ fmt, ##__VA_ARGS__); \
+ ngx_js_http_fetch_done(http, &(http)->reply, NJS_ERROR); \
+ } while (0)
+
+
+static ngx_js_http_t *ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool,
+ ngx_log_t *log);
+static void ngx_js_resolve_handler(ngx_resolver_ctx_t *ctx);
+static njs_int_t ngx_js_fetch_result(njs_vm_t *vm, ngx_js_http_t *http,
+ njs_value_t *result, njs_int_t rc);
+static njs_int_t ngx_js_fetch_promissified_result(njs_vm_t *vm,
+ njs_value_t *result, njs_int_t rc);
+static void ngx_js_http_fetch_done(ngx_js_http_t *http,
+ njs_opaque_value_t *retval, njs_int_t rc);
+static njs_int_t ngx_js_http_promise_trampoline(njs_vm_t *vm,
+ njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
+static njs_int_t ngx_js_http_connect(ngx_js_http_t *http);
+static njs_int_t ngx_js_http_next(ngx_js_http_t *http);
+static void ngx_js_http_write_handler(ngx_event_t *wev);
+static void ngx_js_http_read_handler(ngx_event_t *rev);
+static ngx_int_t ngx_js_http_process_status_line(ngx_js_http_t *http);
+static ngx_int_t ngx_js_http_process_headers(ngx_js_http_t *http);
+static ngx_int_t ngx_js_http_process_body(ngx_js_http_t *http);
+static ngx_int_t ngx_js_http_parse_status_line(ngx_js_http_parse_t *hp,
+ ngx_buf_t *b);
+static ngx_int_t ngx_js_http_parse_header_line(ngx_js_http_parse_t *hp,
+ ngx_buf_t *b);
+static ngx_int_t ngx_js_http_parse_chunked(ngx_js_http_chunk_parse_t *hcp,
+ ngx_buf_t *b, njs_chb_t *chain);
+static void ngx_js_http_dummy_handler(ngx_event_t *ev);
+
+static njs_int_t ngx_response_js_ext_headers_get(njs_vm_t *vm,
+ njs_value_t *args, njs_uint_t nargs, njs_index_t as_array);
+static njs_int_t ngx_response_js_ext_headers_has(njs_vm_t *vm,
+ njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
+static njs_int_t ngx_response_js_ext_header(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t ngx_response_js_ext_keys(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *keys);
+static njs_int_t ngx_response_js_ext_status(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t ngx_response_js_ext_status_text(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t ngx_response_js_ext_ok(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t ngx_response_js_ext_body_used(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t ngx_response_js_ext_type(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t ngx_response_js_ext_body(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused);
+
+
+static njs_external_t ngx_js_ext_http_response_headers[] = {
+
+ {
+ .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+ .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+ .u.property = {
+ .value = "Headers",
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_METHOD,
+ .name.string = njs_str("get"),
+ .writable = 1,
+ .configurable = 1,
+ .enumerable = 1,
+ .u.method = {
+ .native = ngx_response_js_ext_headers_get,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_METHOD,
+ .name.string = njs_str("getAll"),
+ .writable = 1,
+ .configurable = 1,
+ .enumerable = 1,
+ .u.method = {
+ .native = ngx_response_js_ext_headers_get,
+ .magic8 = 1
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_METHOD,
+ .name.string = njs_str("has"),
+ .writable = 1,
+ .configurable = 1,
+ .enumerable = 1,
+ .u.method = {
+ .native = ngx_response_js_ext_headers_has,
+ }
+ },
+
+};
+
+
+static njs_external_t ngx_js_ext_http_response[] = {
+
+ {
+ .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+ .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+ .u.property = {
+ .value = "Response",
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_METHOD,
+ .name.string = njs_str("arrayBuffer"),
+ .writable = 1,
+ .configurable = 1,
+ .enumerable = 1,
+ .u.method = {
+ .native = ngx_response_js_ext_body,
+#define NGX_JS_BODY_ARRAY_BUFFER 0
+#define NGX_JS_BODY_JSON 1
+#define NGX_JS_BODY_TEXT 2
+ .magic8 = NGX_JS_BODY_ARRAY_BUFFER
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("bodyUsed"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = ngx_response_js_ext_body_used,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_OBJECT,
+ .name.string = njs_str("headers"),
+ .enumerable = 1,
+ .u.object = {
+ .enumerable = 1,
+ .properties = ngx_js_ext_http_response_headers,
+ .nproperties = njs_nitems(ngx_js_ext_http_response_headers),
+ .prop_handler = ngx_response_js_ext_header,
+ .keys = ngx_response_js_ext_keys,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_METHOD,
+ .name.string = njs_str("json"),
+ .writable = 1,
+ .configurable = 1,
+ .enumerable = 1,
+ .u.method = {
+ .native = ngx_response_js_ext_body,
+ .magic8 = NGX_JS_BODY_JSON
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("ok"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = ngx_response_js_ext_ok,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("redirected"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = ngx_js_ext_boolean,
+ .magic32 = 0,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("status"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = ngx_response_js_ext_status,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("statusText"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = ngx_response_js_ext_status_text,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_METHOD,
+ .name.string = njs_str("text"),
+ .writable = 1,
+ .configurable = 1,
+ .enumerable = 1,
+ .u.method = {
+ .native = ngx_response_js_ext_body,
+ .magic8 = NGX_JS_BODY_TEXT
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("type"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = ngx_response_js_ext_type,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("url"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = ngx_js_ext_string,
+ .magic32 = offsetof(ngx_js_http_t, url),
+ }
+ },
+};
+
+
+njs_int_t
+ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ int64_t i, length;
+ njs_int_t ret;
+ njs_str_t method, body, name, header;
+ ngx_url_t u;
+ njs_bool_t has_host;
+ ngx_pool_t *pool;
+ njs_value_t *init, *value, *headers, *keys;
+ ngx_js_http_t *http;
+ ngx_connection_t *c;
+ ngx_resolver_ctx_t *ctx;
+ njs_external_ptr_t external;
+ njs_opaque_value_t *start, lvalue, headers_value;
+
+ static const njs_str_t body_key = njs_str("body");
+ static const njs_str_t headers_key = njs_str("headers");
+ static const njs_str_t buffer_size_key = njs_str("buffer_size");
+ static const njs_str_t body_size_key = njs_str("max_response_body_size");
+ static const njs_str_t method_key = njs_str("method");
+
+ external = njs_vm_external(vm, njs_argument(args, 0));
+ if (external == NULL) {
+ njs_vm_error(vm, "\"this\" is not an external");
+ return NJS_ERROR;
+ }
+
+ c = ngx_external_connection(vm, external);
+ pool = ngx_external_pool(vm, external);
+
+ http = ngx_js_http_alloc(vm, pool, c->log);
+ if (http == NULL) {
+ return NJS_ERROR;
+ }
+
+ http->external = external;
+ http->event_handler = ngx_external_event_handler(vm, external);
+ http->buffer_size = 4096;
+ http->max_response_body_size = 32 * 1024;
+
+ ret = ngx_js_string(vm, njs_arg(args, nargs, 1), &http->url);
+ if (ret != NJS_OK) {
+ njs_vm_error(vm, "failed to convert url arg");
+ goto fail;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url.len = http->url.length;
+ u.url.data = http->url.start;
+ u.default_port = 80;
+ u.uri_part = 1;
+ u.no_resolve = 1;
+
+ if (u.url.len > 7
+ && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
+ {
+ u.url.len -= 7;
+ u.url.data += 7;
+
+ } else {
+ njs_vm_error(vm, "unsupported URL prefix");
+ goto fail;
+ }
+
+ if (ngx_parse_url(pool, &u) != NGX_OK) {
+ njs_vm_error(vm, "invalid url");
+ goto fail;
+ }
+
+ init = njs_arg(args, nargs, 2);
+
+ method = njs_str_value("GET");
+ body = njs_str_value("");
+ headers = NULL;
+
+ if (njs_value_is_object(init)) {
+ value = njs_vm_object_prop(vm, init, &method_key, &lvalue);
+ if (value != NULL && ngx_js_string(vm, value, &method) != NGX_OK) {
+ goto fail;
+ }
+
+ headers = njs_vm_object_prop(vm, init, &headers_key, &headers_value);
+ if (headers != NULL && !njs_value_is_object(headers)) {
+ njs_vm_error(vm, "headers is not an object");
+ goto fail;
+ }
+
+ value = njs_vm_object_prop(vm, init, &body_key, &lvalue);
+ if (value != NULL && ngx_js_string(vm, value, &body) != NGX_OK) {
+ goto fail;
+ }
+
+ value = njs_vm_object_prop(vm, init, &buffer_size_key, &lvalue);
+ if (value != NULL
+ && ngx_js_integer(vm, value, &http->buffer_size)
+ != NGX_OK)
+ {
+ goto fail;
+ }
+
+ value = njs_vm_object_prop(vm, init, &body_size_key, &lvalue);
+ if (value != NULL
+ && ngx_js_integer(vm, value, &http->max_response_body_size)
+ != NGX_OK)
+ {
+ goto fail;
+ }
+ }
+
+ njs_chb_init(&http->chain, njs_vm_memory_pool(vm));
+
+ njs_chb_append(&http->chain, method.start, method.length);
+ njs_chb_append_literal(&http->chain, " ");
+
+ if (u.uri.len == 0 || u.uri.data[0] != '/') {
+ njs_chb_append_literal(&http->chain, "/");
+ }
+
+ njs_chb_append(&http->chain, u.uri.data, u.uri.len);
+ njs_chb_append_literal(&http->chain, " HTTP/1.1" CRLF);
+ njs_chb_append_literal(&http->chain, "Connection: close" CRLF);
+
+ has_host = 0;
+
+ if (headers != NULL) {
+ keys = njs_vm_object_keys(vm, headers, njs_value_arg(&lvalue));
+ if (keys == NULL) {
+ goto fail;
+ }
+
+ start = (njs_opaque_value_t *) njs_vm_array_start(vm, keys);
+ if (start == NULL) {
+ goto fail;
+ }
+
+ (void) njs_vm_array_length(vm, keys, &length);
+
+ for (i = 0; i < length; i++) {
+ if (ngx_js_string(vm, njs_value_arg(start), &name) != NGX_OK) {
+ goto fail;
+ }
+
+ start++;
+
+ value = njs_vm_object_prop(vm, headers, &name, &lvalue);
+ if (ret != NJS_OK) {
+ goto fail;
+ }
+
+ if (njs_value_is_null_or_undefined(value)) {
+ continue;
+ }
+
+ if (ngx_js_string(vm, value, &header) != NGX_OK) {
+ goto fail;
+ }
+
+ if (name.length == 4
+ && ngx_strncasecmp(name.start, (u_char *) "Host", 4) == 0)
+ {
+ has_host = 1;
+ }
+
+ njs_chb_append(&http->chain, name.start, name.length);
+ njs_chb_append_literal(&http->chain, ": ");
+ njs_chb_append(&http->chain, header.start, header.length);
+ njs_chb_append_literal(&http->chain, CRLF);
+ }
+ }
+
+ if (!has_host) {
+ njs_chb_append_literal(&http->chain, "Host: ");
+ njs_chb_append(&http->chain, u.host.data, u.host.len);
+ njs_chb_append_literal(&http->chain, CRLF);
+ }
+
+ if (body.length != 0) {
+ njs_chb_sprintf(&http->chain, 32, "Content-Length: %uz" CRLF CRLF,
+ body.length);
+ njs_chb_append(&http->chain, body.start, body.length);
+
+ } else {
+ njs_chb_append_literal(&http->chain, CRLF);
+ }
+
+ if (u.addrs == NULL) {
+ ctx = ngx_resolve_start(ngx_external_resolver(vm, external), NULL);
+ if (ctx == NULL) {
+ njs_vm_memory_error(vm);
+ return NJS_ERROR;
+ }
+
+ if (ctx == NGX_NO_RESOLVER) {
+ njs_vm_error(vm, "no resolver defined");
+ goto fail;
+ }
+
+ http->ctx = ctx;
+ http->port = u.port;
+
+ ctx->name = u.host;
+ ctx->handler = ngx_js_resolve_handler;
+ ctx->data = http;
+ ctx->timeout = ngx_external_resolver_timeout(vm, external);
+
+ ret = ngx_resolve_name(http->ctx);
+ if (ret != NGX_OK) {
+ http->ctx = NULL;
+ njs_vm_memory_error(vm);
+ return NJS_ERROR;
+ }
+
+ } else {
+ http->naddrs = 1;
+ ngx_memcpy(&http->addr, &u.addrs[0], sizeof(ngx_addr_t));
+ http->addrs = &http->addr;
+
+ ret = ngx_js_http_connect(http);
+ }
+
+ return ngx_js_fetch_result(vm, http, njs_value_arg(&http->reply), ret);
+
+fail:
+
+ return ngx_js_fetch_result(vm, http, njs_vm_retval(vm), NJS_ERROR);
+}
+
+
+static ngx_js_http_t *
+ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log)
+{
+ ngx_js_http_t *http;
+
+ http = ngx_pcalloc(pool, sizeof(ngx_js_http_t));
+ if (http == NULL) {
+ goto failed;
+ }
+
+ http->pool = pool;
+ http->log = log;
+ http->vm = vm;
+
+ http->timeout = 10000;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, "js http alloc:%p", http);
+
+ return http;
+
+failed:
+
+ njs_vm_error(vm, "internal error");
+
+ return NULL;
+}
+
+
+static void
+ngx_js_resolve_handler(ngx_resolver_ctx_t *ctx)
+{
+ u_char *p;
+ size_t len;
+ socklen_t socklen;
+ ngx_uint_t i;
+ ngx_js_http_t *http;
+ struct sockaddr *sockaddr;
+
+ http = ctx->data;
+
+ if (ctx->state) {
+ ngx_js_http_error(http, 0, "\"%V\" could not be resolved (%i: %s)",
+ &ctx->name, ctx->state,
+ ngx_resolver_strerror(ctx->state));
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "http fetch resolved: \"%V\"", &ctx->name);
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+ ngx_uint_t i;
+
+ addr.data = text;
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "name was resolved to \"%V\"", &addr);
+ }
+ }
+#endif
+
+ http->naddrs = ctx->naddrs;
+ http->addrs = ngx_pcalloc(http->pool, http->naddrs * sizeof(ngx_addr_t));
+
+ if (http->addrs == NULL) {
+ goto failed;
+ }
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ socklen = ctx->addrs[i].socklen;
+
+ sockaddr = ngx_palloc(http->pool, socklen);
+ if (sockaddr == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(sockaddr, ctx->addrs[i].sockaddr, socklen);
+ ngx_inet_set_port(sockaddr, http->port);
+
+ http->addrs[i].sockaddr = sockaddr;
+ http->addrs[i].socklen = socklen;
+
+ p = ngx_pnalloc(http->pool, NGX_SOCKADDR_STRLEN);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
+ http->addrs[i].name.len = len;
+ http->addrs[i].name.data = p;
+ }
+
+ ngx_resolve_name_done(ctx);
+ http->ctx = NULL;
+
+ (void) ngx_js_http_connect(http);
+
+ return;
+
+failed:
+
+ ngx_js_http_error(http, 0, "memory error");
+}
+
+
+static void
+njs_js_http_destructor(njs_external_ptr_t external, njs_host_event_t host)
+{
+ ngx_js_http_t *http;
+
+ http = host;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js http destructor:%p",
+ http);
+
+ if (http->ctx != NULL) {
+ ngx_resolve_name_done(http->ctx);
+ http->ctx = NULL;
+ }
+
+ if (http->peer.connection != NULL) {
+ ngx_close_connection(http->peer.connection);
+ http->peer.connection = NULL;
+ }
+}
+
+
+static njs_int_t
+ngx_js_fetch_result(njs_vm_t *vm, ngx_js_http_t *http, njs_value_t *result,
+ njs_int_t rc)
+{
+ njs_int_t ret;
+ njs_function_t *callback;
+ njs_vm_event_t vm_event;
+ njs_opaque_value_t arguments[2];
+
+ ret = njs_vm_promise_create(vm, njs_value_arg(&http->promise),
+ njs_value_arg(&http->promise_callbacks));
+ if (ret != NJS_OK) {
+ goto error;
+ }
+
+ callback = njs_vm_function_alloc(vm, ngx_js_http_promise_trampoline);
+ if (callback == NULL) {
+ goto error;
+ }
+
+ vm_event = njs_vm_add_event(vm, callback, 1, http, njs_js_http_destructor);
+ if (vm_event == NULL) {
+ goto error;
+ }
+
+ http->vm_event = vm_event;
+
+ if (rc == NJS_ERROR) {
+ njs_value_assign(&arguments[0], &http->promise_callbacks[1]);
+ njs_value_assign(&arguments[1], result);
+
+ ret = njs_vm_post_event(vm, vm_event, njs_value_arg(&arguments), 2);
+ if (ret == NJS_ERROR) {
+ goto error;
+ }
+ }
+
+ njs_vm_retval_set(vm, njs_value_arg(&http->promise));
+
+ return NJS_OK;
+
+error:
+
+ njs_vm_error(vm, "internal error");
+
+ return NJS_ERROR;
+}
+
+
+static njs_int_t
+ngx_js_fetch_promissified_result(njs_vm_t *vm, njs_value_t *result,
+ njs_int_t rc)
+{
+ njs_int_t ret;
+ njs_function_t *callback;
+ njs_vm_event_t vm_event;
+ njs_opaque_value_t retval, arguments[2];
+
+ ret = njs_vm_promise_create(vm, njs_value_arg(&retval),
+ njs_value_arg(&arguments));
+ if (ret != NJS_OK) {
+ goto error;
+ }
+
+ callback = njs_vm_function_alloc(vm, ngx_js_http_promise_trampoline);
More information about the nginx-devel
mailing list