diff options
author | Konstantin Pavlov <thresh@nginx.com> | 2023-08-31 09:41:46 -0700 |
---|---|---|
committer | Konstantin Pavlov <thresh@nginx.com> | 2023-08-31 09:41:46 -0700 |
commit | c45c8919c7232eb20023484f6d1fc9f1f50395d8 (patch) | |
tree | cc12eb307c1611494948645e4b487fa06495c3d2 /src | |
parent | 88c90e1c351ab8c5bd487a5cd4b735014b08e271 (diff) | |
parent | 9b22b6957bc87b3df002d0bc691fdae6a20abdac (diff) | |
download | unit-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')
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_ */ |