/* * Copyright (C) NGINX, Inc. */ #include struct nxt_js_s { uint32_t index; njs_vm_t *vm; }; struct nxt_js_conf_s { nxt_mp_t *pool; njs_vm_t *vm; njs_uint_t protos; njs_external_t *proto; nxt_array_t *funcs; }; njs_int_t nxt_js_proto_id; nxt_js_conf_t * nxt_js_conf_new(nxt_mp_t *mp) { njs_vm_opt_t opts; nxt_js_conf_t *jcf; jcf = nxt_mp_zget(mp, sizeof(nxt_js_conf_t)); if (nxt_slow_path(jcf == NULL)) { return NULL; } jcf->pool = mp; njs_vm_opt_init(&opts); jcf->vm = njs_vm_create(&opts); if (nxt_slow_path(jcf->vm == 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; } return jcf; } void nxt_js_conf_release(nxt_js_conf_t *jcf) { njs_vm_destroy(jcf->vm); } void nxt_js_set_proto(nxt_js_conf_t *jcf, njs_external_t *proto, njs_uint_t n) { jcf->protos = n; jcf->proto = proto; } nxt_js_t * nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz) { size_t size; u_char *p, *start; nxt_js_t *js; nxt_str_t *func; static nxt_str_t func_str = nxt_string("function(uri, host, remoteAddr, " "args, headers, cookies) {" " return "); /* * Appending a terminating null character if strz is true. */ static nxt_str_t strz_str = nxt_string(" + '\\x00'"); size = func_str.length + str->length + 1; if (strz) { size += strz_str.length; } start = nxt_mp_nget(jcf->pool, size); if (nxt_slow_path(start == NULL)) { return NULL; } p = start; p = nxt_cpymem(p, func_str.start, func_str.length); p = nxt_cpymem(p, str->start, str->length); if (strz) { p = nxt_cpymem(p, strz_str.start, strz_str.length); } *p++ = '}'; js = nxt_mp_get(jcf->pool, sizeof(nxt_js_t)); if (nxt_slow_path(js == NULL)) { return NULL; } js->vm = jcf->vm; func = nxt_array_add(jcf->funcs); if (nxt_slow_path(func == NULL)) { return NULL; } func->start = start; func->length = p - start; js->index = jcf->funcs->nelts - 1; return js; } nxt_int_t nxt_js_compile(nxt_js_conf_t *jcf) { size_t size; u_char *p, *start; njs_int_t ret; nxt_str_t *func; nxt_uint_t i; size = 2; func = jcf->funcs->elts; for (i = 0; i < jcf->funcs->nelts; i++) { size += func[i].length + 1; } start = nxt_mp_nget(jcf->pool, size); if (nxt_slow_path(start == NULL)) { return NXT_ERROR; } p = start; *p++ = '['; func = jcf->funcs->elts; for (i = 0; i < jcf->funcs->nelts; i++) { p = nxt_cpymem(p, func[i].start, func[i].length); *p++ = ','; } *p++ = ']'; nxt_js_proto_id = njs_vm_external_prototype(jcf->vm, jcf->proto, jcf->protos); if (nxt_slow_path(nxt_js_proto_id < 0)) { return NXT_ERROR; } ret = njs_vm_compile(jcf->vm, &start, p); return (ret == NJS_OK) ? NXT_OK : NXT_ERROR; } 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_int_t ret; njs_str_t res; start = nxt_mp_nget(jcf->pool, str->length); if (nxt_slow_path(start == NULL)) { return NXT_ERROR; } nxt_memcpy(start, str->start, str->length); ret = njs_vm_compile(jcf->vm, &start, start + str->length); if (nxt_slow_path(ret != NJS_OK)) { (void) njs_vm_retval_string(jcf->vm, &res); err.start = res.start; err.length = res.length; nxt_sprintf(error, error + NXT_MAX_ERROR_STR, "\"%V\"%Z", &err); return NXT_ERROR; } return NXT_OK; } 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) { njs_vm_t *vm; njs_int_t rc, ret; njs_str_t res; njs_value_t *array, *value; njs_function_t *func; njs_opaque_value_t opaque_value, arguments[6]; static const njs_str_t uri_str = njs_str("uri"); static const njs_str_t host_str = njs_str("host"); static const njs_str_t remote_addr_str = njs_str("remoteAddr"); static const njs_str_t args_str = njs_str("args"); static const njs_str_t headers_str = njs_str("headers"); static const njs_str_t cookies_str = njs_str("cookies"); vm = cache->vm; if (vm == NULL) { vm = njs_vm_clone(js->vm, ctx); if (nxt_slow_path(vm == NULL)) { return NXT_ERROR; } ret = njs_vm_start(vm); 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); func = njs_value_function(value); ret = njs_vm_external_create(vm, njs_value_arg(&opaque_value), nxt_js_proto_id, ctx, 0); if (nxt_slow_path(ret != NJS_OK)) { return NXT_ERROR; } value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &uri_str, &arguments[0]); if (nxt_slow_path(value == NULL)) { return NXT_ERROR; } value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &host_str, &arguments[1]); if (nxt_slow_path(value == NULL)) { return NXT_ERROR; } value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &remote_addr_str, &arguments[2]); if (nxt_slow_path(value == NULL)) { return NXT_ERROR; } value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &args_str, &arguments[3]); if (nxt_slow_path(value == NULL)) { return NXT_ERROR; } value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &headers_str, &arguments[4]); if (nxt_slow_path(value == NULL)) { return NXT_ERROR; } value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &cookies_str, &arguments[5]); if (nxt_slow_path(value == NULL)) { 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; } if (ret != NJS_OK) { nxt_alert(task, "js exception: %V", &res); return NXT_ERROR; } str->length = res.length; str->start = res.start; return NXT_OK; } void nxt_js_release(nxt_js_cache_t *cache) { if (cache->vm != NULL) { njs_vm_destroy(cache->vm); } }