diff options
author | Zhidao HONG <z.hong@f5.com> | 2023-05-08 16:00:25 +0800 |
---|---|---|
committer | Zhidao HONG <z.hong@f5.com> | 2023-05-08 16:00:25 +0800 |
commit | a3c3a29493798873ad04922bb2a7180b2ce267d5 (patch) | |
tree | 4332cbb50520c389d859db27e7266ffa53954b92 | |
parent | 56af7bb825c286e1a397f780bff9da275e5602ad (diff) | |
download | unit-a3c3a29493798873ad04922bb2a7180b2ce267d5.tar.gz unit-a3c3a29493798873ad04922bb2a7180b2ce267d5.tar.bz2 |
NJS: supported loadable modules.
-rw-r--r-- | auto/sources | 2 | ||||
-rw-r--r-- | docs/changes.xml | 6 | ||||
-rw-r--r-- | src/nxt_conf_validation.c | 74 | ||||
-rw-r--r-- | src/nxt_controller.c | 353 | ||||
-rw-r--r-- | src/nxt_js.c | 248 | ||||
-rw-r--r-- | src/nxt_js.h | 11 | ||||
-rw-r--r-- | src/nxt_main_process.c | 7 | ||||
-rw-r--r-- | src/nxt_port.h | 6 | ||||
-rw-r--r-- | src/nxt_process.h | 3 | ||||
-rw-r--r-- | src/nxt_router.c | 158 | ||||
-rw-r--r-- | src/nxt_router.h | 4 | ||||
-rw-r--r-- | src/nxt_runtime.c | 17 | ||||
-rw-r--r-- | src/nxt_runtime.h | 1 | ||||
-rw-r--r-- | src/nxt_script.c | 709 | ||||
-rw-r--r-- | src/nxt_script.h | 37 | ||||
-rw-r--r-- | src/nxt_tstr.c | 5 |
16 files changed, 1591 insertions, 50 deletions
diff --git a/auto/sources b/auto/sources index 9a7ff010..f4a7170a 100644 --- a/auto/sources +++ b/auto/sources @@ -124,7 +124,7 @@ NXT_LIB_PCRE_SRCS="src/nxt_pcre.c" NXT_LIB_PCRE2_SRCS="src/nxt_pcre2.c" if [ "$NXT_NJS" != "NO" ]; then - NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_js.c src/nxt_http_js.c" + NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_js.c src/nxt_http_js.c src/nxt_script.c" fi NXT_LIB_EPOLL_SRCS="src/nxt_epoll_engine.c" diff --git a/docs/changes.xml b/docs/changes.xml index 6c89fa15..9567aebd 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -40,6 +40,12 @@ basic URI rewrite support. <change type="feature"> <para> +njs loadable modules support. +</para> +</change> + +<change type="feature"> +<para> added conditional logging of route selection for HTTP requests. </para> </change> diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index c9aac790..8c75a9fe 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -7,6 +7,7 @@ #include <nxt_main.h> #include <nxt_conf.h> #include <nxt_cert.h> +#include <nxt_script.h> #include <nxt_router.h> #include <nxt_http.h> #include <nxt_sockaddr.h> @@ -226,6 +227,13 @@ static nxt_int_t nxt_conf_vldt_cgroup_path(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); #endif +#if (NXT_HAVE_NJS) +static nxt_int_t nxt_conf_vldt_js_module(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_js_module_element(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value); +#endif + static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[]; @@ -297,6 +305,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[] = { .type = NXT_CONF_VLDT_OBJECT, .validator = nxt_conf_vldt_object, .u.members = nxt_conf_vldt_http_members, +#if (NXT_HAVE_NJS) + }, { + .name = nxt_string("js_module"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = nxt_conf_vldt_js_module, +#endif }, NXT_CONF_VLDT_END @@ -1306,35 +1320,26 @@ nxt_conf_validate(nxt_conf_validation_t *vldt) vldt->tstr_state = nxt_tstr_state_new(vldt->pool, 1); if (nxt_slow_path(vldt->tstr_state == NULL)) { - ret = NXT_ERROR; - goto fail; + return NXT_ERROR; } ret = nxt_conf_vldt_type(vldt, NULL, vldt->conf, NXT_CONF_VLDT_OBJECT); if (ret != NXT_OK) { - goto fail; + return ret; } ret = nxt_conf_vldt_object(vldt, vldt->conf, nxt_conf_vldt_root_members); if (ret != NXT_OK) { - goto fail; + return ret; } ret = nxt_tstr_state_done(vldt->tstr_state, error); if (ret != NXT_OK) { ret = nxt_conf_vldt_error(vldt, "%s", error); - goto fail; + return ret; } - nxt_tstr_state_release(vldt->tstr_state); - return NXT_OK; - -fail: - - nxt_tstr_state_release(vldt->tstr_state); - - return ret; } @@ -3241,6 +3246,49 @@ nxt_conf_vldt_server_weight(nxt_conf_validation_t *vldt, } +#if (NXT_HAVE_NJS) + +static nxt_int_t +nxt_conf_vldt_js_module(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { + return nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_js_module_element); + } + + /* NXT_CONF_STRING */ + + return nxt_conf_vldt_js_module_element(vldt, value); +} + + +static nxt_int_t +nxt_conf_vldt_js_module_element(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value) +{ + nxt_str_t name; + nxt_conf_value_t *module; + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"js_module\" array must " + "contain only string values."); + } + + nxt_conf_get_string(value, &name); + + module = nxt_script_info_get(&name); + if (module == NULL) { + return nxt_conf_vldt_error(vldt, "JS module \"%V\" is not found.", + &name); + } + + return NXT_OK; +} + +#endif + + typedef struct { nxt_str_t path; nxt_str_t format; diff --git a/src/nxt_controller.c b/src/nxt_controller.c index b5e0d831..4e2e3749 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -11,6 +11,7 @@ #include <nxt_conf.h> #include <nxt_status.h> #include <nxt_cert.h> +#include <nxt_script.h> typedef struct { @@ -101,6 +102,15 @@ static nxt_bool_t nxt_controller_cert_in_use(nxt_str_t *name); static void nxt_controller_cert_cleanup(nxt_task_t *task, void *obj, void *data); #endif +#if (NXT_HAVE_NJS) +static void nxt_controller_process_script(nxt_task_t *task, + nxt_controller_request_t *req, nxt_str_t *path); +static void nxt_controller_process_script_save(nxt_task_t *task, + nxt_port_recv_msg_t *msg, void *data); +static nxt_bool_t nxt_controller_script_in_use(nxt_str_t *name); +static void nxt_controller_script_cleanup(nxt_task_t *task, void *obj, + void *data); +#endif static void nxt_controller_process_control(nxt_task_t *task, nxt_controller_request_t *req, nxt_str_t *path); static void nxt_controller_app_restart_handler(nxt_task_t *task, @@ -213,6 +223,13 @@ nxt_controller_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) nxt_mp_cleanup(mp, nxt_controller_cert_cleanup, task, ctrl_init.certs, rt); #endif +#if (NXT_HAVE_NJS) + ctrl_init.scripts = nxt_script_store_load(task, mp); + + nxt_mp_cleanup(mp, nxt_controller_script_cleanup, task, ctrl_init.scripts, + rt); +#endif + process->data.controller = ctrl_init; return NXT_OK; @@ -321,6 +338,13 @@ nxt_controller_start(nxt_task_t *task, nxt_process_data_t *data) } #endif +#if (NXT_HAVE_NJS) + if (init->scripts != NULL) { + nxt_script_info_init(task, init->scripts); + nxt_script_store_release(init->scripts); + } +#endif + json = &init->conf; if (json->start == NULL) { @@ -1047,9 +1071,19 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) nxt_controller_response_t resp; #if (NXT_TLS) nxt_conf_value_t *certs; +#endif +#if (NXT_HAVE_NJS) + nxt_conf_value_t *scripts; +#endif +#if (NXT_TLS) static nxt_str_t certificates = nxt_string("certificates"); #endif + +#if (NXT_HAVE_NJS) + static nxt_str_t scripts_str = nxt_string("js_modules"); +#endif + static nxt_str_t config = nxt_string("config"); static nxt_str_t status = nxt_string("status"); @@ -1120,6 +1154,25 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) #endif +#if (NXT_HAVE_NJS) + + if (nxt_str_start(&path, "/js_modules", 11) + && (path.length == 11 || path.start[11] == '/')) + { + if (path.length == 11) { + path.length = 1; + + } else { + path.length -= 11; + path.start += 11; + } + + nxt_controller_process_script(task, req, &path); + return; + } + +#endif + if (nxt_str_start(&path, "/control/", 9)) { path.length -= 9; path.start += 9; @@ -1143,6 +1196,9 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) #if (NXT_TLS) count++; #endif +#if (NXT_HAVE_NJS) + count++; +#endif value = nxt_conf_create_object(c->mem_pool, count); if (nxt_slow_path(value == NULL)) { @@ -1160,6 +1216,15 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) nxt_conf_set_member(value, &certificates, certs, i++); #endif +#if (NXT_HAVE_NJS) + scripts = nxt_script_info_get_all(c->mem_pool); + if (nxt_slow_path(scripts == NULL)) { + goto alloc_fail; + } + + nxt_conf_set_member(value, &scripts_str, scripts, i++); +#endif + nxt_conf_set_member(value, &config, nxt_controller_conf.root, i++); nxt_conf_set_member(value, &status, nxt_controller_status, i); @@ -1879,6 +1944,294 @@ nxt_controller_cert_in_use(nxt_str_t *name) #endif +#if (NXT_HAVE_NJS) + +static void +nxt_controller_process_script(nxt_task_t *task, + nxt_controller_request_t *req, nxt_str_t *path) +{ + u_char *p; + nxt_int_t ret; + nxt_str_t name; + nxt_conn_t *c; + nxt_script_t *script; + nxt_buf_mem_t *bm; + nxt_conf_value_t *value; + nxt_controller_response_t resp; + u_char error[NXT_MAX_ERROR_STR]; + + name.length = path->length - 1; + name.start = path->start + 1; + + p = memchr(name.start, '/', name.length); + + if (p != NULL) { + name.length = p - name.start; + + path->length -= p - path->start; + path->start = p; + + } else { + path = NULL; + } + + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + c = req->conn; + + if (nxt_str_eq(&req->parser.method, "GET", 3)) { + + if (name.length != 0) { + value = nxt_script_info_get(&name); + if (value == NULL) { + goto script_not_found; + } + + if (path != NULL) { + value = nxt_conf_get_path(value, path); + if (value == NULL) { + goto not_found; + } + } + + } else { + value = nxt_script_info_get_all(c->mem_pool); + if (value == NULL) { + goto alloc_fail; + } + } + + resp.status = 200; + resp.conf = value; + + nxt_controller_response(task, req, &resp); + return; + } + + if (name.length == 0 || path != NULL) { + goto invalid_name; + } + + if (nxt_str_eq(&req->parser.method, "PUT", 3)) { + value = nxt_script_info_get(&name); + if (value != NULL) { + goto exists_script; + } + + bm = &c->read->mem; + + script = nxt_script_new(task, &name, bm->pos, + nxt_buf_mem_used_size(bm), error); + if (script == NULL) { + goto invalid_script; + } + + ret = nxt_script_info_save(&name, script); + + nxt_script_destroy(script); + + if (nxt_slow_path(ret != NXT_OK)) { + goto alloc_fail; + } + + nxt_script_store_get(task, &name, c->mem_pool, + nxt_controller_process_script_save, req); + return; + } + + if (nxt_str_eq(&req->parser.method, "DELETE", 6)) { + + if (nxt_controller_script_in_use(&name)) { + goto script_in_use; + } + + if (nxt_script_info_delete(&name) != NXT_OK) { + goto script_not_found; + } + + nxt_script_store_delete(task, &name, c->mem_pool); + + resp.status = 200; + resp.title = (u_char *) "JS module deleted."; + + nxt_controller_response(task, req, &resp); + return; + } + + resp.status = 405; + resp.title = (u_char *) "Invalid method."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +invalid_name: + + resp.status = 400; + resp.title = (u_char *) "Invalid JS module name."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +invalid_script: + + resp.status = 400; + resp.title = (u_char *) "Invalid JS module."; + resp.offset = -1; + + resp.detail.start = error; + resp.detail.length = nxt_strlen(error); + + nxt_controller_response(task, req, &resp); + return; + +exists_script: + + resp.status = 400; + resp.title = (u_char *) "JS module already exists."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +script_in_use: + + resp.status = 400; + resp.title = (u_char *) "JS module is used in the configuration."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +script_not_found: + + resp.status = 404; + resp.title = (u_char *) "JS module doesn't exist."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +not_found: + + resp.status = 404; + resp.title = (u_char *) "Invalid path."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +alloc_fail: + + resp.status = 500; + resp.title = (u_char *) "Memory allocation failed."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); +} + + +static void +nxt_controller_process_script_save(nxt_task_t *task, nxt_port_recv_msg_t *msg, + void *data) +{ + nxt_conn_t *c; + nxt_buf_mem_t *mbuf; + nxt_controller_request_t *req; + nxt_controller_response_t resp; + + req = data; + + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + if (msg == NULL || msg->port_msg.type == _NXT_PORT_MSG_RPC_ERROR) { + resp.status = 500; + resp.title = (u_char *) "Failed to store script."; + + nxt_controller_response(task, req, &resp); + return; + } + + c = req->conn; + + mbuf = &c->read->mem; + + nxt_fd_write(msg->fd[0], mbuf->pos, nxt_buf_mem_used_size(mbuf)); + + nxt_fd_close(msg->fd[0]); + + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + resp.status = 200; + resp.title = (u_char *) "JS module uploaded."; + + nxt_controller_response(task, req, &resp); +} + + +static nxt_bool_t +nxt_controller_script_in_use(nxt_str_t *name) +{ + uint32_t i, n; + nxt_str_t str; + nxt_conf_value_t *js_module, *element; + + static nxt_str_t js_module_path = nxt_string("/settings/js_module"); + + js_module = nxt_conf_get_path(nxt_controller_conf.root, + &js_module_path); + + if (js_module != NULL) { + + if (nxt_conf_type(js_module) == NXT_CONF_ARRAY) { + n = nxt_conf_array_elements_count(js_module); + + for (i = 0; i < n; i++) { + element = nxt_conf_get_array_element(js_module, i); + + nxt_conf_get_string(element, &str); + + if (nxt_strstr_eq(&str, name)) { + return 1; + } + } + + } else { + /* NXT_CONF_STRING */ + + nxt_conf_get_string(js_module, &str); + + if (nxt_strstr_eq(&str, name)) { + return 1; + } + } + } + + return 0; +} + + +static void +nxt_controller_script_cleanup(nxt_task_t *task, void *obj, void *data) +{ + pid_t main_pid; + nxt_array_t *scripts; + nxt_runtime_t *rt; + + scripts = obj; + rt = data; + + main_pid = rt->port_by_type[NXT_PROCESS_MAIN]->pid; + + if (nxt_pid == main_pid && scripts != NULL) { + nxt_script_store_release(scripts); + } +} + +#endif + + static void nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) diff --git a/src/nxt_js.c b/src/nxt_js.c index 4327e848..df945db6 100644 --- a/src/nxt_js.c +++ b/src/nxt_js.c @@ -8,16 +8,72 @@ struct nxt_js_s { uint32_t index; - njs_vm_t *vm; }; +typedef struct { + nxt_str_t name; + nxt_str_t text; +} nxt_js_module_t; + + struct nxt_js_conf_s { nxt_mp_t *pool; njs_vm_t *vm; njs_uint_t protos; njs_external_t *proto; + nxt_str_t init; + nxt_array_t *modules; /* of nxt_js_module_t */ nxt_array_t *funcs; + uint8_t test; /* 1 bit */ +}; + + +njs_mod_t * +nxt_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) +{ + nxt_str_t text; + nxt_uint_t i, n; + nxt_js_conf_t *jcf; + nxt_js_module_t *modules, *module; + + jcf = external; + + module = NULL; + + n = jcf->modules->nelts; + modules = jcf->modules->elts; + + for (i = 0; i < n; i++) { + if (nxt_strstr_eq(name, &modules[i].name)) { + module = &modules[i]; + break; + } + } + + if (module == NULL) { + return NULL; + } + + text.length = module->text.length; + + text.start = njs_mp_alloc(vm->mem_pool, text.length); + if (nxt_slow_path(text.start == NULL)) { + return NULL; + } + + nxt_memcpy(text.start, module->text.start, text.length); + + return njs_vm_compile_module(vm, name, &text.start, + &text.start[text.length]); +} + + +static njs_vm_ops_t nxt_js_ops = { + NULL, + NULL, + nxt_js_module_loader, + NULL, }; @@ -25,9 +81,8 @@ njs_int_t nxt_js_proto_id; nxt_js_conf_t * -nxt_js_conf_new(nxt_mp_t *mp) +nxt_js_conf_new(nxt_mp_t *mp, nxt_bool_t test) { - njs_vm_opt_t opts; nxt_js_conf_t *jcf; jcf = nxt_mp_zget(mp, sizeof(nxt_js_conf_t)); @@ -36,17 +91,15 @@ nxt_js_conf_new(nxt_mp_t *mp) } jcf->pool = mp; + jcf->test = test; - njs_vm_opt_init(&opts); - - jcf->vm = njs_vm_create(&opts); - if (nxt_slow_path(jcf->vm == NULL)) { + jcf->modules = nxt_array_create(mp, 4, sizeof(nxt_js_module_t)); + if (nxt_slow_path(jcf->modules == NULL)) { return NULL; } jcf->funcs = nxt_array_create(mp, 4, sizeof(nxt_str_t)); if (nxt_slow_path(jcf->funcs == NULL)) { - njs_vm_destroy(jcf->vm); return NULL; } @@ -69,6 +122,115 @@ nxt_js_set_proto(nxt_js_conf_t *jcf, njs_external_t *proto, njs_uint_t n) } +static njs_vm_t * +nxt_js_vm_create(nxt_js_conf_t *jcf) +{ + u_char *p; + size_t size; + nxt_uint_t i; + njs_vm_opt_t opts; + nxt_js_module_t *module, *mod; + + static nxt_str_t import_str = nxt_string("import"); + static nxt_str_t from_str = nxt_string("from"); + static nxt_str_t global_str = nxt_string("globalThis"); + + njs_vm_opt_init(&opts); + + opts.backtrace = 1; + + opts.file.start = (u_char *) "default"; + opts.file.length = 7; + + if (jcf->test || jcf->modules->nelts == 0) { + goto done; + } + + opts.ops = &nxt_js_ops; + opts.external = jcf; + + size = 0; + module = jcf->modules->elts; + + for (i = 0; i < jcf->modules->nelts; i++) { + mod = &module[i]; + + size += import_str.length + 1 + mod->name.length + 1 + + from_str.length + 2 + mod->name.length + 3; + + size += global_str.length + 1 + mod->name.length + 3 + + mod->name.length + 2; + } + + p = nxt_mp_nget(jcf->pool, size); + if (nxt_slow_path(p == NULL)) { + return NULL; + } + + jcf->init.length = size; + jcf->init.start = p; + + for (i = 0; i < jcf->modules->nelts; i++) { + mod = &module[i]; + + p = nxt_cpymem(p, import_str.start, import_str.length); + *p++ = ' '; + + p = nxt_cpymem(p, mod->name.start, mod->name.length); + *p++ = ' '; + + p = nxt_cpymem(p, from_str.start, from_str.length); + *p++ = ' '; + + *p++ = '\"'; + p = nxt_cpymem(p, mod->name.start, mod->name.length); + *p++ = '\"'; + *p++ = ';'; + *p++ = '\n'; + + p = nxt_cpymem(p, global_str.start, global_str.length); + *p++ = '.'; + + p = nxt_cpymem(p, mod->name.start, mod->name.length); + *p++ = ' '; + *p++ = '='; + *p++ = ' '; + + p = nxt_cpymem(p, mod->name.start, mod->name.length); + *p++ = ';'; + *p++ = '\n'; + } + +done: + + return njs_vm_create(&opts); +} + + +nxt_int_t +nxt_js_add_module(nxt_js_conf_t *jcf, nxt_str_t *name, nxt_str_t *text) +{ + nxt_js_module_t *module; + + module = nxt_array_add(jcf->modules); + if (nxt_slow_path(module == NULL)) { + return NXT_ERROR; + } + + module->name = *name; + + module->text.length = text->length; + module->text.start = nxt_mp_nget(jcf->pool, text->length); + if (nxt_slow_path(module->text.start == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(module->text.start, text->start, text->length); + + return NXT_OK; +} + + nxt_js_t * nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz) { @@ -113,8 +275,6 @@ nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz) return NULL; } - js->vm = jcf->vm; - func = nxt_array_add(jcf->funcs); if (nxt_slow_path(func == NULL)) { return NULL; @@ -138,7 +298,16 @@ nxt_js_compile(nxt_js_conf_t *jcf) nxt_str_t *func; nxt_uint_t i; - size = 2; + if (jcf->test) { + return NXT_OK; + } + + jcf->vm = nxt_js_vm_create(jcf); + if (nxt_slow_path(jcf->vm == NULL)) { + return NXT_ERROR; + } + + size = jcf->init.length + 2; func = jcf->funcs->elts; for (i = 0; i < jcf->funcs->nelts; i++) { @@ -150,7 +319,7 @@ nxt_js_compile(nxt_js_conf_t *jcf) return NXT_ERROR; } - p = start; + p = nxt_cpymem(start, jcf->init.start, jcf->init.length); *p++ = '['; func = jcf->funcs->elts; @@ -178,37 +347,43 @@ nxt_int_t nxt_js_test(nxt_js_conf_t *jcf, nxt_str_t *str, u_char *error) { u_char *start; - nxt_str_t err; + njs_vm_t *vm; njs_int_t ret; - njs_str_t res; + + vm = nxt_js_vm_create(jcf); + if (nxt_slow_path(vm == NULL)) { + return NXT_ERROR; + } start = nxt_mp_nget(jcf->pool, str->length); if (nxt_slow_path(start == NULL)) { - return NXT_ERROR; + goto fail; } nxt_memcpy(start, str->start, str->length); - ret = njs_vm_compile(jcf->vm, &start, start + str->length); + ret = njs_vm_compile(vm, &start, start + str->length); if (nxt_slow_path(ret != NJS_OK)) { - (void) njs_vm_retval_string(jcf->vm, &res); + (void) nxt_js_error(vm, error); + goto fail; + } + + njs_vm_destroy(vm); - err.start = res.start; - err.length = res.length; + return NXT_OK; - nxt_sprintf(error, error + NXT_MAX_ERROR_STR, "\"%V\"%Z", &err); +fail: - return NXT_ERROR; - } + njs_vm_destroy(vm); - return NXT_OK; + return NXT_ERROR; } nxt_int_t -nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js, - nxt_str_t *str, void *ctx) +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; @@ -227,7 +402,7 @@ nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js, vm = cache->vm; if (vm == NULL) { - vm = njs_vm_clone(js->vm, ctx); + vm = njs_vm_clone(jcf->vm, ctx); if (nxt_slow_path(vm == NULL)) { return NXT_ERROR; } @@ -314,3 +489,24 @@ nxt_js_release(nxt_js_cache_t *cache) njs_vm_destroy(cache->vm); } } + + +nxt_int_t +nxt_js_error(njs_vm_t *vm, u_char *error) +{ + njs_int_t ret; + njs_str_t res; + nxt_str_t err; + + ret = njs_vm_retval_string(vm, &res); + if (nxt_slow_path(ret != NJS_OK)) { + return NXT_ERROR; + } + + err.start = res.start; + err.length = res.length; + + nxt_sprintf(error, error + NXT_MAX_ERROR_STR, "\"%V\"%Z", &err); + + return NXT_OK; +} diff --git a/src/nxt_js.h b/src/nxt_js.h index 74d041ca..48f036b8 100644 --- a/src/nxt_js.h +++ b/src/nxt_js.h @@ -21,15 +21,20 @@ typedef struct { } nxt_js_cache_t; -nxt_js_conf_t *nxt_js_conf_new(nxt_mp_t *mp); +njs_mod_t *nxt_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, + njs_str_t *name); +nxt_js_conf_t *nxt_js_conf_new(nxt_mp_t *mp, nxt_bool_t test); void nxt_js_conf_release(nxt_js_conf_t *jcf); void nxt_js_set_proto(nxt_js_conf_t *jcf, njs_external_t *proto, nxt_uint_t n); +nxt_int_t nxt_js_add_module(nxt_js_conf_t *jcf, nxt_str_t *name, + nxt_str_t *text); nxt_js_t *nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz); nxt_int_t nxt_js_compile(nxt_js_conf_t *jcf); nxt_int_t nxt_js_test(nxt_js_conf_t *jcf, nxt_str_t *str, u_char *error); -nxt_int_t nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js, - nxt_str_t *str, void *ctx); +nxt_int_t 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); void nxt_js_release(nxt_js_cache_t *cache); +nxt_int_t nxt_js_error(njs_vm_t *vm, u_char *error); extern njs_int_t nxt_js_proto_id; diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index ae62aff4..7cba08d4 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -14,6 +14,9 @@ #if (NXT_TLS) #include <nxt_cert.h> #endif +#if (NXT_HAVE_NJS) +#include <nxt_script.h> +#endif #include <sys/mount.h> @@ -608,6 +611,10 @@ static nxt_port_handlers_t nxt_main_process_port_handlers = { .cert_get = nxt_cert_store_get_handler, .cert_delete = nxt_cert_store_delete_handler, #endif +#if (NXT_HAVE_NJS) + .script_get = nxt_script_store_get_handler, + .script_delete = nxt_script_store_delete_handler, +#endif .access_log = nxt_main_port_access_log_handler, .rpc_ready = nxt_port_rpc_handler, .rpc_error = nxt_port_rpc_handler, diff --git a/src/nxt_port.h b/src/nxt_port.h index eba8d06f..772fb41a 100644 --- a/src/nxt_port.h +++ b/src/nxt_port.h @@ -21,6 +21,8 @@ struct nxt_port_handlers_s { nxt_port_handler_t conf_store; nxt_port_handler_t cert_get; nxt_port_handler_t cert_delete; + nxt_port_handler_t script_get; + nxt_port_handler_t script_delete; nxt_port_handler_t access_log; /* File descriptor exchange. */ @@ -87,6 +89,8 @@ typedef enum { _NXT_PORT_MSG_CONF_STORE = nxt_port_handler_idx(conf_store), _NXT_PORT_MSG_CERT_GET = nxt_port_handler_idx(cert_get), _NXT_PORT_MSG_CERT_DELETE = nxt_port_handler_idx(cert_delete), + _NXT_PORT_MSG_SCRIPT_GET = nxt_port_handler_idx(script_get), + _NXT_PORT_MSG_SCRIPT_DELETE = nxt_port_handler_idx(script_delete), _NXT_PORT_MSG_ACCESS_LOG = nxt_port_handler_idx(access_log), _NXT_PORT_MSG_CHANGE_FILE = nxt_port_handler_idx(change_file), @@ -129,6 +133,8 @@ typedef enum { NXT_PORT_MSG_CONF_STORE = nxt_msg_last(_NXT_PORT_MSG_CONF_STORE), NXT_PORT_MSG_CERT_GET = nxt_msg_last(_NXT_PORT_MSG_CERT_GET), NXT_PORT_MSG_CERT_DELETE = nxt_msg_last(_NXT_PORT_MSG_CERT_DELETE), + NXT_PORT_MSG_SCRIPT_GET = nxt_msg_last(_NXT_PORT_MSG_SCRIPT_GET), + NXT_PORT_MSG_SCRIPT_DELETE = nxt_msg_last(_NXT_PORT_MSG_SCRIPT_DELETE), NXT_PORT_MSG_ACCESS_LOG = nxt_msg_last(_NXT_PORT_MSG_ACCESS_LOG), NXT_PORT_MSG_CHANGE_FILE = nxt_msg_last(_NXT_PORT_MSG_CHANGE_FILE), NXT_PORT_MSG_NEW_PORT = nxt_msg_last(_NXT_PORT_MSG_NEW_PORT), diff --git a/src/nxt_process.h b/src/nxt_process.h index 16d6110c..42fd1bed 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -29,6 +29,9 @@ typedef struct { #if (NXT_TLS) nxt_array_t *certs; #endif +#if (NXT_HAVE_NJS) + nxt_array_t *scripts; +#endif } nxt_controller_init_t; diff --git a/src/nxt_router.c b/src/nxt_router.c index c4e29e3a..d089cfb8 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -11,6 +11,9 @@ #if (NXT_TLS) #include <nxt_cert.h> #endif +#if (NXT_HAVE_NJS) +#include <nxt_script.h> +#endif #include <nxt_http.h> #include <nxt_port_memory_int.h> #include <nxt_unit_request.h> @@ -55,6 +58,17 @@ typedef struct { #endif +#if (NXT_HAVE_NJS) + +typedef struct { + nxt_str_t name; + nxt_router_temp_conf_t *temp_conf; + nxt_queue_link_t link; +} nxt_router_js_module_t; + +#endif + + typedef struct { nxt_str_t *name; nxt_socket_conf_t *socket_conf; @@ -139,6 +153,12 @@ static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *value, nxt_socket_conf_t *skcf, nxt_tls_init_t *tls_init, nxt_bool_t last); #endif +#if (NXT_HAVE_NJS) +static void nxt_router_js_module_rpc_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg, void *data); +static nxt_int_t nxt_router_js_module_insert(nxt_router_temp_conf_t *tmcf, + nxt_conf_value_t *value); +#endif static void nxt_router_app_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_app_t *app); static void nxt_router_app_prefork_ready(nxt_task_t *task, @@ -1100,6 +1120,10 @@ nxt_router_temp_conf(nxt_task_t *task) nxt_queue_init(&tmcf->tls); #endif +#if (NXT_HAVE_NJS) + nxt_queue_init(&tmcf->js_modules); +#endif + nxt_queue_init(&tmcf->apps); nxt_queue_init(&tmcf->previous); @@ -1154,6 +1178,9 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data) #if (NXT_TLS) nxt_router_tlssock_t *tls; #endif +#if (NXT_HAVE_NJS) + nxt_router_js_module_t *js_module; +#endif tmcf = obj; @@ -1184,6 +1211,27 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data) } #endif +#if (NXT_HAVE_NJS) + qlk = nxt_queue_last(&tmcf->js_modules); + + if (qlk != nxt_queue_head(&tmcf->js_modules)) { + nxt_queue_remove(qlk); + + js_module = nxt_queue_link_data(qlk, nxt_router_js_module_t, link); + + nxt_script_store_get(task, &js_module->name, tmcf->mem_pool, + nxt_router_js_module_rpc_handler, js_module); + return; + } +#endif + + rtcf = tmcf->router_conf; + + ret = nxt_tstr_state_done(rtcf->tstr_state, NULL); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + nxt_queue_each(app, &tmcf->apps, nxt_app_t, link) { if (nxt_router_app_need_start(app)) { @@ -1193,8 +1241,6 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data) } nxt_queue_loop; - rtcf = tmcf->router_conf; - if (rtcf->access_log != NULL && rtcf->access_log->fd == -1) { nxt_router_access_log_open(task, tmcf); return; @@ -1570,6 +1616,9 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_tls_init_t *tls_init; nxt_conf_value_t *certificate; #endif +#if (NXT_HAVE_NJS) + nxt_conf_value_t *js_module; +#endif nxt_conf_value_t *root, *conf, *http, *value, *websocket; nxt_conf_value_t *applications, *application; nxt_conf_value_t *listeners, *listener; @@ -1593,6 +1642,9 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, static nxt_str_t conf_timeout_path = nxt_string("/tls/session/timeout"); static nxt_str_t conf_tickets = nxt_string("/tls/session/tickets"); #endif +#if (NXT_HAVE_NJS) + static nxt_str_t js_module_path = nxt_string("/settings/js_module"); +#endif static nxt_str_t static_path = nxt_string("/settings/http/static"); static nxt_str_t websocket_path = nxt_string("/settings/http/websocket"); static nxt_str_t forwarded_path = nxt_string("/forwarded"); @@ -2064,11 +2116,34 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } } - ret = nxt_tstr_state_done(rtcf->tstr_state, NULL); - if (nxt_slow_path(ret != NXT_OK)) { - goto fail; +#if (NXT_HAVE_NJS) + js_module = nxt_conf_get_path(root, &js_module_path); + + if (js_module != NULL) { + if (nxt_conf_type(js_module) == NXT_CONF_ARRAY) { + n = nxt_conf_array_elements_count(js_module); + + for (i = 0; i < n; i++) { + value = nxt_conf_get_array_element(js_module, i); + + ret = nxt_router_js_module_insert(tmcf, value); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + } + + } else { + /* NXT_CONF_STRING */ + + ret = nxt_router_js_module_insert(tmcf, js_module); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + } } +#endif + nxt_queue_add(&deleting_sockets, &router->sockets); nxt_queue_init(&router->sockets); @@ -2120,6 +2195,79 @@ nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, #endif +#if (NXT_HAVE_NJS) + +static void +nxt_router_js_module_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, + void *data) +{ + nxt_int_t ret; + nxt_str_t text; + nxt_router_conf_t *rtcf; + nxt_router_temp_conf_t *tmcf; + nxt_router_js_module_t *js_module; + + nxt_debug(task, "auto module rpc handler"); + + js_module = data; + tmcf = js_module->temp_conf; + + if (msg == NULL || msg->port_msg.type == _NXT_PORT_MSG_RPC_ERROR) { + goto fail; + } + + rtcf = tmcf->router_conf; + + ret = nxt_script_file_read(msg->fd[0], &text); + + nxt_fd_close(msg->fd[0]); + + if (nxt_slow_path(ret == NXT_ERROR)) { + goto fail; + } + + if (text.length > 0) { + ret = nxt_js_add_module(rtcf->tstr_state->jcf, &js_module->name, &text); + + nxt_free(text.start); + + if (nxt_slow_path(ret == NXT_ERROR)) { + goto fail; + } + } + + nxt_work_queue_add(&task->thread->engine->fast_work_queue, + nxt_router_conf_apply, task, tmcf, NULL); + return; + +fail: + + nxt_router_conf_error(task, tmcf); +} + + +static nxt_int_t +nxt_router_js_module_insert(nxt_router_temp_conf_t *tmcf, + nxt_conf_value_t *value) +{ + nxt_router_js_module_t *js_module; + + js_module = nxt_mp_get(tmcf->mem_pool, sizeof(nxt_router_js_module_t)); + if (nxt_slow_path(js_module == NULL)) { + return NXT_ERROR; + } + + js_module->temp_conf = tmcf; + nxt_conf_get_string(value, &js_module->name); + + nxt_queue_insert_tail(&tmcf->js_modules, &js_module->link); + + return NXT_OK; +} + +#endif + + static nxt_int_t nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_conf_value_t *conf) diff --git a/src/nxt_router.h b/src/nxt_router.h index 6f0ba5ad..b14f8410 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -74,6 +74,10 @@ typedef struct { nxt_queue_t tls; /* of nxt_router_tlssock_t */ #endif +#if (NXT_HAVE_NJS) + nxt_queue_t js_modules; +#endif + nxt_queue_t apps; /* of nxt_app_t */ nxt_queue_t previous; /* of nxt_app_t */ diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 1da52da0..96f801fb 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -906,6 +906,23 @@ nxt_runtime_conf_init(nxt_task_t *task, nxt_runtime_t *rt) "mkdir(%s) failed %E", file_name.start, nxt_errno); } + ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%sscripts/%Z", + rt->state, slash); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + ret = mkdir((char *) file_name.start, S_IRWXU); + + if (nxt_fast_path(ret == 0 || nxt_errno == EEXIST)) { + rt->scripts.length = file_name.len; + rt->scripts.start = file_name.start; + + } else { + nxt_alert(task, "Unable to create scripts storage directory: " + "mkdir(%s) failed %E", file_name.start, nxt_errno); + } + control.length = nxt_strlen(rt->control); control.start = (u_char *) rt->control; diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h index 687914f0..66ec0106 100644 --- a/src/nxt_runtime.h +++ b/src/nxt_runtime.h @@ -74,6 +74,7 @@ struct nxt_runtime_s { const char *tmp; nxt_str_t certs; + nxt_str_t scripts; nxt_queue_t engines; /* of nxt_event_engine_t */ diff --git a/src/nxt_script.c b/src/nxt_script.c new file mode 100644 index 00000000..70045a22 --- /dev/null +++ b/src/nxt_script.c @@ -0,0 +1,709 @@ + +/* + * Copyright (C) NGINX, Inc. + * Copyright (C) Zhidao HONG + */ + +#include <nxt_main.h> +#include <nxt_conf.h> +#include <nxt_script.h> +#include <dirent.h> + + +struct nxt_script_s { + nxt_str_t text; +}; + + +typedef struct { + nxt_str_t name; + nxt_conf_value_t *value; + nxt_mp_t *mp; +} nxt_script_info_t; + + +typedef struct { + nxt_str_t name; + nxt_fd_t fd; +} nxt_script_item_t; + + +static nxt_script_t *nxt_script_get(nxt_task_t *task, nxt_str_t *name, + nxt_fd_t fd); +static nxt_conf_value_t *nxt_script_details(nxt_mp_t *mp, nxt_script_t *cert); +static void nxt_script_buf_completion(nxt_task_t *task, void *obj, void *data); + + +static nxt_lvlhsh_t nxt_script_info; + + +static njs_vm_ops_t nxt_js_ops = { + NULL, + NULL, + nxt_js_module_loader, + NULL, +}; + + +nxt_script_t * +nxt_script_new(nxt_task_t *task, nxt_str_t *name, u_char *data, size_t size, + u_char *error) +{ + u_char *start; + njs_vm_t *vm; + njs_str_t mod_name; + njs_mod_t *mod; + njs_vm_opt_t opts; + nxt_script_t *script; + + njs_vm_opt_init(&opts); + + opts.backtrace = 1; + + opts.file.start = (u_char *) "default"; + opts.file.length = 7; + + opts.ops = &nxt_js_ops; + + vm = njs_vm_create(&opts); + if (nxt_slow_path(vm == NULL)) { + return NULL; + } + + mod_name.length = name->length; + mod_name.start = name->start; + + start = data; + + mod = njs_vm_compile_module(vm, &mod_name, &start, start + size); + + if (nxt_slow_path(mod == NULL)) { + (void) nxt_js_error(vm, error); + nxt_alert(task, "JS compile module(%V) failed: %s", name, error); + + goto fail; + } + + script = nxt_zalloc(sizeof(nxt_script_t) + size); + if (nxt_slow_path(script == NULL)) { + goto fail; + } + + script->text.length = size; + script->text.start = (u_char *) script + sizeof(nxt_script_t); + + nxt_memcpy(script->text.start, data, size); + + njs_vm_destroy(vm); + + return script; + +fail: + + njs_vm_destroy(vm); + + return NULL; +} + + +static nxt_script_t * +nxt_script_get(nxt_task_t *task, nxt_str_t *name, nxt_fd_t fd) +{ + nxt_int_t ret; + nxt_str_t text; + nxt_script_t *script; + u_char error[NXT_MAX_ERROR_STR]; + + ret = nxt_script_file_read(fd, &text); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + + script = nxt_script_new(task, name, text.start, text.length, error); + + nxt_free(text.start); + + return script; +} + + +void +nxt_script_destroy(nxt_script_t *script) +{ + nxt_free(script); +} + + +static nxt_int_t +nxt_script_info_hash_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + nxt_script_info_t *info; + + info = data; + + if (nxt_strcasestr_eq(&lhq->key, &info->name)) { + return NXT_OK; + } + + return NXT_DECLINED; +} + + +static const nxt_lvlhsh_proto_t nxt_script_info_hash_proto + nxt_aligned(64) = +{ + NXT_LVLHSH_DEFAULT, + nxt_script_info_hash_test, + nxt_lvlhsh_alloc, + nxt_lvlhsh_free, +}; + + +void +nxt_script_info_init(nxt_task_t *task, nxt_array_t *scripts) +{ + uint32_t i; + nxt_script_t *script; + nxt_script_item_t *item; + + item = scripts->elts; + + for (i = 0; i < scripts->nelts; i++) { + script = nxt_script_get(task, &item->name, item->fd); + + if (nxt_slow_path(script == NULL)) { + continue; + } + + (void) nxt_script_info_save(&item->name, script); + + nxt_script_destroy(script); + + item++; + } +} + + +nxt_int_t +nxt_script_info_save(nxt_str_t *name, nxt_script_t *script) +{ + nxt_mp_t *mp; + nxt_int_t ret; + nxt_conf_value_t *value; + nxt_script_info_t *info; + nxt_lvlhsh_query_t lhq; + + mp = nxt_mp_create(1024, 128, 256, 32); + if (nxt_slow_path(mp == NULL)) { + return NXT_ERROR; + } + + info = nxt_mp_get(mp, sizeof(nxt_script_info_t)); + if (nxt_slow_path(info == NULL)) { + goto fail; + } + + name = nxt_str_dup(mp, &info->name, name); + if (nxt_slow_path(name == NULL)) { + goto fail; + } + + value = nxt_script_details(mp, script); + if (nxt_slow_path(value == NULL)) { + goto fail; + } + + info->mp = mp; + info->value = value; + + lhq.key_hash = nxt_djb_hash(name->start, name->length); + lhq.replace = 1; + lhq.key = *name; + lhq.value = info; + lhq.proto = &nxt_script_info_hash_proto; + + ret = nxt_lvlhsh_insert(&nxt_script_info, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + + if (lhq.value != info) { + info = lhq.value; + nxt_mp_destroy(info->mp); + } + + return NXT_OK; + +fail: + + nxt_mp_destroy(mp); + return NXT_ERROR; +} + + +nxt_conf_value_t * +nxt_script_info_get(nxt_str_t *name) +{ + nxt_int_t ret; + nxt_script_info_t *info; + nxt_lvlhsh_query_t lhq; + + lhq.key_hash = nxt_djb_hash(name->start, name->length); + lhq.key = *name; + lhq.proto = &nxt_script_info_hash_proto; + + ret = nxt_lvlhsh_find(&nxt_script_info, &lhq); + if (ret != NXT_OK) { + return NULL; + } + + info = lhq.value; + + return info->value; +} + + +nxt_conf_value_t * +nxt_script_info_get_all(nxt_mp_t *mp) +{ + uint32_t i; + nxt_conf_value_t *all; + nxt_script_info_t *info; + nxt_lvlhsh_each_t lhe; + + nxt_lvlhsh_each_init(&lhe, &nxt_script_info_hash_proto); + + for (i = 0; /* void */; i++) { + info = nxt_lvlhsh_each(&nxt_script_info, &lhe); + + if (info == NULL) { + break; + } + } + + all = nxt_conf_create_object(mp, i); + if (nxt_slow_path(all == NULL)) { + return NULL; + } + + nxt_lvlhsh_each_init(&lhe, &nxt_script_info_hash_proto); + + for (i = 0; /* void */; i++) { + info = nxt_lvlhsh_each(&nxt_script_info, &lhe); + + if (info == NULL) { + break; + } + + nxt_conf_set_member(all, &info->name, info->value, i); + } + + return all; +} + + +static nxt_conf_value_t * +nxt_script_details(nxt_mp_t *mp, nxt_script_t *script) +{ + nxt_conf_value_t *value; + + value = nxt_conf_create_object(mp, 0); + if (nxt_slow_path(value == NULL)) { + return NULL; + } + + nxt_conf_set_string_dup(value, mp, &script->text); + + return value; +} + + +nxt_int_t +nxt_script_info_delete(nxt_str_t *name) +{ + nxt_int_t ret; + nxt_script_info_t *info; + nxt_lvlhsh_query_t lhq; + + lhq.key_hash = nxt_djb_hash(name->start, name->length); + lhq.key = *name; + lhq.proto = &nxt_script_info_hash_proto; + + ret = nxt_lvlhsh_delete(&nxt_script_info, &lhq); + + if (ret == NXT_OK) { + info = lhq.value; + nxt_mp_destroy(info->mp); + } + + return ret; +} + + +nxt_array_t * +nxt_script_store_load(nxt_task_t *task, nxt_mp_t *mp) +{ + DIR *dir; + size_t size, alloc; + u_char *buf, *p; + nxt_str_t name; + nxt_int_t ret; + nxt_file_t file; + nxt_array_t *scripts; + nxt_runtime_t *rt; + struct dirent *de; + nxt_script_item_t *item; + + rt = task->thread->runtime; + + if (nxt_slow_path(rt->scripts.start == NULL)) { + nxt_alert(task, "no scripts storage directory"); + return NULL; + } + + scripts = nxt_array_create(mp, 16, sizeof(nxt_script_item_t)); + if (nxt_slow_path(scripts == NULL)) { + return NULL; + } + + buf = NULL; + alloc = 0; + + dir = opendir((char *) rt->scripts.start); + if (nxt_slow_path(dir == NULL)) { + nxt_alert(task, "opendir(\"%s\") failed %E", + rt->scripts.start, nxt_errno); + goto fail; + } + + for ( ;; ) { + de = readdir(dir); + if (de == NULL) { + break; + } + + nxt_debug(task, "readdir(\"%s\"): \"%s\"", + rt->scripts.start, de->d_name); + + name.length = nxt_strlen(de->d_name); + name.start = (u_char *) de->d_name; + + if (nxt_str_eq(&name, ".", 1) || nxt_str_eq(&name, "..", 2)) { + continue; + } + + item = nxt_array_add(scripts); + if (nxt_slow_path(item == NULL)) { + goto fail; + } + + item->fd = -1; + + size = rt->scripts.length + name.length + 1; + + if (size > alloc) { + size += 32; + + p = nxt_realloc(buf, size); + if (p == NULL) { + goto fail; + } + + alloc = size; + buf = p; + } + + p = nxt_cpymem(buf, rt->scripts.start, rt->scripts.length); + p = nxt_cpymem(p, name.start, name.length + 1); + + nxt_memzero(&file, sizeof(nxt_file_t)); + + file.name = buf; + + ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, + NXT_FILE_OWNER_ACCESS); + + if (nxt_slow_path(ret != NXT_OK)) { + nxt_array_remove_last(scripts); + continue; + } + + item->fd = file.fd; + + if (nxt_slow_path(nxt_str_dup(mp, &item->name, &name) == NULL)) { + goto fail; + } + } + + if (buf != NULL) { + nxt_free(buf); + } + + (void) closedir(dir); + + return scripts; + +fail: + + if (buf != NULL) { + nxt_free(buf); + } + + if (dir != NULL) { + (void) closedir(dir); + } + + nxt_script_store_release(scripts); + + return NULL; +} + + +void +nxt_script_store_release(nxt_array_t *scripts) +{ + uint32_t i; + nxt_script_item_t *item; + + item = scripts->elts; + + for (i = 0; i < scripts->nelts; i++) { + nxt_fd_close(item[i].fd); + } + + nxt_array_destroy(scripts); +} + + +void +nxt_script_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp, + nxt_port_rpc_handler_t handler, void *ctx) +{ + uint32_t stream; + nxt_int_t ret; + nxt_buf_t *b; + nxt_port_t *main_port, *recv_port; + nxt_runtime_t *rt; + + b = nxt_buf_mem_alloc(mp, name->length + 1, 0); + if (nxt_slow_path(b == NULL)) { + goto fail; + } + + nxt_mp_retain(mp); + b->completion_handler = nxt_script_buf_completion; + + nxt_buf_cpystr(b, name); + *b->mem.free++ = '\0'; + + rt = task->thread->runtime; + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + recv_port = rt->port_by_type[rt->type]; + + stream = nxt_port_rpc_register_handler(task, recv_port, handler, handler, + -1, ctx); + if (nxt_slow_path(stream == 0)) { + goto fail; + } + + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_SCRIPT_GET, -1, + stream, recv_port->id, b); + + if (nxt_slow_path(ret != NXT_OK)) { + nxt_port_rpc_cancel(task, recv_port, stream); + goto fail; + } + + return; + +fail: + + handler(task, NULL, ctx); +} + + +static void +nxt_script_buf_completion(nxt_task_t *task, void *obj, void *data) +{ + nxt_mp_t *mp; + nxt_buf_t *b; + + b = obj; + mp = b->data; + nxt_assert(b->next == NULL); + + nxt_mp_free(mp, b); + nxt_mp_release(mp); +} + + +void +nxt_script_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + u_char *p; + nxt_int_t ret; + nxt_str_t name; + nxt_file_t file; + nxt_port_t *port; + nxt_runtime_t *rt; + nxt_port_msg_type_t type; + + port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid, + msg->port_msg.reply_port); + + if (nxt_slow_path(port == NULL)) { + nxt_alert(task, "process port not found (pid %PI, reply_port %d)", + msg->port_msg.pid, msg->port_msg.reply_port); + return; + } + + if (nxt_slow_path(port->type != NXT_PROCESS_CONTROLLER + && port->type != NXT_PROCESS_ROUTER)) + { + nxt_alert(task, "process %PI cannot store scripts", + msg->port_msg.pid); + return; + } + + nxt_memzero(&file, sizeof(nxt_file_t)); + + file.fd = -1; + type = NXT_PORT_MSG_RPC_ERROR; + + rt = task->thread->runtime; + + if (nxt_slow_path(rt->certs.start == NULL)) { + nxt_alert(task, "no scripts storage directory"); + goto error; + } + + name.start = msg->buf->mem.pos; + name.length = nxt_strlen(name.start); + + file.name = nxt_malloc(rt->scripts.length + name.length + 1); + if (nxt_slow_path(file.name == NULL)) { + goto error; + } + + p = nxt_cpymem(file.name, rt->scripts.start, rt->scripts.length); + p = nxt_cpymem(p, name.start, name.length + 1); + + ret = nxt_file_open(task, &file, NXT_FILE_RDWR, NXT_FILE_CREATE_OR_OPEN, + NXT_FILE_OWNER_ACCESS); + + nxt_free(file.name); + + if (nxt_fast_path(ret == NXT_OK)) { + type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD; + } + +error: + + (void) nxt_port_socket_write(task, port, type, file.fd, + msg->port_msg.stream, 0, NULL); +} + + +void +nxt_script_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp) +{ + nxt_buf_t *b; + nxt_port_t *main_port; + nxt_runtime_t *rt; + + b = nxt_buf_mem_alloc(mp, name->length + 1, 0); + + if (nxt_fast_path(b != NULL)) { + nxt_buf_cpystr(b, name); + *b->mem.free++ = '\0'; + + rt = task->thread->runtime; + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + + (void) nxt_port_socket_write(task, main_port, + NXT_PORT_MSG_SCRIPT_DELETE, -1, 0, 0, b); + } +} + + +void +nxt_script_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + u_char *p; + nxt_str_t name; + nxt_port_t *ctl_port; + nxt_runtime_t *rt; + nxt_file_name_t *path; + + rt = task->thread->runtime; + ctl_port = rt->port_by_type[NXT_PROCESS_CONTROLLER]; + + if (nxt_slow_path(ctl_port == NULL)) { + nxt_alert(task, "controller port not found"); + return; + } + + if (nxt_slow_path(nxt_recv_msg_cmsg_pid(msg) != ctl_port->pid)) { + nxt_alert(task, "process %PI cannot delete scripts", + nxt_recv_msg_cmsg_pid(msg)); + return; + } + + if (nxt_slow_path(rt->scripts.start == NULL)) { + nxt_alert(task, "no scripts storage directory"); + return; + } + + name.start = msg->buf->mem.pos; + name.length = nxt_strlen(name.start); + + path = nxt_malloc(rt->scripts.length + name.length + 1); + + if (nxt_fast_path(path != NULL)) { + p = nxt_cpymem(path, rt->scripts.start, rt->scripts.length); + p = nxt_cpymem(p, name.start, name.length + 1); + + (void) nxt_file_delete(path); + + nxt_free(path); + } +} + + +nxt_int_t +nxt_script_file_read(nxt_fd_t fd, nxt_str_t *str) +{ + ssize_t n; + nxt_int_t ret; + nxt_file_t file; + nxt_file_info_t fi; + + nxt_memzero(&file, sizeof(nxt_file_t)); + + file.fd = fd; + + ret = nxt_file_info(&file, &fi); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + if (nxt_slow_path(!nxt_is_file(&fi))) { + nxt_str_null(str); + return NXT_DECLINED; + } + + str->length = nxt_file_size(&fi); + str->start = nxt_malloc(str->length); + if (nxt_slow_path(str->start == NULL)) { + return NXT_ERROR; + } + + n = nxt_file_read(&file, str->start, str->length, 0); + + if (nxt_slow_path(n != (ssize_t) str->length)) { + nxt_free(str->start); + return NXT_ERROR; + } + + return NXT_OK; +} diff --git a/src/nxt_script.h b/src/nxt_script.h new file mode 100644 index 00000000..ffefc108 --- /dev/null +++ b/src/nxt_script.h @@ -0,0 +1,37 @@ + +/* + * Copyright (C) NGINX, Inc. + * Copyright (C) Zhidao HONG + */ + +#ifndef _NXT_SCRIPT_INCLUDED_ +#define _NXT_SCRIPT_INCLUDED_ + + +typedef struct nxt_script_s nxt_script_t; + +nxt_script_t *nxt_script_new(nxt_task_t *task, nxt_str_t *name, u_char *data, + size_t size, u_char *error); +void nxt_script_destroy(nxt_script_t *script); + +void nxt_script_info_init(nxt_task_t *task, nxt_array_t *scripts); +nxt_int_t nxt_script_info_save(nxt_str_t *name, nxt_script_t *script); +nxt_conf_value_t *nxt_script_info_get(nxt_str_t *name); +nxt_conf_value_t *nxt_script_info_get_all(nxt_mp_t *mp); +nxt_int_t nxt_script_info_delete(nxt_str_t *name); + +nxt_array_t *nxt_script_store_load(nxt_task_t *task, nxt_mp_t *mem_pool); +void nxt_script_store_release(nxt_array_t *scripts); + +void nxt_script_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp, + nxt_port_rpc_handler_t handler, void *ctx); +void nxt_script_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp); + +void nxt_script_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); +void nxt_script_store_delete_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg); + +nxt_int_t nxt_script_file_read(nxt_fd_t fd, nxt_str_t *str); + + +#endif /* _NXT_SCRIPT_INCLUDED_ */ diff --git a/src/nxt_tstr.c b/src/nxt_tstr.c index c439696e..516415d9 100644 --- a/src/nxt_tstr.c +++ b/src/nxt_tstr.c @@ -70,7 +70,7 @@ nxt_tstr_state_new(nxt_mp_t *mp, nxt_bool_t test) } #if (NXT_HAVE_NJS) - state->jcf = nxt_js_conf_new(mp); + state->jcf = nxt_js_conf_new(mp, test); if (nxt_slow_path(state->jcf == NULL)) { return NULL; } @@ -273,7 +273,8 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr, } else { #if (NXT_HAVE_NJS) - ret = nxt_js_call(task, &query->cache->js, tstr->u.js, val, query->ctx); + ret = nxt_js_call(task, query->state->jcf, &query->cache->js, + tstr->u.js, val, query->ctx); if (nxt_slow_path(ret != NXT_OK)) { query->failed = 1; |