summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorKonstantin Pavlov <thresh@nginx.com>2023-08-31 09:41:46 -0700
committerKonstantin Pavlov <thresh@nginx.com>2023-08-31 09:41:46 -0700
commitc45c8919c7232eb20023484f6d1fc9f1f50395d8 (patch)
treecc12eb307c1611494948645e4b487fa06495c3d2 /src
parent88c90e1c351ab8c5bd487a5cd4b735014b08e271 (diff)
parent9b22b6957bc87b3df002d0bc691fdae6a20abdac (diff)
downloadunit-1.31.0-1.tar.gz
unit-1.31.0-1.tar.bz2
Merged with the default branch.1.31.0-1
Diffstat (limited to 'src')
-rw-r--r--src/nxt_application.c3
-rw-r--r--src/nxt_application.h19
-rw-r--r--src/nxt_conf_validation.c92
-rw-r--r--src/nxt_h1proto.c2
-rw-r--r--src/nxt_h1proto.h3
-rw-r--r--src/nxt_http.h10
-rw-r--r--src/nxt_http_request.c14
-rw-r--r--src/nxt_http_rewrite.c14
-rw-r--r--src/nxt_http_route.c17
-rw-r--r--src/nxt_http_set_headers.c176
-rw-r--r--src/nxt_http_static.c2
-rw-r--r--src/nxt_http_variables.c391
-rw-r--r--src/nxt_js.c33
-rw-r--r--src/nxt_main.h2
-rw-r--r--src/nxt_main_process.c55
-rw-r--r--src/nxt_router.c13
-rw-r--r--src/nxt_runtime.c31
-rw-r--r--src/nxt_tstr.c18
-rw-r--r--src/nxt_tstr.h6
-rw-r--r--src/nxt_var.c196
-rw-r--r--src/nxt_var.h29
-rw-r--r--src/python/nxt_python_asgi.c48
-rw-r--r--src/python/nxt_python_asgi_lifespan.c54
-rw-r--r--src/python/nxt_python_asgi_str.c2
-rw-r--r--src/python/nxt_python_asgi_str.h1
-rw-r--r--src/test/nxt_unit_app_test.c4
-rw-r--r--src/unit.pc.in11
-rw-r--r--src/wasm/nxt_rt_wasmtime.c412
-rw-r--r--src/wasm/nxt_wasm.c296
-rw-r--r--src/wasm/nxt_wasm.h138
30 files changed, 1847 insertions, 245 deletions
diff --git a/src/nxt_application.c b/src/nxt_application.c
index ffa8eb53..872e387a 100644
--- a/src/nxt_application.c
+++ b/src/nxt_application.c
@@ -1099,6 +1099,9 @@ nxt_app_parse_type(u_char *p, size_t length)
} else if (nxt_str_eq(&str, "java", 4)) {
return NXT_APP_JAVA;
+
+ } else if (nxt_str_eq(&str, "wasm", 4)) {
+ return NXT_APP_WASM;
}
return NXT_APP_UNKNOWN;
diff --git a/src/nxt_application.h b/src/nxt_application.h
index 2675e6a0..64866db6 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -21,6 +21,7 @@ typedef enum {
NXT_APP_PERL,
NXT_APP_RUBY,
NXT_APP_JAVA,
+ NXT_APP_WASM,
NXT_APP_UNKNOWN,
} nxt_app_type_t;
@@ -86,6 +87,23 @@ typedef struct {
} nxt_java_app_conf_t;
+typedef struct {
+ const char *module;
+
+ const char *request_handler;
+ const char *malloc_handler;
+ const char *free_handler;
+
+ const char *module_init_handler;
+ const char *module_end_handler;
+ const char *request_init_handler;
+ const char *request_end_handler;
+ const char *response_end_handler;
+
+ nxt_conf_value_t *access;
+} nxt_wasm_app_conf_t;
+
+
struct nxt_common_app_conf_s {
nxt_str_t name;
nxt_str_t type;
@@ -114,6 +132,7 @@ struct nxt_common_app_conf_s {
nxt_perl_app_conf_t perl;
nxt_ruby_app_conf_t ruby;
nxt_java_app_conf_t java;
+ nxt_wasm_app_conf_t wasm;
} u;
nxt_conf_value_t *self;
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 8c75a9fe..f00b28b8 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -167,6 +167,8 @@ static nxt_int_t nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_match_addr(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_response_header(nxt_conf_validation_t *vldt,
+ nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_forwarded(nxt_conf_validation_t *vldt,
@@ -250,6 +252,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_access_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[];
@@ -688,6 +691,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_action_common_members[] = {
.name = nxt_string("rewrite"),
.type = NXT_CONF_VLDT_STRING,
},
+ {
+ .name = nxt_string("response_headers"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object_iterator,
+ .u.object = nxt_conf_vldt_response_header,
+ },
NXT_CONF_VLDT_END
};
@@ -1041,6 +1050,59 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = {
};
+static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_members[] = {
+ {
+ .name = nxt_string("module"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("request_handler"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ },{
+ .name = nxt_string("malloc_handler"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("free_handler"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("module_init_handler"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("module_end_handler"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("request_init_handler"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("request_end_handler"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("response_end_handler"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("access"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_wasm_access_members,
+ },
+
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
+};
+
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_access_members[] = {
+ {
+ .name = nxt_string("filesystem"),
+ .type = NXT_CONF_VLDT_ARRAY,
+ },
+
+ NXT_CONF_VLDT_END
+};
+
+
static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = {
{
.name = nxt_string("type"),
@@ -2448,6 +2510,35 @@ nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt,
static nxt_int_t
+nxt_conf_vldt_response_header(nxt_conf_validation_t *vldt, nxt_str_t *name,
+ nxt_conf_value_t *value)
+{
+ nxt_uint_t type;
+
+ static nxt_str_t content_length = nxt_string("Content-Length");
+
+ if (name->length == 0) {
+ return nxt_conf_vldt_error(vldt, "The response header name "
+ "must not be empty.");
+ }
+
+ if (nxt_strstr_eq(name, &content_length)) {
+ return nxt_conf_vldt_error(vldt, "The \"Content-Length\" response "
+ "header value is not supported");
+ }
+
+ type = nxt_conf_type(value);
+
+ if (type == NXT_CONF_STRING || type == NXT_CONF_NULL) {
+ return NXT_OK;
+ }
+
+ return nxt_conf_vldt_error(vldt, "The \"%V\" response header value "
+ "must either be a string or a null", name);
+}
+
+
+static nxt_int_t
nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
@@ -2525,6 +2616,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name,
{ nxt_conf_vldt_object, nxt_conf_vldt_perl_members },
{ nxt_conf_vldt_object, nxt_conf_vldt_ruby_members },
{ nxt_conf_vldt_object, nxt_conf_vldt_java_members },
+ { nxt_conf_vldt_object, nxt_conf_vldt_wasm_members },
};
ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT);
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index df1f82f9..1dfe4b6e 100644
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
@@ -1284,7 +1284,7 @@ nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r,
size += NXT_WEBSOCKET_ACCEPT_SIZE + 2;
} else {
- http11 = (h1p->parser.version.s.minor != '0');
+ http11 = nxt_h1p_is_http11(h1p);
if (r->resp.content_length == NULL || r->resp.content_length->skip) {
diff --git a/src/nxt_h1proto.h b/src/nxt_h1proto.h
index f8500963..b324db8d 100644
--- a/src/nxt_h1proto.h
+++ b/src/nxt_h1proto.h
@@ -51,4 +51,7 @@ struct nxt_h1proto_s {
nxt_conn_t *conn;
};
+#define nxt_h1p_is_http11(h1p) \
+ ((h1p)->parser.version.s.minor != '0')
+
#endif /* _NXT_H1PROTO_H_INCLUDED_ */
diff --git a/src/nxt_http.h b/src/nxt_http.h
index 08e1fcbe..e812bd0d 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -173,6 +173,7 @@ struct nxt_http_request_s {
nxt_tstr_query_t *tstr_query;
nxt_tstr_cache_t tstr_cache;
+ nxt_http_action_t *action;
void *req_rpc_data;
#if (NXT_HAVE_REGEX)
@@ -227,6 +228,7 @@ typedef struct nxt_http_route_addr_rule_s nxt_http_route_addr_rule_t;
typedef struct {
nxt_conf_value_t *rewrite;
+ nxt_conf_value_t *set_headers;
nxt_conf_value_t *pass;
nxt_conf_value_t *ret;
nxt_conf_value_t *location;
@@ -255,6 +257,7 @@ struct nxt_http_action_s {
} u;
nxt_tstr_t *rewrite;
+ nxt_array_t *set_headers; /* of nxt_http_field_t */
nxt_http_action_t *fallback;
};
@@ -382,8 +385,11 @@ nxt_int_t nxt_upstreams_joint_create(nxt_router_temp_conf_t *tmcf,
nxt_int_t nxt_http_rewrite_init(nxt_router_conf_t *rtcf,
nxt_http_action_t *action, nxt_http_action_conf_t *acf);
-nxt_int_t nxt_http_rewrite(nxt_task_t *task, nxt_http_request_t *r,
- nxt_http_action_t *action);
+nxt_int_t nxt_http_rewrite(nxt_task_t *task, nxt_http_request_t *r);
+
+nxt_int_t nxt_http_set_headers_init(nxt_router_conf_t *rtcf,
+ nxt_http_action_t *action, nxt_http_action_conf_t *acf);
+nxt_int_t nxt_http_set_headers(nxt_http_request_t *r);
nxt_int_t nxt_http_return_init(nxt_router_conf_t *rtcf,
nxt_http_action_t *action, nxt_http_action_conf_t *acf);
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 48f7dbe3..e532baff 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -560,11 +560,9 @@ nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r,
if (nxt_fast_path(action != NULL)) {
do {
- if (action->rewrite != NULL) {
- ret = nxt_http_rewrite(task, r, action);
- if (nxt_slow_path(ret != NXT_OK)) {
- break;
- }
+ ret = nxt_http_rewrite(task, r);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ break;
}
action = action->handler(task, r, action);
@@ -632,9 +630,15 @@ nxt_http_request_header_send(nxt_task_t *task, nxt_http_request_t *r,
nxt_work_handler_t body_handler, void *data)
{
u_char *p, *end, *server_string;
+ nxt_int_t ret;
nxt_http_field_t *server, *date, *content_length;
nxt_socket_conf_t *skcf;
+ ret = nxt_http_set_headers(r);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+
/*
* TODO: "Server", "Date", and "Content-Length" processing should be moved
* to the last header filter.
diff --git a/src/nxt_http_rewrite.c b/src/nxt_http_rewrite.c
index b800a919..ae5c865a 100644
--- a/src/nxt_http_rewrite.c
+++ b/src/nxt_http_rewrite.c
@@ -10,8 +10,8 @@
nxt_int_t
nxt_http_rewrite_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action,
- nxt_http_action_conf_t *acf)
- {
+ nxt_http_action_conf_t *acf)
+{
nxt_str_t str;
nxt_conf_get_string(acf->rewrite, &str);
@@ -26,15 +26,21 @@ nxt_http_rewrite_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action,
nxt_int_t
-nxt_http_rewrite(nxt_task_t *task, nxt_http_request_t *r,
- nxt_http_action_t *action)
+nxt_http_rewrite(nxt_task_t *task, nxt_http_request_t *r)
{
u_char *p;
nxt_int_t ret;
nxt_str_t str, encoded_path, target;
nxt_router_conf_t *rtcf;
+ nxt_http_action_t *action;
nxt_http_request_parse_t rp;
+ action = r->action;
+
+ if (action == NULL || action->rewrite == NULL) {
+ return NXT_OK;
+ }
+
if (nxt_tstr_is_const(action->rewrite)) {
nxt_tstr_str(action->rewrite, &str);
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 0935dd4a..4a64d5c1 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -584,6 +584,11 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = {
offsetof(nxt_http_action_conf_t, rewrite)
},
{
+ nxt_string("response_headers"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_action_conf_t, set_headers)
+ },
+ {
nxt_string("pass"),
NXT_CONF_MAP_PTR,
offsetof(nxt_http_action_conf_t, pass)
@@ -671,6 +676,13 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
}
}
+ if (acf.set_headers != NULL) {
+ ret = nxt_http_set_headers_init(rtcf, action, &acf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
if (acf.ret != NULL) {
return nxt_http_return_init(rtcf, action, &acf);
}
@@ -1573,6 +1585,11 @@ nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r,
}
if (action != NULL) {
+
+ if (action != NXT_HTTP_ACTION_ERROR) {
+ r->action = action;
+ }
+
return action;
}
}
diff --git a/src/nxt_http_set_headers.c b/src/nxt_http_set_headers.c
new file mode 100644
index 00000000..25dd7478
--- /dev/null
+++ b/src/nxt_http_set_headers.c
@@ -0,0 +1,176 @@
+
+/*
+ * Copyright (C) Zhidao HONG
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_router.h>
+#include <nxt_http.h>
+
+
+typedef struct {
+ nxt_str_t name;
+ nxt_tstr_t *value;
+} nxt_http_header_val_t;
+
+
+nxt_int_t
+nxt_http_set_headers_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action,
+ nxt_http_action_conf_t *acf)
+ {
+ uint32_t next;
+ nxt_str_t str, name;
+ nxt_array_t *headers;
+ nxt_conf_value_t *value;
+ nxt_http_header_val_t *hv;
+
+ headers = nxt_array_create(rtcf->mem_pool, 4,
+ sizeof(nxt_http_header_val_t));
+ if (nxt_slow_path(headers == NULL)) {
+ return NXT_ERROR;
+ }
+
+ action->set_headers = headers;
+
+ next = 0;
+
+ for ( ;; ) {
+ value = nxt_conf_next_object_member(acf->set_headers, &name, &next);
+ if (value == NULL) {
+ break;
+ }
+
+ hv = nxt_array_zero_add(headers);
+ if (nxt_slow_path(hv == NULL)) {
+ return NXT_ERROR;
+ }
+
+ hv->name.length = name.length;
+
+ hv->name.start = nxt_mp_nget(rtcf->mem_pool, name.length);
+ if (nxt_slow_path(hv->name.start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(hv->name.start, name.start, name.length);
+
+ if (nxt_conf_type(value) == NXT_CONF_STRING) {
+ nxt_conf_get_string(value, &str);
+
+ hv->value = nxt_tstr_compile(rtcf->tstr_state, &str, 0);
+ if (nxt_slow_path(hv->value == NULL)) {
+ return NXT_ERROR;
+ }
+ }
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_http_field_t *
+nxt_http_resp_header_find(nxt_http_request_t *r, u_char *name, size_t length)
+{
+ nxt_http_field_t *f;
+
+ nxt_list_each(f, r->resp.fields) {
+
+ if (f->skip) {
+ continue;
+ }
+
+ if (length == f->name_length
+ && nxt_memcasecmp(name, f->name, f->name_length) == 0)
+ {
+ return f;
+ }
+
+ } nxt_list_loop;
+
+ return NULL;
+}
+
+
+nxt_int_t
+nxt_http_set_headers(nxt_http_request_t *r)
+{
+ nxt_int_t ret;
+ nxt_uint_t i, n;
+ nxt_str_t *value;
+ nxt_http_field_t *f;
+ nxt_router_conf_t *rtcf;
+ nxt_http_action_t *action;
+ nxt_http_header_val_t *hv, *header;
+
+ action = r->action;
+
+ if (action == NULL || action->set_headers == NULL) {
+ return NXT_OK;
+ }
+
+ if ((r->status < NXT_HTTP_OK || r->status >= NXT_HTTP_BAD_REQUEST)) {
+ return NXT_OK;
+ }
+
+ rtcf = r->conf->socket_conf->router_conf;
+
+ header = action->set_headers->elts;
+ n = action->set_headers->nelts;
+
+ value = nxt_mp_zalloc(r->mem_pool, sizeof(nxt_str_t) * n);
+ if (nxt_slow_path(value == NULL)) {
+ return NXT_ERROR;
+ }
+
+ for (i = 0; i < n; i++) {
+ hv = &header[i];
+
+ if (hv->value == NULL) {
+ continue;
+ }
+
+ if (nxt_tstr_is_const(hv->value)) {
+ nxt_tstr_str(hv->value, &value[i]);
+
+ } else {
+ ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
+ &r->tstr_cache, r, r->mem_pool);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ nxt_tstr_query(&r->task, r->tstr_query, hv->value, &value[i]);
+
+ if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) {
+ return NXT_ERROR;
+ }
+ }
+ }
+
+ for (i = 0; i < n; i++) {
+ hv = &header[i];
+
+ f = nxt_http_resp_header_find(r, hv->name.start, hv->name.length);
+
+ if (value[i].start != NULL) {
+
+ if (f == NULL) {
+ f = nxt_list_zero_add(r->resp.fields);
+ if (nxt_slow_path(f == NULL)) {
+ return NXT_ERROR;
+ }
+
+ f->name = hv->name.start;
+ f->name_length = hv->name.length;
+ }
+
+ f->value = value[i].start;
+ f->value_length = value[i].length;
+
+ } else if (f != NULL) {
+ f->skip = 1;
+ }
+ }
+
+ return NXT_OK;
+}
diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c
index 5e44aab4..e51ba6b0 100644
--- a/src/nxt_http_static.c
+++ b/src/nxt_http_static.c
@@ -696,6 +696,8 @@ nxt_http_static_next(nxt_task_t *task, nxt_http_request_t *r,
if (nxt_slow_path(r->log_route)) {
nxt_log(task, NXT_LOG_NOTICE, "\"fallback\" taken");
}
+
+ r->action = action->fallback;
nxt_http_request_action(task, r, action->fallback);
return;
}
diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c
index b73d9151..46594a6b 100644
--- a/src/nxt_http_variables.c
+++ b/src/nxt_http_variables.c
@@ -5,96 +5,118 @@
#include <nxt_router.h>
#include <nxt_http.h>
+#include <nxt_h1proto.h>
static nxt_int_t nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_method(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_request_uri(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_uri(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field);
+ void *data);
static nxt_int_t nxt_http_var_host(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field);
+ void *data);
static nxt_int_t nxt_http_var_remote_addr(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_time_local(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static u_char *nxt_http_log_date(u_char *buf, nxt_realtime_t *now,
struct tm *tm, size_t size, const char *format);
static nxt_int_t nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_status(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_referer(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_user_agent(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
+static nxt_int_t nxt_http_var_response_connection(nxt_task_t *task,
+ nxt_str_t *str, void *ctx, void *data);
+static nxt_int_t nxt_http_var_response_content_length(nxt_task_t *task,
+ nxt_str_t *str, void *ctx, void *data);
+static nxt_int_t nxt_http_var_response_transfer_encoding(nxt_task_t *task,
+ nxt_str_t *str, void *ctx, void *data);
static nxt_int_t nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field);
+ void *data);
static nxt_int_t nxt_http_var_header(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
static nxt_int_t nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
+static nxt_int_t nxt_http_var_response_header(nxt_task_t *task, nxt_str_t *str,
+ void *ctx, void *data);
static nxt_var_decl_t nxt_http_vars[] = {
{
.name = nxt_string("dollar"),
.handler = nxt_http_var_dollar,
+ .cacheable = 1,
}, {
.name = nxt_string("request_time"),
.handler = nxt_http_var_request_time,
+ .cacheable = 1,
}, {
.name = nxt_string("method"),
.handler = nxt_http_var_method,
+ .cacheable = 1,
}, {
.name = nxt_string("request_uri"),
.handler = nxt_http_var_request_uri,
+ .cacheable = 1,
}, {
.name = nxt_string("uri"),
.handler = nxt_http_var_uri,
+ .cacheable = 0,
}, {
.name = nxt_string("host"),
.handler = nxt_http_var_host,
+ .cacheable = 1,
}, {
.name = nxt_string("remote_addr"),
.handler = nxt_http_var_remote_addr,
+ .cacheable = 1,
}, {
.name = nxt_string("time_local"),
.handler = nxt_http_var_time_local,
+ .cacheable = 1,
}, {
.name = nxt_string("request_line"),
.handler = nxt_http_var_request_line,
+ .cacheable = 1,
}, {
.name = nxt_string("status"),
.handler = nxt_http_var_status,
+ .cacheable = 1,
}, {
.name = nxt_string("body_bytes_sent"),
.handler = nxt_http_var_body_bytes_sent,
+ .cacheable = 1,
}, {
.name = nxt_string("header_referer"),
.handler = nxt_http_var_referer,
+ .cacheable = 1,
}, {
- .name = nxt_string("header_user_agent"),
- .handler = nxt_http_var_user_agent,
+ .name = nxt_string("response_header_connection"),
+ .handler = nxt_http_var_response_connection,
+ .cacheable = 1,
}, {
- .name = nxt_string("arg"),
- .handler = nxt_http_var_arg,
- .field_hash = nxt_http_argument_hash,
+ .name = nxt_string("response_header_content_length"),
+ .handler = nxt_http_var_response_content_length,
+ .cacheable = 1,
}, {
- .name = nxt_string("header"),
- .handler = nxt_http_var_header,
- .field_hash = nxt_http_header_hash,
+ .name = nxt_string("response_header_transfer_encoding"),
+ .handler = nxt_http_var_response_transfer_encoding,
+ .cacheable = 1,
}, {
- .name = nxt_string("cookie"),
- .handler = nxt_http_var_cookie,
- .field_hash = nxt_http_cookie_hash,
+ .name = nxt_string("header_user_agent"),
+ .handler = nxt_http_var_user_agent,
+ .cacheable = 1,
},
};
@@ -106,8 +128,99 @@ nxt_http_register_variables(void)
}
+nxt_int_t
+nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref,
+ nxt_str_t *name)
+{
+ int64_t hash;
+ nxt_str_t str, *lower;
+
+ if (nxt_str_start(name, "response_header_", 16)) {
+ ref->handler = nxt_http_var_response_header;
+ ref->cacheable = 0;
+
+ str.start = name->start + 16;
+ str.length = name->length - 16;
+
+ if (str.length == 0) {
+ return NXT_ERROR;
+ }
+
+ lower = nxt_str_alloc(state->pool, str.length);
+ if (nxt_slow_path(lower == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy_lowcase(lower->start, str.start, str.length);
+
+ ref->data = lower;
+
+ return NXT_OK;
+ }
+
+ if (nxt_str_start(name, "header_", 7)) {
+ ref->handler = nxt_http_var_header;
+ ref->cacheable = 1;
+
+ str.start = name->start + 7;
+ str.length = name->length - 7;
+
+ if (str.length == 0) {
+ return NXT_ERROR;
+ }
+
+ hash = nxt_http_header_hash(state->pool, &str);
+ if (nxt_slow_path(hash == -1)) {
+ return NXT_ERROR;
+ }
+
+ } else if (nxt_str_start(name, "arg_", 4)) {
+ ref->handler = nxt_http_var_arg;
+ ref->cacheable = 1;
+
+ str.start = name->start + 4;
+ str.length = name->length - 4;
+
+ if (str.length == 0) {
+ return NXT_ERROR;
+ }
+
+ hash = nxt_http_argument_hash(state->pool, &str);
+ if (nxt_slow_path(hash == -1)) {
+ return NXT_ERROR;
+ }
+
+ } else if (nxt_str_start(name, "cookie_", 7)) {
+ ref->handler = nxt_http_var_cookie;
+ ref->cacheable = 1;
+
+ str.start = name->start + 7;
+ str.length = name->length - 7;
+
+ if (str.length == 0) {
+ return NXT_ERROR;
+ }
+
+ hash = nxt_http_cookie_hash(state->pool, &str);
+ if (nxt_slow_path(hash == -1)) {
+ return NXT_ERROR;
+ }
+
+ } else {
+ return NXT_ERROR;
+ }
+
+ ref->data = nxt_var_field_new(state->pool, &str, (uint32_t) hash);
+ if (nxt_slow_path(ref->data == NULL)) {
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
static nxt_int_t
-nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
+nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_str_set(str, "$");
@@ -117,7 +230,7 @@ nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
static nxt_int_t
nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field)
+ void *data)
{
u_char *p;
nxt_msec_t ms;
@@ -144,7 +257,7 @@ nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str, void *ctx,
static nxt_int_t
-nxt_http_var_method(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
+nxt_http_var_method(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_http_request_t *r;
@@ -158,7 +271,7 @@ nxt_http_var_method(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
static nxt_int_t
nxt_http_var_request_uri(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field)
+ void *data)
{
nxt_http_request_t *r;
@@ -171,7 +284,7 @@ nxt_http_var_request_uri(nxt_task_t *task, nxt_str_t *str, void *ctx,
static nxt_int_t
-nxt_http_var_uri(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
+nxt_http_var_uri(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_http_request_t *r;
@@ -184,7 +297,7 @@ nxt_http_var_uri(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
static nxt_int_t
-nxt_http_var_host(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
+nxt_http_var_host(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_http_request_t *r;
@@ -198,7 +311,7 @@ nxt_http_var_host(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
static nxt_int_t
nxt_http_var_remote_addr(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field)
+ void *data)
{
nxt_http_request_t *r;
@@ -212,8 +325,7 @@ nxt_http_var_remote_addr(nxt_task_t *task, nxt_str_t *str, void *ctx,
static nxt_int_t
-nxt_http_var_time_local(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field)
+nxt_http_var_time_local(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_http_request_t *r;
@@ -271,7 +383,7 @@ nxt_http_log_date(u_char *buf, nxt_realtime_t *now, struct tm *tm,
static nxt_int_t
nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field)
+ void *data)
{
nxt_http_request_t *r;
@@ -285,8 +397,9 @@ nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str, void *ctx,
static nxt_int_t
nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field)
+ void *data)
{
+ u_char *p;
nxt_off_t bytes;
nxt_http_request_t *r;
@@ -299,16 +412,18 @@ nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, void *ctx,
bytes = nxt_http_proto[r->protocol].body_bytes_sent(task, r->proto);
- str->length = nxt_sprintf(str->start, str->start + NXT_OFF_T_LEN, "%O",
- bytes) - str->start;
+ p = nxt_sprintf(str->start, str->start + NXT_OFF_T_LEN, "%O", bytes);
+
+ str->length = p - str->start;
return NXT_OK;
}
static nxt_int_t
-nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
+nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
+ u_char *p;
nxt_http_request_t *r;
r = ctx;
@@ -318,16 +433,16 @@ nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
return NXT_ERROR;
}
- str->length = nxt_sprintf(str->start, str->start + 3, "%03d", r->status)
- - str->start;
+ p = nxt_sprintf(str->start, str->start + 3, "%03d", r->status);
+
+ str->length = p - str->start;
return NXT_OK;
}
static nxt_int_t
-nxt_http_var_referer(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field)
+nxt_http_var_referer(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_http_request_t *r;
@@ -346,8 +461,7 @@ nxt_http_var_referer(nxt_task_t *task, nxt_str_t *str, void *ctx,
static nxt_int_t
-nxt_http_var_user_agent(nxt_task_t *task, nxt_str_t *str, void *ctx,
- uint16_t field)
+nxt_http_var_user_agent(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_http_request_t *r;
@@ -366,19 +480,112 @@ nxt_http_var_user_agent(nxt_task_t *task, nxt_str_t *str, void *ctx,
static nxt_int_t
-nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
+nxt_http_var_response_connection(nxt_task_t *task, nxt_str_t *str, void *ctx,
+ void *data)
+{
+ nxt_int_t conn;
+ nxt_bool_t http11;
+ nxt_h1proto_t *h1p;
+ nxt_http_request_t *r;
+
+ static const nxt_str_t connection[3] = {
+ nxt_string("close"),
+ nxt_string("keep-alive"),
+ nxt_string("Upgrade"),
+ };
+
+ r = ctx;
+ h1p = r->proto.h1;
+
+ conn = -1;
+
+ if (r->websocket_handshake && r->status == NXT_HTTP_SWITCHING_PROTOCOLS) {
+ conn = 2;
+
+ } else {
+ http11 = nxt_h1p_is_http11(h1p);
+
+ if (http11 ^ h1p->keepalive) {
+ conn = h1p->keepalive;
+ }
+ }
+
+ if (conn >= 0) {
+ *str = connection[conn];
+
+ } else {
+ nxt_str_null(str);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_http_var_response_content_length(nxt_task_t *task, nxt_str_t *str,
+ void *ctx, void *data)
+{
+ u_char *p;
+ nxt_http_request_t *r;
+
+ r = ctx;
+
+ if (r->resp.content_length != NULL) {
+ str->length = r->resp.content_length->value_length;
+ str->start = r->resp.content_length->value;
+
+ return NXT_OK;
+ }
+
+ if (r->resp.content_length_n >= 0) {
+ str->start = nxt_mp_nget(r->mem_pool, NXT_OFF_T_LEN);
+ if (str->start == NULL) {
+ return NXT_ERROR;
+ }
+
+ p = nxt_sprintf(str->start, str->start + NXT_OFF_T_LEN,
+ "%O", r->resp.content_length_n);
+
+ str->length = p - str->start;
+
+ return NXT_OK;
+ }
+
+ nxt_str_null(str);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_http_var_response_transfer_encoding(nxt_task_t *task, nxt_str_t *str,
+ void *ctx, void *data)
+{
+ nxt_http_request_t *r;
+
+ r = ctx;
+
+ if (r->proto.h1->chunked) {
+ nxt_str_set(str, "chunked");
+
+ } else {
+ nxt_str_null(str);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_array_t *args;
nxt_var_field_t *vf;
- nxt_router_conf_t *rtcf;
nxt_http_request_t *r;
nxt_http_name_value_t *nv, *start;
r = ctx;
-
- rtcf = r->conf->socket_conf->router_conf;
-
- vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field);
+ vf = data;
args = nxt_http_arguments_parse(r);
if (nxt_slow_path(args == NULL)) {
@@ -410,18 +617,14 @@ nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
static nxt_int_t
-nxt_http_var_header(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
+nxt_http_var_header(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_var_field_t *vf;
nxt_http_field_t *f;
- nxt_router_conf_t *rtcf;
nxt_http_request_t *r;
r = ctx;
-
- rtcf = r->conf->socket_conf->router_conf;
-
- vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field);
+ vf = data;
nxt_list_each(f, r->fields) {
@@ -444,19 +647,15 @@ nxt_http_var_header(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
static nxt_int_t
-nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
+nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
{
nxt_array_t *cookies;
nxt_var_field_t *vf;
- nxt_router_conf_t *rtcf;
nxt_http_request_t *r;
nxt_http_name_value_t *nv, *end;
r = ctx;
-
- rtcf = r->conf->socket_conf->router_conf;
-
- vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field);
+ vf = data;
cookies = nxt_http_cookies_parse(r);
if (nxt_slow_path(cookies == NULL)) {
@@ -485,3 +684,65 @@ nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
return NXT_OK;
}
+
+
+static int
+nxt_http_field_name_cmp(nxt_str_t *name, nxt_http_field_t *field)
+{
+ size_t i;
+ u_char c1, c2;
+
+ if (name->length != field->name_length) {
+ return 1;
+ }
+
+ for (i = 0; i < name->length; i++) {
+ c1 = name->start[i];
+ c2 = field->name[i];
+
+ if (c2 >= 'A' && c2 <= 'Z') {
+ c2 |= 0x20;
+
+ } else if (c2 == '-') {
+ c2 = '_';
+ }
+
+ if (c1 != c2) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static nxt_int_t
+nxt_http_var_response_header(nxt_task_t *task, nxt_str_t *str, void *ctx,
+ void *data)
+{
+ nxt_str_t *name;
+ nxt_http_field_t *f;
+ nxt_http_request_t *r;
+
+ r = ctx;
+ name = data;
+
+ nxt_list_each(f, r->resp.fields) {
+
+ if (f->skip) {
+ continue;
+ }
+
+ if (nxt_http_field_name_cmp(name, f) == 0) {
+ str->start = f->value;
+ str->length = f->value_length;
+
+ return NXT_OK;
+ }
+
+ } nxt_list_loop;
+
+ nxt_str_null(str);
+
+ return NXT_OK;
+}
diff --git a/src/nxt_js.c b/src/nxt_js.c
index df945db6..74663660 100644
--- a/src/nxt_js.c
+++ b/src/nxt_js.c
@@ -386,11 +386,11 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache,
nxt_js_t *js, nxt_str_t *str, void *ctx)
{
njs_vm_t *vm;
- njs_int_t rc, ret;
+ njs_int_t ret;
njs_str_t res;
- njs_value_t *array, *value;
+ njs_value_t *value;
njs_function_t *func;
- njs_opaque_value_t opaque_value, arguments[6];
+ njs_opaque_value_t retval, opaque_value, arguments[6];
static const njs_str_t uri_str = njs_str("uri");
static const njs_str_t host_str = njs_str("host");
@@ -407,15 +407,12 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache,
return NXT_ERROR;
}
- ret = njs_vm_start(vm);
+ cache->vm = vm;
+
+ ret = njs_vm_start(vm, &cache->array);
if (ret != NJS_OK) {
return NXT_ERROR;
}
-
- array = njs_vm_retval(vm);
-
- cache->vm = vm;
- cache->array = *array;
}
value = njs_vm_array_prop(vm, &cache->array, js->index, &opaque_value);
@@ -463,18 +460,20 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache,
return NXT_ERROR;
}
- ret = njs_vm_call(vm, func, njs_value_arg(&arguments), 6);
-
- rc = njs_vm_retval_string(vm, &res);
- if (rc != NJS_OK) {
- return NXT_ERROR;
- }
+ ret = njs_vm_invoke(vm, func, njs_value_arg(&arguments), 6,
+ njs_value_arg(&retval));
if (ret != NJS_OK) {
- nxt_alert(task, "js exception: %V", &res);
+ ret = njs_vm_exception_string(vm, &res);
+ if (ret == NJS_OK) {
+ nxt_alert(task, "js exception: %V", &res);
+ }
+
return NXT_ERROR;
}
+ ret = njs_vm_value_string(vm, &res, njs_value_arg(&retval));
+
str->length = res.length;
str->start = res.start;
@@ -498,7 +497,7 @@ nxt_js_error(njs_vm_t *vm, u_char *error)
njs_str_t res;
nxt_str_t err;
- ret = njs_vm_retval_string(vm, &res);
+ ret = njs_vm_exception_string(vm, &res);
if (nxt_slow_path(ret != NJS_OK)) {
return NXT_ERROR;
}
diff --git a/src/nxt_main.h b/src/nxt_main.h
index a7e0c283..aa96256e 100644
--- a/src/nxt_main.h
+++ b/src/nxt_main.h
@@ -68,6 +68,8 @@ typedef uint16_t nxt_port_id_t;
#include <nxt_sprintf.h>
#include <nxt_parse.h>
+
+typedef struct nxt_tstr_state_s nxt_tstr_state_t;
#include <nxt_var.h>
#include <nxt_tstr.h>
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index 7cba08d4..6622f67e 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -323,6 +323,60 @@ static nxt_conf_map_t nxt_java_app_conf[] = {
};
+static nxt_conf_map_t nxt_wasm_app_conf[] = {
+ {
+ nxt_string("module"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.module),
+ },
+ {
+ nxt_string("request_handler"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.request_handler),
+ },
+ {
+ nxt_string("malloc_handler"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.malloc_handler),
+ },
+ {
+ nxt_string("free_handler"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.free_handler),
+ },
+ {
+ nxt_string("module_init_handler"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.module_init_handler),
+ },
+ {
+ nxt_string("module_end_handler"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.module_end_handler),
+ },
+ {
+ nxt_string("request_init_handler"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.request_init_handler),
+ },
+ {
+ nxt_string("request_end_handler"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.request_end_handler),
+ },
+ {
+ nxt_string("response_end_handler"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_common_app_conf_t, u.wasm.response_end_handler),
+ },
+ {
+ nxt_string("access"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_common_app_conf_t, u.wasm.access),
+ },
+};
+
+
static nxt_conf_app_map_t nxt_app_maps[] = {
{ nxt_nitems(nxt_external_app_conf), nxt_external_app_conf },
{ nxt_nitems(nxt_python_app_conf), nxt_python_app_conf },
@@ -330,6 +384,7 @@ static nxt_conf_app_map_t nxt_app_maps[] = {
{ nxt_nitems(nxt_perl_app_conf), nxt_perl_app_conf },
{ nxt_nitems(nxt_ruby_app_conf), nxt_ruby_app_conf },
{ nxt_nitems(nxt_java_app_conf), nxt_java_app_conf },
+ { nxt_nitems(nxt_wasm_app_conf), nxt_wasm_app_conf },
};
diff --git a/src/nxt_router.c b/src/nxt_router.c
index d089cfb8..4e3cb303 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -274,12 +274,13 @@ static const nxt_str_t http_prefix = nxt_string("HTTP_");
static const nxt_str_t empty_prefix = nxt_string("");
static const nxt_str_t *nxt_app_msg_prefix[] = {
- &empty_prefix,
- &empty_prefix,
- &http_prefix,
- &http_prefix,
- &http_prefix,
- &empty_prefix,
+ [NXT_APP_EXTERNAL] = &empty_prefix,
+ [NXT_APP_PYTHON] = &empty_prefix,
+ [NXT_APP_PHP] = &http_prefix,
+ [NXT_APP_PERL] = &http_prefix,
+ [NXT_APP_RUBY] = &http_prefix,
+ [NXT_APP_JAVA] = &empty_prefix,
+ [NXT_APP_WASM] = &empty_prefix,
};
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index 96f801fb..9bfabc75 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -966,6 +966,13 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt)
"option \"--statedir\" requires directory\n";
static const char no_tmp[] = "option \"--tmpdir\" requires directory\n";
+ static const char modules_deprecated[] =
+ "option \"--modules\" is deprecated; use \"--modulesdir\" instead\n";
+ static const char state_deprecated[] =
+ "option \"--state\" is deprecated; use \"--statedir\" instead\n";
+ static const char tmp_deprecated[] =
+ "option \"--tmp\" is deprecated; use \"--tmpdir\" instead\n";
+
static const char help[] =
"\n"
"unit options:\n"
@@ -992,6 +999,10 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt)
" --tmpdir DIR set tmp directory name\n"
" default: \"" NXT_TMPDIR "\"\n"
"\n"
+ " --modules DIR [deprecated] synonym for --modulesdir\n"
+ " --state DIR [deprecated] synonym for --statedir\n"
+ " --tmp DIR [deprecated] synonym for --tmpdir\n"
+ "\n"
" --user USER set non-privileged processes to run"
" as specified user\n"
" default: \"" NXT_USER "\"\n"
@@ -1073,7 +1084,14 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt)
continue;
}
+ if (nxt_strcmp(p, "--modules") == 0) {
+ write(STDERR_FILENO, modules_deprecated,
+ nxt_length(modules_deprecated));
+ goto modulesdir;
+ }
+
if (nxt_strcmp(p, "--modulesdir") == 0) {
+modulesdir:
if (*argv == NULL) {
write(STDERR_FILENO, no_modules, nxt_length(no_modules));
return NXT_ERROR;
@@ -1086,7 +1104,14 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt)
continue;
}
+ if (nxt_strcmp(p, "--state") == 0) {
+ write(STDERR_FILENO, state_deprecated,
+ nxt_length(state_deprecated));
+ goto statedir;
+ }
+
if (nxt_strcmp(p, "--statedir") == 0) {
+statedir:
if (*argv == NULL) {
write(STDERR_FILENO, no_state, nxt_length(no_state));
return NXT_ERROR;
@@ -1099,7 +1124,13 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt)
continue;
}
+ if (nxt_strcmp(p, "--tmp") == 0) {
+ write(STDERR_FILENO, tmp_deprecated, nxt_length(tmp_deprecated));
+ goto tmpdir;
+ }
+
if (nxt_strcmp(p, "--tmpdir") == 0) {
+tmpdir:
if (*argv == NULL) {
write(STDERR_FILENO, no_tmp, nxt_length(no_tmp));
return NXT_ERROR;
diff --git a/src/nxt_tstr.c b/src/nxt_tstr.c
index 516415d9..edf6860a 100644
--- a/src/nxt_tstr.c
+++ b/src/nxt_tstr.c
@@ -42,8 +42,8 @@ struct nxt_tstr_query_s {
void *ctx;
void *data;
- nxt_work_handler_t ready;
- nxt_work_handler_t error;
+ nxt_work_handler_t ready;
+ nxt_work_handler_t error;
};
@@ -64,8 +64,8 @@ nxt_tstr_state_new(nxt_mp_t *mp, nxt_bool_t test)
state->pool = mp;
state->test = test;
- state->var_fields = nxt_array_create(mp, 4, sizeof(nxt_var_field_t));
- if (nxt_slow_path(state->var_fields == NULL)) {
+ state->var_refs = nxt_array_create(mp, 4, sizeof(nxt_var_ref_t));
+ if (nxt_slow_path(state->var_refs == NULL)) {
return NULL;
}
@@ -133,8 +133,7 @@ nxt_tstr_compile(nxt_tstr_state_t *state, nxt_str_t *str,
if (p != NULL) {
tstr->type = NXT_TSTR_VAR;
- tstr->u.var = nxt_var_compile(&tstr->str, state->pool,
- state->var_fields);
+ tstr->u.var = nxt_var_compile(state, &tstr->str);
if (nxt_slow_path(tstr->u.var == NULL)) {
return NULL;
}
@@ -168,7 +167,7 @@ nxt_tstr_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error)
p = memchr(str->start, '$', str->length);
if (p != NULL) {
- return nxt_var_test(str, state->var_fields, error);
+ return nxt_var_test(state, str, error);
}
}
@@ -263,8 +262,9 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
}
if (tstr->type == NXT_TSTR_VAR) {
- ret = nxt_var_interpreter(task, &query->cache->var, tstr->u.var, val,
- query->ctx, tstr->flags & NXT_TSTR_LOGGING);
+ ret = nxt_var_interpreter(task, query->state, &query->cache->var,
+ tstr->u.var, val, query->ctx,
+ tstr->flags & NXT_TSTR_LOGGING);
if (nxt_slow_path(ret != NXT_OK)) {
query->failed = 1;
diff --git a/src/nxt_tstr.h b/src/nxt_tstr.h
index afa7f56d..3e842f81 100644
--- a/src/nxt_tstr.h
+++ b/src/nxt_tstr.h
@@ -13,14 +13,14 @@ typedef struct nxt_tstr_s nxt_tstr_t;
typedef struct nxt_tstr_query_s nxt_tstr_query_t;
-typedef struct {
+struct nxt_tstr_state_s {
nxt_mp_t *pool;
- nxt_array_t *var_fields;
+ nxt_array_t *var_refs;
#if (NXT_HAVE_NJS)
nxt_js_conf_t *jcf;
#endif
uint8_t test; /* 1 bit */
-} nxt_tstr_state_t;
+};
typedef struct {
diff --git a/src/nxt_var.c b/src/nxt_var.c
index e113969f..729de788 100644
--- a/src/nxt_var.c
+++ b/src/nxt_var.c
@@ -50,14 +50,11 @@ struct nxt_var_query_s {
static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
static nxt_var_decl_t *nxt_var_hash_find(nxt_str_t *name);
-static nxt_var_decl_t *nxt_var_decl_get(nxt_str_t *name, nxt_array_t *fields,
- uint32_t *index);
-static nxt_var_field_t *nxt_var_field_add(nxt_array_t *fields, nxt_str_t *name,
- uint32_t hash);
+static nxt_var_ref_t *nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name);
static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data);
-static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_var_cache_t *cache,
- uint32_t index, void *ctx);
+static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_tstr_state_t *state,
+ nxt_var_cache_t *cache, uint32_t index, void *ctx);
static u_char *nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part);
@@ -80,7 +77,7 @@ static const nxt_lvlhsh_proto_t nxt_var_cache_proto nxt_aligned(64) = {
static nxt_lvlhsh_t nxt_var_hash;
static uint32_t nxt_var_count;
-static nxt_var_handler_t *nxt_var_index;
+static nxt_var_decl_t **nxt_vars;
static nxt_int_t
@@ -111,95 +108,70 @@ nxt_var_hash_find(nxt_str_t *name)
}
-static nxt_var_decl_t *
-nxt_var_decl_get(nxt_str_t *name, nxt_array_t *fields, uint32_t *index)
+static nxt_var_ref_t *
+nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name)
{
- u_char *p, *end;
- int64_t hash;
- uint16_t field;
- nxt_str_t str;
- nxt_var_decl_t *decl;
- nxt_var_field_t *f;
-
- f = NULL;
- field = 0;
- decl = nxt_var_hash_find(name);
+ nxt_int_t ret;
+ nxt_uint_t i;
+ nxt_var_ref_t *ref;
+ nxt_var_decl_t *decl;
- if (decl == NULL) {
- p = name->start;
- end = p + name->length;
+ ref = state->var_refs->elts;
- while (p < end) {
- if (*p++ == '_') {
- break;
- }
- }
+ for (i = 0; i < state->var_refs->nelts; i++) {
- if (p == end) {
- return NULL;
+ if (nxt_strstr_eq(ref[i].name, name)) {
+ return &ref[i];
}
+ }
- str.start = name->start;
- str.length = p - 1 - name->start;
+ ref = nxt_array_add(state->var_refs);
+ if (nxt_slow_path(ref == NULL)) {
+ return NULL;
+ }
- decl = nxt_var_hash_find(&str);
+ ref->index = state->var_refs->nelts - 1;
- if (decl != NULL) {
- str.start = p;
- str.length = end - p;
+ ref->name = nxt_str_dup(state->pool, NULL, name);
+ if (nxt_slow_path(ref->name == NULL)) {
+ return NULL;
+ }
- hash = decl->field_hash(fields->mem_pool, &str);
- if (nxt_slow_path(hash == -1)) {
- return NULL;
- }
+ decl = nxt_var_hash_find(name);
- f = nxt_var_field_add(fields, &str, (uint32_t) hash);
- if (nxt_slow_path(f == NULL)) {
- return NULL;
- }
+ if (decl != NULL) {
+ ref->handler = decl->handler;
+ ref->cacheable = decl->cacheable;
- field = f->index;
- }
+ return ref;
}
- if (decl != NULL) {
- if (decl->field_hash != NULL && f == NULL) {
- return NULL;
- }
-
- if (index != NULL) {
- *index = (decl->index << 16) | field;
- }
+ ret = nxt_http_unknown_var_ref(state, ref, name);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NULL;
}
- return decl;
+ return ref;
}
-static nxt_var_field_t *
-nxt_var_field_add(nxt_array_t *fields, nxt_str_t *name, uint32_t hash)
+nxt_var_field_t *
+nxt_var_field_new(nxt_mp_t *mp, nxt_str_t *name, uint32_t hash)
{
- nxt_uint_t i;
+ nxt_str_t *str;
nxt_var_field_t *field;
- field = fields->elts;
-
- for (i = 0; i < fields->nelts; i++) {
- if (field[i].hash == hash
- && nxt_strstr_eq(&field[i].name, name))
- {
- return field;
- }
+ field = nxt_mp_alloc(mp, sizeof(nxt_var_field_t));
+ if (nxt_slow_path(field == NULL)) {
+ return NULL;
}
- field = nxt_array_add(fields);
- if (nxt_slow_path(field == NULL)) {
+ str = nxt_str_dup(mp, &field->name, name);
+ if (nxt_slow_path(str == NULL)) {
return NULL;
}
- field->name = *name;
field->hash = hash;
- field->index = fields->nelts - 1;
return field;
}
@@ -230,13 +202,17 @@ nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data)
static nxt_str_t *
-nxt_var_cache_value(nxt_task_t *task, nxt_var_cache_t *cache, uint32_t index,
- void *ctx)
+nxt_var_cache_value(nxt_task_t *task, nxt_tstr_state_t *state,
+ nxt_var_cache_t *cache, uint32_t index, void *ctx)
{
nxt_int_t ret;
nxt_str_t *value;
+ nxt_var_ref_t *ref;
nxt_lvlhsh_query_t lhq;
+ ref = state->var_refs->elts;
+ ref = &ref[index];
+
value = cache->spare;
if (value == NULL) {
@@ -248,6 +224,10 @@ nxt_var_cache_value(nxt_task_t *task, nxt_var_cache_t *cache, uint32_t index,
cache->spare = value;
}
+ if (!ref->cacheable) {
+ goto not_cached;
+ }
+
lhq.key_hash = nxt_murmur_hash2_uint32(&index);
lhq.replace = 0;
lhq.key.length = sizeof(uint32_t);
@@ -261,16 +241,20 @@ nxt_var_cache_value(nxt_task_t *task, nxt_var_cache_t *cache, uint32_t index,
return NULL;
}
- if (ret == NXT_OK) {
- ret = nxt_var_index[index >> 16](task, value, ctx, index & 0xffff);
- if (nxt_slow_path(ret != NXT_OK)) {
- return NULL;
- }
+ if (ret == NXT_DECLINED) {
+ return lhq.value;
+ }
+
+not_cached:
- cache->spare = NULL;
+ ret = ref->handler(task, value, ctx, ref->data);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NULL;
}
- return lhq.value;
+ cache->spare = NULL;
+
+ return value;
}
@@ -303,12 +287,11 @@ nxt_int_t
nxt_var_index_init(void)
{
nxt_uint_t i;
- nxt_var_decl_t *decl;
- nxt_var_handler_t *index;
+ nxt_var_decl_t *decl, **vars;
nxt_lvlhsh_each_t lhe;
- index = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_handler_t));
- if (index == NULL) {
+ vars = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_decl_t *));
+ if (vars == NULL) {
return NXT_ERROR;
}
@@ -316,27 +299,25 @@ nxt_var_index_init(void)
for (i = 0; i < nxt_var_count; i++) {
decl = nxt_lvlhsh_each(&nxt_var_hash, &lhe);
- decl->index = i;
- index[i] = decl->handler;
+ vars[i] = decl;
}
- nxt_var_index = index;
+ nxt_vars = vars;
return NXT_OK;
}
nxt_var_t *
-nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields)
+nxt_var_compile(nxt_tstr_state_t *state, nxt_str_t *str)
{
- u_char *p, *end, *next, *src;
- size_t size;
- uint32_t index;
- nxt_var_t *var;
- nxt_str_t part;
- nxt_uint_t n;
- nxt_var_sub_t *subs;
- nxt_var_decl_t *decl;
+ u_char *p, *end, *next, *src;
+ size_t size;
+ nxt_var_t *var;
+ nxt_str_t part;
+ nxt_uint_t n;
+ nxt_var_sub_t *subs;
+ nxt_var_ref_t *ref;
n = 0;
@@ -356,7 +337,7 @@ nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields)
size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length;
- var = nxt_mp_get(mp, size);
+ var = nxt_mp_get(state->pool, size);
if (nxt_slow_path(var == NULL)) {
return NULL;
}
@@ -376,12 +357,12 @@ nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields)
next = nxt_var_next_part(p, end, &part);
if (part.start != NULL) {
- decl = nxt_var_decl_get(&part, fields, &index);
- if (nxt_slow_path(decl == NULL)) {
+ ref = nxt_var_ref_get(state, &part);
+ if (nxt_slow_path(ref == NULL)) {
return NULL;
}
- subs[n].index = index;
+ subs[n].index = ref->index;
subs[n].length = next - p;
subs[n].position = p - str->start;
@@ -396,11 +377,11 @@ nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields)
nxt_int_t
-nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error)
+nxt_var_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error)
{
- u_char *p, *end, *next;
- nxt_str_t part;
- nxt_var_decl_t *decl;
+ u_char *p, *end, *next;
+ nxt_str_t part;
+ nxt_var_ref_t *ref;
p = str->start;
end = p + str->length;
@@ -416,9 +397,9 @@ nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error)
}
if (part.start != NULL) {
- decl = nxt_var_decl_get(&part, fields, NULL);
+ ref = nxt_var_ref_get(state, &part);
- if (decl == NULL) {
+ if (ref == NULL) {
nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
"Unknown variable \"%V\"%Z", &part);
@@ -504,8 +485,9 @@ nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part)
nxt_int_t
-nxt_var_interpreter(nxt_task_t *task, nxt_var_cache_t *cache, nxt_var_t *var,
- nxt_str_t *str, void *ctx, nxt_bool_t logging)
+nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state,
+ nxt_var_cache_t *cache, nxt_var_t *var, nxt_str_t *str, void *ctx,
+ nxt_bool_t logging)
{
u_char *p, *src;
size_t length, last, next;
@@ -522,7 +504,7 @@ nxt_var_interpreter(nxt_task_t *task, nxt_var_cache_t *cache, nxt_var_t *var,
length = var->length;
for (i = 0; i < var->vars; i++) {
- value = nxt_var_cache_value(task, cache, subs[i].index, ctx);
+ value = nxt_var_cache_value(task, state, cache, subs[i].index, ctx);
if (nxt_slow_path(value == NULL)) {
return NXT_ERROR;
}
diff --git a/src/nxt_var.h b/src/nxt_var.h
index ab25800d..fde64f1e 100644
--- a/src/nxt_var.h
+++ b/src/nxt_var.h
@@ -13,22 +13,29 @@ typedef struct nxt_var_query_s nxt_var_query_t;
typedef nxt_int_t (*nxt_var_handler_t)(nxt_task_t *task,
nxt_str_t *str,
- void *ctx, uint16_t field);
+ void *ctx, void *data);
typedef int64_t (*nxt_var_field_hash_t)(nxt_mp_t *mp, nxt_str_t *str);
typedef struct {
nxt_str_t name;
nxt_var_handler_t handler;
- nxt_var_field_hash_t field_hash;
- uint32_t index;
+ uint8_t cacheable; /* 1 bit */
} nxt_var_decl_t;
typedef struct {
+ nxt_str_t *name;
+ nxt_var_handler_t handler;
+ void *data;
+ uint32_t index;
+ uint8_t cacheable; /* 1 bit */
+} nxt_var_ref_t;
+
+
+typedef struct {
nxt_str_t name;
uint16_t hash;
- uint32_t index;
} nxt_var_field_t;
@@ -43,14 +50,20 @@ nxt_int_t nxt_var_register(nxt_var_decl_t *decl, size_t n);
nxt_int_t nxt_var_index_init(void);
nxt_var_field_t *nxt_var_field_get(nxt_array_t *fields, uint16_t index);
+nxt_var_field_t *nxt_var_field_new(nxt_mp_t *mp, nxt_str_t *name,
+ uint32_t hash);
-nxt_var_t *nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields);
-nxt_int_t nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error);
+nxt_var_t *nxt_var_compile(nxt_tstr_state_t *state, nxt_str_t *str);
+nxt_int_t nxt_var_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error);
-nxt_int_t nxt_var_interpreter(nxt_task_t *task, nxt_var_cache_t *cache,
- nxt_var_t *var, nxt_str_t *str, void *ctx, nxt_bool_t logging);
+nxt_int_t nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state,
+ nxt_var_cache_t *cache, nxt_var_t *var, nxt_str_t *str, void *ctx,
+ nxt_bool_t logging);
nxt_str_t *nxt_var_get(nxt_task_t *task, nxt_var_cache_t *cache,
nxt_str_t *name, void *ctx);
+nxt_int_t nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref,
+ nxt_str_t *name);
+
#endif /* _NXT_VAR_H_INCLUDED_ */
diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c
index adf03e2b..8f300b53 100644
--- a/src/python/nxt_python_asgi.c
+++ b/src/python/nxt_python_asgi.c
@@ -450,6 +450,7 @@ static void
nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
{
PyObject *scope, *res, *task, *receive, *send, *done, *asgi;
+ PyObject *state, *newstate, *lifespan;
PyObject *stage2;
nxt_python_target_t *target;
nxt_py_asgi_ctx_data_t *ctx_data;
@@ -477,7 +478,7 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
}
send = PyObject_GetAttrString(asgi, "send");
- if (nxt_slow_path(receive == NULL)) {
+ if (nxt_slow_path(send == NULL)) {
nxt_unit_req_alert(req, "Python failed to get 'send' method");
nxt_unit_request_done(req, NXT_UNIT_ERROR);
@@ -485,7 +486,7 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
}
done = PyObject_GetAttrString(asgi, "_done");
- if (nxt_slow_path(receive == NULL)) {
+ if (nxt_slow_path(done == NULL)) {
nxt_unit_req_alert(req, "Python failed to get '_done' method");
nxt_unit_request_done(req, NXT_UNIT_ERROR);
@@ -493,15 +494,41 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
}
req->data = asgi;
+ ctx_data = req->ctx->data;
target = &nxt_py_targets->target[req->request->app_target];
+ lifespan = ctx_data->target_lifespans[req->request->app_target];
+ state = PyObject_GetAttr(lifespan, nxt_py_state_str);
+ if (nxt_slow_path(state == NULL)) {
+ nxt_unit_req_alert(req, "Python failed to get 'state' attribute");
+ nxt_unit_request_done(req, NXT_UNIT_ERROR);
+
+ goto release_done;
+ }
+
+ newstate = PyDict_Copy(state);
+ if (nxt_slow_path(newstate == NULL)) {
+ nxt_unit_req_alert(req, "Python failed to call state.copy()");
+ nxt_unit_request_done(req, NXT_UNIT_ERROR);
+ Py_DECREF(state);
+ goto release_done;
+ }
+ Py_DECREF(state);
scope = nxt_py_asgi_create_http_scope(req, target);
if (nxt_slow_path(scope == NULL)) {
nxt_unit_request_done(req, NXT_UNIT_ERROR);
-
+ Py_DECREF(newstate);
goto release_done;
}
+ if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_state_str, newstate)
+ == -1))
+ {
+ Py_DECREF(newstate);
+ goto release_scope;
+ }
+ Py_DECREF(newstate);
+
if (!target->asgi_legacy) {
nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
@@ -555,7 +582,6 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
goto release_scope;
}
- ctx_data = req->ctx->data;
task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL);
if (nxt_slow_path(task == NULL)) {
@@ -828,7 +854,7 @@ nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
static PyObject *
nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
{
- char *p, *s;
+ char *p;
PyObject *pair, *v;
pair = PyTuple_New(2);
@@ -837,9 +863,8 @@ nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
}
p = nxt_unit_sptr_get(sptr);
- s = memchr(p, ':', len);
- v = PyString_FromStringAndSize(p, s == NULL ? len : s - p);
+ v = PyString_FromStringAndSize(p, len);
if (nxt_slow_path(v == NULL)) {
Py_DECREF(pair);
@@ -848,14 +873,7 @@ nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
PyTuple_SET_ITEM(pair, 0, v);
- if (s != NULL) {
- p += len;
- v = PyLong_FromString(s + 1, &p, 10);
-
- } else {
- v = PyLong_FromLong(port);
- }
-
+ v = PyLong_FromLong(port);
if (nxt_slow_path(v == NULL)) {
Py_DECREF(pair);
diff --git a/src/python/nxt_python_asgi_lifespan.c b/src/python/nxt_python_asgi_lifespan.c
index 1fc0e6b7..041cca21 100644
--- a/src/python/nxt_python_asgi_lifespan.c
+++ b/src/python/nxt_python_asgi_lifespan.c
@@ -12,6 +12,8 @@
#include <python/nxt_python_asgi.h>
#include <python/nxt_python_asgi_str.h>
+#include <structmember.h>
+
typedef struct {
PyObject_HEAD
@@ -25,6 +27,7 @@ typedef struct {
PyObject *startup_future;
PyObject *shutdown_future;
PyObject *receive_future;
+ PyObject *state;
} nxt_py_asgi_lifespan_t;
static PyObject *nxt_py_asgi_lifespan_target_startup(
@@ -41,6 +44,7 @@ static PyObject *nxt_py_asgi_lifespan_send_shutdown(
nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan);
static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future);
+static void nxt_py_asgi_lifespan_dealloc(PyObject *self);
static PyMethodDef nxt_py_asgi_lifespan_methods[] = {
@@ -50,6 +54,26 @@ static PyMethodDef nxt_py_asgi_lifespan_methods[] = {
{ NULL, NULL, 0, 0 }
};
+static PyMemberDef nxt_py_asgi_lifespan_members[] = {
+ {
+#if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 7)
+ .name = "state",
+#else
+ .name = (char *)"state",
+#endif
+ .type = T_OBJECT_EX,
+ .offset = offsetof(nxt_py_asgi_lifespan_t, state),
+ .flags = READONLY,
+#if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 7)
+ .doc = PyDoc_STR("lifespan.state")
+#else
+ .doc = (char *)PyDoc_STR("lifespan.state")
+#endif
+ },
+
+ { NULL, 0, 0, 0, NULL }
+};
+
static PyAsyncMethods nxt_py_asgi_async_methods = {
.am_await = nxt_py_asgi_await,
};
@@ -59,13 +83,14 @@ static PyTypeObject nxt_py_asgi_lifespan_type = {
.tp_name = "unit._asgi_lifespan",
.tp_basicsize = sizeof(nxt_py_asgi_lifespan_t),
- .tp_dealloc = nxt_py_asgi_dealloc,
+ .tp_dealloc = nxt_py_asgi_lifespan_dealloc,
.tp_as_async = &nxt_py_asgi_async_methods,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "unit ASGI Lifespan object",
.tp_iter = nxt_py_asgi_iter,
.tp_iternext = nxt_py_asgi_next,
.tp_methods = nxt_py_asgi_lifespan_methods,
+ .tp_members = nxt_py_asgi_lifespan_members,
};
@@ -163,12 +188,29 @@ nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t *ctx_data,
lifespan->shutdown_called = 0;
lifespan->shutdown_future = NULL;
lifespan->receive_future = NULL;
+ lifespan->state = NULL;
scope = nxt_py_asgi_new_scope(NULL, nxt_py_lifespan_str, nxt_py_2_0_str);
if (nxt_slow_path(scope == NULL)) {
goto release_future;
}
+ lifespan->state = PyDict_New();
+ if (nxt_slow_path(lifespan->state == NULL)) {
+ nxt_unit_req_error(NULL,
+ "Python failed to create 'state' dict");
+ goto release_future;
+ }
+
+ if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_state_str,
+ lifespan->state) == -1))
+ {
+ nxt_unit_req_error(NULL,
+ "Python failed to set 'scope.state' item");
+ Py_CLEAR(lifespan->state);
+ goto release_future;
+ }
+
if (!target->asgi_legacy) {
nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application");
@@ -604,4 +646,14 @@ nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future)
}
+static void
+nxt_py_asgi_lifespan_dealloc(PyObject *self)
+{
+ nxt_py_asgi_lifespan_t *lifespan = (nxt_py_asgi_lifespan_t *)self;
+
+ Py_CLEAR(lifespan->state);
+ PyObject_Del(self);
+}
+
+
#endif /* NXT_HAVE_ASGI */
diff --git a/src/python/nxt_python_asgi_str.c b/src/python/nxt_python_asgi_str.c
index 7171d52b..3bea87d5 100644
--- a/src/python/nxt_python_asgi_str.c
+++ b/src/python/nxt_python_asgi_str.c
@@ -55,6 +55,7 @@ PyObject *nxt_py_subprotocol_str;
PyObject *nxt_py_subprotocols_str;
PyObject *nxt_py_text_str;
PyObject *nxt_py_type_str;
+PyObject *nxt_py_state_str;
PyObject *nxt_py_version_str;
PyObject *nxt_py_websocket_str;
PyObject *nxt_py_websocket_accept_str;
@@ -110,6 +111,7 @@ static nxt_python_string_t nxt_py_asgi_strings[] = {
{ nxt_string("subprotocols"), &nxt_py_subprotocols_str },
{ nxt_string("text"), &nxt_py_text_str },
{ nxt_string("type"), &nxt_py_type_str },
+ { nxt_string("state"), &nxt_py_state_str },
{ nxt_string("version"), &nxt_py_version_str },
{ nxt_string("websocket"), &nxt_py_websocket_str },
{ nxt_string("websocket.accept"), &nxt_py_websocket_accept_str },
diff --git a/src/python/nxt_python_asgi_str.h b/src/python/nxt_python_asgi_str.h
index 92969fd2..3c7a3ed9 100644
--- a/src/python/nxt_python_asgi_str.h
+++ b/src/python/nxt_python_asgi_str.h
@@ -50,6 +50,7 @@ extern PyObject *nxt_py_subprotocol_str;
extern PyObject *nxt_py_subprotocols_str;
extern PyObject *nxt_py_text_str;
extern PyObject *nxt_py_type_str;
+extern PyObject *nxt_py_state_str;
extern PyObject *nxt_py_version_str;
extern PyObject *nxt_py_websocket_str;
extern PyObject *nxt_py_websocket_accept_str;
diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c
index d83bd83a..5dcebe18 100644
--- a/src/test/nxt_unit_app_test.c
+++ b/src/test/nxt_unit_app_test.c
@@ -257,8 +257,8 @@ greeting_app_request_handler(nxt_unit_request_info_t *req)
if (r->content_length > 0) {
p = copy(p, BODY, nxt_length(BODY));
- res = nxt_unit_request_read(req, buf->free, buf->end - buf->free);
- buf->free += res;
+ res = nxt_unit_request_read(req, p, buf->end - p);
+ p += res;
}
diff --git a/src/unit.pc.in b/src/unit.pc.in
new file mode 100644
index 00000000..4de0556f
--- /dev/null
+++ b/src/unit.pc.in
@@ -0,0 +1,11 @@
+prefix=@PREFIX@
+libdir=@LIBDIR@
+confargs=@CONFARGS@
+modulesdir=@MODULESDIR@
+
+Name: unit
+Description: library to embed Unit
+Version: @VERSION@
+URL: https://unit.nginx.org
+Cflags: @CFLAGS@
+Libs: -L${libdir} -lunit @EXTRA_LIBS@
diff --git a/src/wasm/nxt_rt_wasmtime.c b/src/wasm/nxt_rt_wasmtime.c
new file mode 100644
index 00000000..99786b89
--- /dev/null
+++ b/src/wasm/nxt_rt_wasmtime.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include <wasm.h>
+#include <wasi.h>
+#include <wasmtime.h>
+
+#include "nxt_wasm.h"
+
+
+typedef struct nxt_wasmtime_ctx_s nxt_wasmtime_ctx_t;
+
+struct nxt_wasmtime_ctx_s {
+ wasm_engine_t *engine;
+ wasmtime_store_t *store;
+ wasmtime_memory_t memory;
+ wasmtime_module_t *module;
+ wasmtime_linker_t *linker;
+ wasmtime_context_t *ctx;
+};
+
+static nxt_wasmtime_ctx_t nxt_wasmtime_ctx;
+
+
+static void
+nxt_wasmtime_err_msg(wasmtime_error_t *error, wasm_trap_t *trap,
+ const char *fmt, ...)
+{
+ va_list args;
+ wasm_byte_vec_t error_message;
+
+ fprintf(stderr, "WASMTIME ERROR: ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+
+ if (error == NULL && trap == NULL) {
+ return;
+ }
+
+ if (error != NULL) {
+ wasmtime_error_message(error, &error_message);
+ wasmtime_error_delete(error);
+ } else {
+ wasm_trap_message(trap, &error_message);
+ wasm_trap_delete(trap);
+ }
+ fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data);
+
+ wasm_byte_vec_delete(&error_message);
+}
+
+
+static wasm_trap_t *
+nxt_wasm_get_init_mem_size(void *env, wasmtime_caller_t *caller,
+ const wasmtime_val_t *args, size_t nargs,
+ wasmtime_val_t *results, size_t nresults)
+{
+ results[0].of.i32 = NXT_WASM_MEM_SIZE;
+
+ return NULL;
+}
+
+
+static wasm_trap_t *
+nxt_wasm_response_end(void *env, wasmtime_caller_t *caller,
+ const wasmtime_val_t *args, size_t nargs,
+ wasmtime_val_t *results, size_t nresults)
+{
+ nxt_wasm_do_response_end(env);
+
+ return NULL;
+}
+
+
+static wasm_trap_t *
+nxt_wasm_send_response(void *env, wasmtime_caller_t *caller,
+ const wasmtime_val_t *args, size_t nargs,
+ wasmtime_val_t *results, size_t nresults)
+{
+ nxt_wasm_do_send_response(env, args[0].of.i32);
+
+ return NULL;
+}
+
+
+static wasm_trap_t *
+nxt_wasm_send_headers(void *env, wasmtime_caller_t *caller,
+ const wasmtime_val_t *args, size_t nargs,
+ wasmtime_val_t *results, size_t nresults)
+{
+ nxt_wasm_do_send_headers(env, args[0].of.i32);
+
+ return NULL;
+}
+
+
+static void
+nxt_wasmtime_execute_hook(const nxt_wasm_ctx_t *ctx, nxt_wasm_fh_t hook)
+{
+ const char *name = ctx->fh[hook].func_name;
+ wasm_trap_t *trap = NULL;
+ wasmtime_error_t *error;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+ const nxt_wasm_func_t *func = &ctx->fh[hook].func;
+
+ if (name == NULL) {
+ return;
+ }
+
+ error = wasmtime_func_call(rt_ctx->ctx, func, NULL, 0, NULL, 0, &trap);
+ if (error != NULL || trap != NULL) {
+ nxt_wasmtime_err_msg(error, trap, "failed to call hook function [%s]",
+ name);
+ }
+}
+
+
+static void
+nxt_wasmtime_execute_request(const nxt_wasm_ctx_t *ctx)
+{
+ int i = 0;
+ wasm_trap_t *trap = NULL;
+ wasmtime_val_t args[1] = { };
+ wasmtime_val_t results[1] = { };
+ wasmtime_error_t *error;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+ const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_REQUEST].func;
+
+ args[i].kind = WASMTIME_I32;
+ args[i++].of.i32 = ctx->baddr_off;
+
+ error = wasmtime_func_call(rt_ctx->ctx, func, args, i, results, 1, &trap);
+ if (error != NULL || trap != NULL) {
+ nxt_wasmtime_err_msg(error, trap,
+ "failed to call function [->wasm_request_handler]"
+ );
+ }
+}
+
+
+static void
+nxt_wasmtime_set_function_imports(nxt_wasm_ctx_t *ctx)
+{
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+
+ static const struct {
+ const char *func_name;
+
+ wasmtime_func_callback_t func;
+ wasm_valkind_t params[1];
+ wasm_valkind_t results[1];
+
+ enum {
+ NXT_WASM_FT_0_0,
+ NXT_WASM_FT_1_0,
+ NXT_WASM_FT_0_1,
+ } ft;
+ } import_functions[] = {
+ {
+ .func_name = "nxt_wasm_get_init_mem_size",
+ .func = nxt_wasm_get_init_mem_size,
+ .results = { WASM_I32 },
+ .ft = NXT_WASM_FT_0_1
+ }, {
+ .func_name = "nxt_wasm_response_end",
+ .func = nxt_wasm_response_end,
+ .ft = NXT_WASM_FT_0_0
+ }, {
+ .func_name = "nxt_wasm_send_response",
+ .func = nxt_wasm_send_response,
+ .params = { WASM_I32 },
+ .ft = NXT_WASM_FT_1_0
+ }, {
+ .func_name = "nxt_wasm_send_headers",
+ .func = nxt_wasm_send_headers,
+ .params = { WASM_I32 },
+ .ft = NXT_WASM_FT_1_0
+ },
+
+ { }
+ }, *imf;
+
+ for (imf = import_functions; imf->func_name != NULL; imf++) {
+ wasm_functype_t *func_ty;
+
+ switch (imf->ft) {
+ case NXT_WASM_FT_0_0:
+ func_ty = wasm_functype_new_0_0();
+ break;
+ case NXT_WASM_FT_1_0:
+ func_ty = wasm_functype_new_1_0(wasm_valtype_new(imf->params[0]));
+ break;
+ case NXT_WASM_FT_0_1:
+ func_ty = wasm_functype_new_0_1(wasm_valtype_new(imf->results[0]));
+ break;
+ default:
+ /* Stop GCC complaining about func_ty being used uninitialised */
+ func_ty = NULL;
+ }
+
+ wasmtime_linker_define_func(rt_ctx->linker, "env", 3,
+ imf->func_name, strlen(imf->func_name),
+ func_ty, imf->func, ctx, NULL);
+ wasm_functype_delete(func_ty);
+ }
+}
+
+
+static int
+nxt_wasmtime_get_function_exports(nxt_wasm_ctx_t *ctx)
+{
+ int i;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+
+ for (i = 0; i < NXT_WASM_FH_NR; i++) {
+ bool ok;
+ wasmtime_extern_t item;
+
+ if (ctx->fh[i].func_name == NULL) {
+ continue;
+ }
+
+ ok = wasmtime_linker_get(rt_ctx->linker, rt_ctx->ctx, "", 0,
+ ctx->fh[i].func_name,
+ strlen(ctx->fh[i].func_name), &item);
+ if (!ok) {
+ nxt_wasmtime_err_msg(NULL, NULL,
+ "couldn't get (%s) export from module",
+ ctx->fh[i].func_name);
+ return -1;
+ }
+ ctx->fh[i].func = item.of.func;
+ }
+
+ return 0;
+}
+
+
+static int
+nxt_wasmtime_wasi_init(const nxt_wasm_ctx_t *ctx)
+{
+ char **dir;
+ wasi_config_t *wasi_config;
+ wasmtime_error_t *error;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+
+ wasi_config = wasi_config_new();
+
+ wasi_config_inherit_env(wasi_config);
+ wasi_config_inherit_stdin(wasi_config);
+ wasi_config_inherit_stdout(wasi_config);
+ wasi_config_inherit_stderr(wasi_config);
+
+ for (dir = ctx->dirs; dir != NULL && *dir != NULL; dir++) {
+ wasi_config_preopen_dir(wasi_config, *dir, *dir);
+ }
+
+ error = wasmtime_context_set_wasi(rt_ctx->ctx, wasi_config);
+ if (error != NULL) {
+ nxt_wasmtime_err_msg(error, NULL, "failed to instantiate WASI");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+nxt_wasmtime_init_memory(nxt_wasm_ctx_t *ctx)
+{
+ int i = 0;
+ bool ok;
+ wasm_trap_t *trap = NULL;
+ wasmtime_val_t args[1] = { };
+ wasmtime_val_t results[1] = { };
+ wasmtime_error_t *error;
+ wasmtime_extern_t item;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+ const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_MALLOC].func;
+
+ args[i].kind = WASMTIME_I32;
+ args[i++].of.i32 = NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE;
+
+ error = wasmtime_func_call(rt_ctx->ctx, func, args, i, results, 1, &trap);
+ if (error != NULL || trap != NULL) {
+ nxt_wasmtime_err_msg(error, trap,
+ "failed to call function [->wasm_malloc_handler]"
+ );
+ return -1;
+ }
+
+ ok = wasmtime_linker_get(rt_ctx->linker, rt_ctx->ctx, "", 0, "memory",
+ strlen("memory"), &item);
+ if (!ok) {
+ nxt_wasmtime_err_msg(NULL, NULL, "couldn't get 'memory' from module\n");
+ return -1;
+ }
+ rt_ctx->memory = item.of.memory;
+
+ ctx->baddr_off = results[0].of.i32;
+ ctx->baddr = wasmtime_memory_data(rt_ctx->ctx, &rt_ctx->memory);
+
+ ctx->baddr += ctx->baddr_off;
+
+ return 0;
+}
+
+
+static int
+nxt_wasmtime_init(nxt_wasm_ctx_t *ctx)
+{
+ int err;
+ FILE *fp;
+ size_t file_size;
+ wasm_byte_vec_t wasm;
+ wasmtime_error_t *error;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+
+ rt_ctx->engine = wasm_engine_new();
+ rt_ctx->store = wasmtime_store_new(rt_ctx->engine, NULL, NULL);
+ rt_ctx->ctx = wasmtime_store_context(rt_ctx->store);
+
+ rt_ctx->linker = wasmtime_linker_new(rt_ctx->engine);
+ error = wasmtime_linker_define_wasi(rt_ctx->linker);
+ if (error != NULL) {
+ nxt_wasmtime_err_msg(error, NULL, "failed to link wasi");
+ return -1;
+ }
+
+ fp = fopen(ctx->module_path, "r");
+ if (!fp) {
+ nxt_wasmtime_err_msg(NULL, NULL,
+ "error opening file (%s)", ctx->module_path);
+ return -1;
+ }
+ fseek(fp, 0L, SEEK_END);
+ file_size = ftell(fp);
+ wasm_byte_vec_new_uninitialized(&wasm, file_size);
+ fseek(fp, 0L, SEEK_SET);
+ if (fread(wasm.data, file_size, 1, fp) != 1) {
+ nxt_wasmtime_err_msg(NULL, NULL, "error loading module");
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+
+ error = wasmtime_module_new(rt_ctx->engine, (uint8_t *)wasm.data, wasm.size,
+ &rt_ctx->module);
+ if (!rt_ctx->module) {
+ nxt_wasmtime_err_msg(error, NULL, "failed to compile module");
+ return -1;
+ }
+ wasm_byte_vec_delete(&wasm);
+
+ nxt_wasmtime_set_function_imports(ctx);
+
+ nxt_wasmtime_wasi_init(ctx);
+
+ error = wasmtime_linker_module(rt_ctx->linker, rt_ctx->ctx, "", 0,
+ rt_ctx->module);
+ if (error != NULL) {
+ nxt_wasmtime_err_msg(error, NULL, "failed to instantiate");
+ return -1;
+ }
+
+ err = nxt_wasmtime_get_function_exports(ctx);
+ if (err) {
+ return -1;
+ }
+
+ err = nxt_wasmtime_init_memory(ctx);
+ if (err) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+nxt_wasmtime_destroy(const nxt_wasm_ctx_t *ctx)
+{
+ int i = 0;
+ wasmtime_val_t args[1] = { };
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+ const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_FREE].func;
+
+ args[i].kind = WASMTIME_I32;
+ args[i++].of.i32 = ctx->baddr_off;
+
+ wasmtime_func_call(rt_ctx->ctx, func, args, i, NULL, 0, NULL);
+
+ wasmtime_module_delete(rt_ctx->module);
+ wasmtime_store_delete(rt_ctx->store);
+ wasm_engine_delete(rt_ctx->engine);
+}
+
+
+const nxt_wasm_operations_t nxt_wasm_ops = {
+ .init = nxt_wasmtime_init,
+ .destroy = nxt_wasmtime_destroy,
+ .exec_request = nxt_wasmtime_execute_request,
+ .exec_hook = nxt_wasmtime_execute_hook,
+};
diff --git a/src/wasm/nxt_wasm.c b/src/wasm/nxt_wasm.c
new file mode 100644
index 00000000..45a40b4b
--- /dev/null
+++ b/src/wasm/nxt_wasm.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#include <nxt_main.h>
+#include <nxt_application.h>
+#include <nxt_unit.h>
+#include <nxt_unit_request.h>
+
+#include "nxt_wasm.h"
+
+
+#define NXT_WASM_VERSION "0.1"
+
+#define NXT_WASM_DO_HOOK(hook) nxt_wops->exec_hook(&nxt_wasm_ctx, hook);
+
+
+static uint32_t compat[] = {
+ NXT_VERNUM, NXT_DEBUG,
+};
+
+static nxt_wasm_ctx_t nxt_wasm_ctx;
+
+static const nxt_wasm_operations_t *nxt_wops;
+
+
+void
+nxt_wasm_do_response_end(nxt_wasm_ctx_t *ctx)
+{
+ nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
+
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_RESPONSE_END);
+}
+
+
+void
+nxt_wasm_do_send_headers(nxt_wasm_ctx_t *ctx, uint32_t offset)
+{
+ size_t fields_len;
+ unsigned int i;
+ nxt_wasm_response_fields_t *rh;
+
+ rh = (nxt_wasm_response_fields_t *)(ctx->baddr + offset);
+
+ fields_len = 0;
+ for (i = 0; i < rh->nfields; i++) {
+ fields_len += rh->fields[i].name_len + rh->fields[i].value_len;
+ }
+
+ nxt_unit_response_init(ctx->req, 200, rh->nfields, fields_len);
+
+ for (i = 0; i < rh->nfields; i++) {
+ const char *name;
+ const char *val;
+
+ name = (const char *)rh + rh->fields[i].name_off;
+ val = (const char *)rh + rh->fields[i].value_off;
+
+ nxt_unit_response_add_field(ctx->req, name, rh->fields[i].name_len,
+ val, rh->fields[i].value_len);
+ }
+
+ nxt_unit_response_send(ctx->req);
+}
+
+
+void
+nxt_wasm_do_send_response(nxt_wasm_ctx_t *ctx, uint32_t offset)
+{
+ nxt_wasm_response_t *resp;
+ nxt_unit_request_info_t *req = ctx->req;
+
+ if (!nxt_unit_response_is_init(req)) {
+ nxt_unit_response_init(req, 200, 0, 0);
+ }
+
+ resp = (nxt_wasm_response_t *)(nxt_wasm_ctx.baddr + offset);
+
+ nxt_unit_response_write(req, (const char *)resp->data, resp->size);
+}
+
+
+static void
+nxt_wasm_request_handler(nxt_unit_request_info_t *req)
+{
+ size_t offset, read_bytes, content_sent, content_len;
+ ssize_t bytes_read;
+ nxt_unit_field_t *sf, *sf_end;
+ nxt_unit_request_t *r;
+ nxt_wasm_request_t *wr;
+ nxt_wasm_http_field_t *df;
+
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_REQUEST_INIT);
+
+ wr = (nxt_wasm_request_t *)nxt_wasm_ctx.baddr;
+
+#define SET_REQ_MEMBER(dmember, smember) \
+ do { \
+ const char *str = nxt_unit_sptr_get(&r->smember); \
+ wr->dmember##_off = offset; \
+ wr->dmember##_len = strlen(str); \
+ memcpy((uint8_t *)wr + offset, str, wr->dmember##_len + 1); \
+ offset += wr->dmember##_len + 1; \
+ } while (0)
+
+ r = req->request;
+ offset = sizeof(nxt_wasm_request_t)
+ + (r->fields_count * sizeof(nxt_wasm_http_field_t));
+
+ SET_REQ_MEMBER(path, path);
+ SET_REQ_MEMBER(method, method);
+ SET_REQ_MEMBER(version, version);
+ SET_REQ_MEMBER(query, query);
+ SET_REQ_MEMBER(remote, remote);
+ SET_REQ_MEMBER(local_addr, local_addr);
+ SET_REQ_MEMBER(local_port, local_port);
+ SET_REQ_MEMBER(server_name, server_name);
+#undef SET_REQ_MEMBER
+
+ df = wr->fields;
+ sf_end = r->fields + r->fields_count;
+ for (sf = r->fields; sf < sf_end; sf++) {
+ const char *name = nxt_unit_sptr_get(&sf->name);
+ const char *value = nxt_unit_sptr_get(&sf->value);
+
+ df->name_off = offset;
+ df->name_len = strlen(name);
+ memcpy((uint8_t *)wr + offset, name, df->name_len + 1);
+ offset += df->name_len + 1;
+
+ df->value_off = offset;
+ df->value_len = strlen(value);
+ memcpy((uint8_t *)wr + offset, value, df->value_len + 1);
+ offset += df->value_len + 1;
+
+ df++;
+ }
+
+ wr->tls = r->tls;
+ wr->nfields = r->fields_count;
+ wr->content_off = offset;
+ wr->content_len = content_len = r->content_length;
+
+ read_bytes = nxt_min(wr->content_len, NXT_WASM_MEM_SIZE - offset);
+
+ bytes_read = nxt_unit_request_read(req, (uint8_t *)wr + offset, read_bytes);
+ wr->content_sent = wr->total_content_sent = content_sent = bytes_read;
+
+ wr->request_size = offset + bytes_read;
+
+ nxt_wasm_ctx.req = req;
+ nxt_wops->exec_request(&nxt_wasm_ctx);
+
+ if (content_len == content_sent) {
+ goto request_done;
+ }
+
+ wr->nfields = 0;
+ wr->content_off = offset = sizeof(nxt_wasm_request_t);
+ do {
+ read_bytes = nxt_min(content_len - content_sent,
+ NXT_WASM_MEM_SIZE - offset);
+ bytes_read = nxt_unit_request_read(req, (uint8_t *)wr + offset,
+ read_bytes);
+
+ content_sent += bytes_read;
+ wr->request_size = wr->content_sent = bytes_read;
+ wr->total_content_sent = content_sent;
+
+ nxt_wops->exec_request(&nxt_wasm_ctx);
+ } while (content_sent < content_len);
+
+request_done:
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_REQUEST_END);
+}
+
+
+static nxt_int_t
+nxt_wasm_start(nxt_task_t *task, nxt_process_data_t *data)
+{
+ nxt_int_t ret;
+ nxt_unit_ctx_t *unit_ctx;
+ nxt_unit_init_t wasm_init;
+ nxt_common_app_conf_t *conf;
+
+ conf = data->app;
+
+ ret = nxt_unit_default_init(task, &wasm_init, conf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "nxt_unit_default_init() failed");
+ return ret;
+ }
+
+ wasm_init.callbacks.request_handler = nxt_wasm_request_handler;
+
+ unit_ctx = nxt_unit_init(&wasm_init);
+ if (nxt_slow_path(unit_ctx == NULL)) {
+ return NXT_ERROR;
+ }
+
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_MODULE_INIT);
+ nxt_unit_run(unit_ctx);
+ nxt_unit_done(unit_ctx);
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_MODULE_END);
+
+ if (nxt_wasm_ctx.dirs != NULL) {
+ char **p;
+
+ for (p = nxt_wasm_ctx.dirs; *p != NULL; p++) {
+ nxt_free(*p);
+ }
+ nxt_free(nxt_wasm_ctx.dirs);
+ }
+
+ nxt_wops->destroy(&nxt_wasm_ctx);
+
+ exit(EXIT_SUCCESS);
+}
+
+
+static nxt_int_t
+nxt_wasm_setup(nxt_task_t *task, nxt_process_t *process,
+ nxt_common_app_conf_t *conf)
+{
+ int n, i, err;
+ nxt_conf_value_t *dirs = NULL;
+ nxt_wasm_app_conf_t *c;
+ nxt_wasm_func_handler_t *fh;
+ static nxt_str_t filesystem_str = nxt_string("filesystem");
+
+ c = &conf->u.wasm;
+
+ nxt_wops = &nxt_wasm_ops;
+
+ nxt_wasm_ctx.module_path = c->module;
+
+ fh = nxt_wasm_ctx.fh;
+
+ fh[NXT_WASM_FH_REQUEST].func_name = c->request_handler;
+ fh[NXT_WASM_FH_MALLOC].func_name = c->malloc_handler;
+ fh[NXT_WASM_FH_FREE].func_name = c->free_handler;
+
+ /* Optional function handlers (hooks) */
+ fh[NXT_WASM_FH_MODULE_INIT].func_name = c->module_init_handler;
+ fh[NXT_WASM_FH_MODULE_END].func_name = c->module_end_handler;
+ fh[NXT_WASM_FH_REQUEST_INIT].func_name = c->request_init_handler;
+ fh[NXT_WASM_FH_REQUEST_END].func_name = c->request_end_handler;
+ fh[NXT_WASM_FH_RESPONSE_END].func_name = c->response_end_handler;
+
+ /* Get any directories to pass through to the WASM module */
+ if (c->access != NULL) {
+ dirs = nxt_conf_get_object_member(c->access, &filesystem_str, NULL);
+ }
+
+ n = (dirs != NULL) ? nxt_conf_object_members_count(dirs) : 0;
+ if (n == 0) {
+ goto out_init;
+ }
+
+ nxt_wasm_ctx.dirs = nxt_zalloc((n + 1) * sizeof(char *));
+ if (nxt_slow_path(nxt_wasm_ctx.dirs == NULL)) {
+ return NXT_ERROR;
+ }
+
+ for (i = 0; i < n; i++) {
+ nxt_str_t str;
+ nxt_conf_value_t *value;
+
+ value = nxt_conf_get_array_element(dirs, i);
+ nxt_conf_get_string(value, &str);
+
+ nxt_wasm_ctx.dirs[i] = nxt_zalloc(str.length + 1);
+ memcpy(nxt_wasm_ctx.dirs[i], str.start, str.length);
+ }
+
+out_init:
+ err = nxt_wops->init(&nxt_wasm_ctx);
+ if (err) {
+ exit(EXIT_FAILURE);
+ }
+
+ return NXT_OK;
+}
+
+
+NXT_EXPORT nxt_app_module_t nxt_app_module = {
+ .compat_length = sizeof(compat),
+ .compat = compat,
+ .type = nxt_string("wasm"),
+ .version = NXT_WASM_VERSION,
+ .mounts = NULL,
+ .nmounts = 0,
+ .setup = nxt_wasm_setup,
+ .start = nxt_wasm_start,
+};
diff --git a/src/wasm/nxt_wasm.h b/src/wasm/nxt_wasm.h
new file mode 100644
index 00000000..cb9dbdfe
--- /dev/null
+++ b/src/wasm/nxt_wasm.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#ifndef _NXT_WASM_H_INCLUDED_
+#define _NXT_WASM_H_INCLUDED_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <nxt_unit.h>
+
+#include <wasm.h>
+#if defined(NXT_HAVE_WASM_WASMTIME)
+#include <wasmtime.h>
+#endif
+
+
+#define NXT_WASM_PAGE_SIZE (64 * 1024)
+#define NXT_WASM_MEM_SIZE (32UL * 1024 * 1024)
+
+#if defined(NXT_HAVE_WASM_WASMTIME)
+typedef wasmtime_func_t nxt_wasm_func_t;
+#endif
+
+
+typedef struct nxt_wasm_http_field_s nxt_wasm_http_field_t;
+typedef struct nxt_wasm_request_s nxt_wasm_request_t;
+typedef struct nxt_wasm_response_s nxt_wasm_response_t;
+typedef struct nxt_wasm_response_fields_s nxt_wasm_response_fields_t;
+typedef enum nxt_wasm_fh_e nxt_wasm_fh_t;
+typedef struct nxt_wasm_func_handler_s nxt_wasm_func_handler_t;
+typedef struct nxt_wasm_ctx_s nxt_wasm_ctx_t;
+typedef struct nxt_wasm_operations_s nxt_wasm_operations_t;
+
+struct nxt_wasm_http_field_s {
+ uint32_t name_off;
+ uint32_t name_len;
+ uint32_t value_off;
+ uint32_t value_len;
+};
+
+struct nxt_wasm_request_s {
+ uint32_t method_off;
+ uint32_t method_len;
+ uint32_t version_off;
+ uint32_t version_len;
+ uint32_t path_off;
+ uint32_t path_len;
+ uint32_t query_off;
+ uint32_t query_len;
+ uint32_t remote_off;
+ uint32_t remote_len;
+ uint32_t local_addr_off;
+ uint32_t local_addr_len;
+ uint32_t local_port_off;
+ uint32_t local_port_len;
+ uint32_t server_name_off;
+ uint32_t server_name_len;
+
+ uint32_t content_off;
+ uint32_t content_len;
+ uint32_t content_sent;
+ uint32_t total_content_sent;
+
+ uint32_t request_size;
+
+ uint32_t nfields;
+
+ uint32_t tls;
+
+ nxt_wasm_http_field_t fields[];
+};
+
+struct nxt_wasm_response_s {
+ uint32_t size;
+
+ uint8_t data[];
+};
+
+struct nxt_wasm_response_fields_s {
+ uint32_t nfields;
+
+ nxt_wasm_http_field_t fields[];
+};
+
+enum nxt_wasm_fh_e {
+ NXT_WASM_FH_REQUEST = 0,
+ NXT_WASM_FH_MALLOC,
+ NXT_WASM_FH_FREE,
+
+ /* Optional handlers */
+ NXT_WASM_FH_MODULE_INIT,
+ NXT_WASM_FH_MODULE_END,
+ NXT_WASM_FH_REQUEST_INIT,
+ NXT_WASM_FH_REQUEST_END,
+ NXT_WASM_FH_RESPONSE_END,
+
+ NXT_WASM_FH_NR
+};
+
+struct nxt_wasm_func_handler_s {
+ const char *func_name;
+ nxt_wasm_func_t func;
+};
+
+struct nxt_wasm_ctx_s {
+ const char *module_path;
+
+ nxt_wasm_func_handler_t fh[NXT_WASM_FH_NR];
+
+ char **dirs;
+
+ nxt_unit_request_info_t *req;
+
+ uint8_t *baddr;
+ size_t baddr_off;
+
+ size_t response_off;
+};
+
+struct nxt_wasm_operations_s {
+ int (*init)(nxt_wasm_ctx_t *ctx);
+ void (*destroy)(const nxt_wasm_ctx_t *ctx);
+ void (*exec_request)(const nxt_wasm_ctx_t *ctx);
+ void (*exec_hook)(const nxt_wasm_ctx_t *ctx, nxt_wasm_fh_t hook);
+};
+
+extern const nxt_wasm_operations_t nxt_wasm_ops;
+
+
+/* Exported to the WASM module */
+extern void nxt_wasm_do_response_end(nxt_wasm_ctx_t *ctx);
+extern void nxt_wasm_do_send_response(nxt_wasm_ctx_t *ctx, uint32_t offset);
+extern void nxt_wasm_do_send_headers(nxt_wasm_ctx_t *ctx, uint32_t offset);
+
+#endif /* _NXT_WASM_H_INCLUDED_ */