diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 7f2eded24..35f988d08 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -48,14 +48,13 @@ typedef struct { #define NJS_HEADER_ARRAY 0x4 -typedef struct { +typedef struct ngx_http_js_ctx_s ngx_http_js_ctx_t; + +struct ngx_http_js_ctx_s { NGX_JS_COMMON_CTX; - ngx_log_t *log; ngx_uint_t done; ngx_int_t status; - njs_opaque_value_t retval; - njs_opaque_value_t request; - njs_opaque_value_t args; + njs_opaque_value_t rargs; njs_opaque_value_t request_body; njs_opaque_value_t response_body; ngx_str_t redirect_uri; @@ -65,9 +64,13 @@ typedef struct { ngx_chain_t **last_out; ngx_chain_t *free; ngx_chain_t *busy; + ngx_int_t (*body_filter)(ngx_http_request_t *r, + ngx_http_js_loc_conf_t *jlcf, + ngx_http_js_ctx_t *ctx, + ngx_chain_t *in); ngx_js_periodic_t *periodic; -} ngx_http_js_ctx_t; +}; typedef struct { @@ -299,6 +302,11 @@ static ngx_flag_t ngx_http_js_ssl_verify(ngx_http_request_t *r); static ngx_int_t ngx_http_js_parse_unsafe_uri(ngx_http_request_t *r, njs_str_t *uri, njs_str_t *args); +static ngx_conf_bitmask_t ngx_http_js_engines[] = { + { ngx_string("njs"), NGX_ENGINE_NJS }, + { ngx_null_string, 0 } +}; + #if (NGX_HTTP_SSL) static ngx_conf_bitmask_t ngx_http_js_ssl_protocols[] = { @@ -313,6 +321,13 @@ static ngx_conf_bitmask_t ngx_http_js_ssl_protocols[] = { static ngx_command_t ngx_http_js_commands[] = { + { ngx_string("js_engine"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_js_engine, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_js_loc_conf_t, type), + &ngx_http_js_engines }, + { ngx_string("js_import"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, ngx_js_import, @@ -960,8 +975,8 @@ ngx_http_js_content_event_handler(ngx_http_request_t *r) ctx->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - rc = ngx_js_name_call(ctx->vm, &jlcf->content, r->connection->log, - &ctx->request, 1); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->content, &ctx->args[0], + 1); if (rc == NGX_ERROR) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -990,7 +1005,7 @@ ngx_http_js_content_write_event_handler(ngx_http_request_t *r) ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - if (!ngx_vm_pending(ctx)) { + if (!ngx_js_ctx_pending(ctx)) { ngx_http_js_content_finalize(r, ctx); return; } @@ -1086,13 +1101,13 @@ ngx_http_js_header_filter(ngx_http_request_t *r) ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); ctx->filter = 1; - pending = ngx_vm_pending(ctx); + pending = ngx_js_ctx_pending(ctx); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js header call \"%V\"", &jlcf->header_filter); - rc = ngx_js_name_call(ctx->vm, &jlcf->header_filter, r->connection->log, - &ctx->request, 1); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->header_filter, + &ctx->args[0], 1); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -1110,51 +1125,28 @@ ngx_http_js_header_filter(ngx_http_request_t *r) static ngx_int_t -ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +ngx_http_njs_body_filter(ngx_http_request_t *r, ngx_http_js_loc_conf_t *jlcf, + ngx_http_js_ctx_t *ctx, ngx_chain_t *in) { - size_t len; - u_char *p; - ngx_int_t rc; - njs_int_t ret, pending; - ngx_buf_t *b; - ngx_chain_t *out, *cl; - ngx_connection_t *c; - ngx_http_js_ctx_t *ctx; - njs_opaque_value_t last_key, last; - ngx_http_js_loc_conf_t *jlcf; - njs_opaque_value_t arguments[3]; + size_t len; + u_char *p; + njs_vm_t *vm; + ngx_int_t rc; + njs_int_t ret, pending; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_connection_t *c; + njs_opaque_value_t last_key, last; + njs_opaque_value_t arguments[3]; static const njs_str_t last_str = njs_str("last"); - jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); - - if (jlcf->body_filter.len == 0 || in == NULL) { - return ngx_http_next_body_filter(r, in); - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http js body filter"); - - rc = ngx_http_js_init_vm(r, ngx_http_js_request_proto_id); - - if (rc == NGX_ERROR || rc == NGX_DECLINED) { - return NGX_ERROR; - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - - if (ctx->done) { - return ngx_http_next_body_filter(r, in); - } - c = r->connection; + vm = ctx->engine->u.njs.vm; - ctx->filter = 1; - ctx->last_out = &out; + njs_value_assign(&arguments[0], &ctx->args[0]); - njs_value_assign(&arguments[0], &ctx->request); - - njs_vm_value_string_create(ctx->vm, njs_value_arg(&last_key), + njs_vm_value_string_create(vm, njs_value_arg(&last_key), last_str.start, last_str.length); while (in != NULL) { @@ -1166,7 +1158,7 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in) p = ngx_pnalloc(r->pool, len); if (p == NULL) { - njs_vm_memory_error(ctx->vm); + njs_vm_memory_error(vm); return NJS_ERROR; } @@ -1174,7 +1166,7 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_memcpy(p, b->pos, len); } - ret = ngx_js_prop(ctx->vm, jlcf->buffer_type, + ret = ngx_js_prop(vm, jlcf->buffer_type, njs_value_arg(&arguments[1]), p, len); if (ret != NJS_OK) { return ret; @@ -1182,20 +1174,20 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in) njs_value_boolean_set(njs_value_arg(&last), b->last_buf); - ret = njs_vm_object_alloc(ctx->vm, njs_value_arg(&arguments[2]), + ret = njs_vm_object_alloc(vm, njs_value_arg(&arguments[2]), njs_value_arg(&last_key), njs_value_arg(&last), NULL); if (ret != NJS_OK) { return ret; } - pending = ngx_vm_pending(ctx); + pending = ngx_js_ctx_pending(ctx); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http js body call \"%V\"", &jlcf->body_filter); - rc = ngx_js_name_call(ctx->vm, &jlcf->body_filter, c->log, - &arguments[0], 3); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->body_filter, + &arguments[0], 3); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -1225,13 +1217,54 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in) in = in->next; } + return NGX_OK; +} + + +static ngx_int_t +ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_int_t rc; + ngx_chain_t *out; + ngx_http_js_ctx_t *ctx; + ngx_http_js_loc_conf_t *jlcf; + + jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); + + if (jlcf->body_filter.len == 0 || in == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http js body filter"); + + rc = ngx_http_js_init_vm(r, ngx_http_js_request_proto_id); + + if (rc == NGX_ERROR || rc == NGX_DECLINED) { + return NGX_ERROR; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + if (ctx->done) { + return ngx_http_next_body_filter(r, in); + } + + ctx->filter = 1; + ctx->last_out = &out; + + rc = ctx->body_filter(r, jlcf, ctx, in); + if (rc != NGX_OK) { + return NGX_ERROR; + } + *ctx->last_out = NULL; - if (out != NULL || c->buffered) { + if (out != NULL || r->connection->buffered) { rc = ngx_http_next_body_filter(r, out); - ngx_chain_update_chains(c->pool, &ctx->free, &ctx->busy, &out, - (ngx_buf_tag_t) &ngx_http_js_module); + ngx_chain_update_chains(r->connection->pool, &ctx->free, &ctx->busy, + &out, (ngx_buf_tag_t) &ngx_http_js_module); } else { rc = NGX_OK; @@ -1249,8 +1282,7 @@ ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, ngx_int_t rc; njs_int_t pending; - ngx_str_t *fname; - njs_str_t value; + ngx_str_t *fname, value; ngx_http_js_ctx_t *ctx; fname = &vdata->fname; @@ -1271,10 +1303,9 @@ ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - pending = ngx_vm_pending(ctx); + pending = ngx_js_ctx_pending(ctx); - rc = ngx_js_name_invoke(ctx->vm, fname, r->connection->log, &ctx->request, - 1, &ctx->retval); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, fname, &ctx->args[0], 1); if (rc == NGX_ERROR) { v->not_found = 1; @@ -1287,15 +1318,15 @@ ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, return NGX_ERROR; } - if (ngx_js_string(ctx->vm, njs_value_arg(&ctx->retval), &value) != NGX_OK) { + if (ctx->engine->string(ctx->engine, &ctx->retval, &value) != NGX_OK) { return NGX_ERROR; } - v->len = value.length; + v->len = value.len; v->valid = 1; v->no_cacheable = vdata->flags & NGX_NJS_VAR_NOCACHE; v->not_found = 0; - v->data = value.start; + v->data = value.data; return NGX_OK; } @@ -1331,18 +1362,12 @@ ngx_http_js_variable_var(ngx_http_request_t *r, ngx_http_variable_value_t *v, static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id) { - njs_int_t rc; - ngx_str_t exception; - njs_str_t key; - ngx_uint_t i; ngx_http_js_ctx_t *ctx; - njs_opaque_value_t retval; ngx_pool_cleanup_t *cln; - ngx_js_named_path_t *preload; ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); - if (jlcf->vm == NULL) { + if (jlcf->engine == NULL) { return NGX_DECLINED; } @@ -1354,71 +1379,33 @@ ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id) return NGX_ERROR; } - ngx_js_ctx_init((ngx_js_ctx_t *) ctx); - - njs_value_invalid_set(njs_value_arg(&ctx->retval)); + ngx_js_ctx_init((ngx_js_ctx_t *) ctx, r->connection->log); ngx_http_set_ctx(r, ctx, ngx_http_js_module); } - if (ctx->vm) { + if (ctx->engine) { return NGX_OK; } - ctx->vm = njs_vm_clone(jlcf->vm, r); - if (ctx->vm == NULL) { + ctx->engine = jlcf->engine->clone((ngx_js_ctx_t *) ctx, + (ngx_js_loc_conf_t *) jlcf, proto_id, r); + if (ctx->engine == NULL) { return NGX_ERROR; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http js vm clone: %p from: %p", ctx->vm, jlcf->vm); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "http js vm clone %s: %p from: %p", jlcf->engine->name, + ctx->engine, jlcf->engine); cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_ERROR; } - ctx->log = r->connection->log; - cln->handler = ngx_http_js_cleanup_ctx; cln->data = ctx; - /* bind objects from preload vm */ - - if (jlcf->preload_objects != NGX_CONF_UNSET_PTR) { - preload = jlcf->preload_objects->elts; - - for (i = 0; i < jlcf->preload_objects->nelts; i++) { - key.start = preload[i].name.data; - key.length = preload[i].name.len; - - rc = njs_vm_value(jlcf->preload_vm, &key, njs_value_arg(&retval)); - if (rc != NJS_OK) { - return NGX_ERROR; - } - - rc = njs_vm_bind(ctx->vm, &key, njs_value_arg(&retval), 0); - if (rc != NJS_OK) { - return NGX_ERROR; - } - } - } - - if (njs_vm_start(ctx->vm, njs_value_arg(&retval)) == NJS_ERROR) { - ngx_js_exception(ctx->vm, &exception); - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "js exception: %V", &exception); - - return NGX_ERROR; - } - - rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->request), - proto_id, r, 0); - if (rc != NJS_OK) { - return NGX_ERROR; - } - return NGX_OK; } @@ -1426,16 +1413,22 @@ ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id) static void ngx_http_js_cleanup_ctx(void *data) { - ngx_http_js_ctx_t *ctx = data; + ngx_http_request_t *r; + ngx_http_js_loc_conf_t *jlcf; + + ngx_http_js_ctx_t *ctx = data; - if (ngx_vm_pending(ctx)) { + if (ngx_js_ctx_pending(ctx)) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events"); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http js vm destroy: %p", - ctx->vm); + ctx->engine); + + r = ngx_js_ctx_external(ctx); + jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); - ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx); + ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx, (ngx_js_loc_conf_t *) jlcf); } @@ -2657,7 +2650,7 @@ ngx_http_js_ext_get_args(njs_vm_t *vm, njs_object_prop_t *prop, ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - args = njs_value_arg(&ctx->args); + args = njs_value_arg(&ctx->rargs); if (njs_value_is_null(args)) { data = (r->args.len != 0) ? r->args.data : (u_char *) ""; @@ -3109,9 +3102,8 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, ngx_str_t uri, rargs; ngx_uint_t method, methods_max, has_body, detached, promise; - njs_value_t *value, *arg, *options; + njs_value_t *value, *arg, *options, *callback; ngx_js_event_t *event; - njs_function_t *callback; ngx_http_js_ctx_t *ctx; njs_opaque_value_t lvalue; ngx_http_request_t *r, *sr; @@ -3168,7 +3160,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } } else if (njs_value_is_function(arg)) { - callback = njs_value_function(arg); + callback = arg; } else if (njs_value_is_object(arg)) { options = arg; @@ -3237,7 +3229,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } else { - callback = njs_value_function(arg); + callback = arg; } } @@ -3254,35 +3246,33 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, if (!detached) { ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (ps == NULL) { - njs_vm_memory_error(ctx->vm); + njs_vm_memory_error(vm); return NJS_ERROR; } promise = !!(callback == NULL); - event = njs_mp_zalloc(njs_vm_memory_pool(ctx->vm), + event = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(ngx_js_event_t) + promise * (sizeof(njs_opaque_value_t) * 2)); if (njs_slow_path(event == NULL)) { - njs_vm_memory_error(ctx->vm); + njs_vm_memory_error(vm); return NJS_ERROR; } - event->vm = ctx->vm; event->fd = ctx->event_id++; if (promise) { - event->args = (njs_value_t *) &event[1]; - rc = njs_vm_promise_create(ctx->vm, retval, - njs_value_arg(event->args)); + event->args = (njs_opaque_value_t *) &event[1]; + rc = njs_vm_promise_create(vm, retval, njs_value_arg(event->args)); if (rc != NJS_OK) { return NJS_ERROR; } - callback = njs_value_function(njs_value_arg(event->args)); + callback = njs_value_arg(event->args); } - event->function = callback; + njs_value_assign(&event->function, callback); ps->handler = ngx_http_js_subrequest_done; ps->data = event; @@ -3303,7 +3293,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, if (ngx_http_subrequest(r, &uri, rargs.len ? &rargs : NULL, &sr, ps, flags) != NGX_OK) { - njs_vm_error(ctx->vm, "subrequest creation failed"); + njs_vm_error(vm, "subrequest creation failed"); return NJS_ERROR; } @@ -3358,7 +3348,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, memory_error: - njs_vm_error(ctx->vm, "internal error"); + njs_vm_error(vm, "internal error"); return NJS_ERROR; } @@ -3370,6 +3360,7 @@ ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc) ngx_js_event_t *event = data; njs_int_t ret; + njs_vm_t *vm; ngx_http_js_ctx_t *ctx; njs_opaque_value_t reply; @@ -3407,7 +3398,9 @@ ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc) return NGX_ERROR; } - ret = njs_vm_external_create(ctx->vm, njs_value_arg(&reply), + vm = ctx->engine->u.njs.vm; + + ret = njs_vm_external_create(vm, njs_value_arg(&reply), ngx_http_js_request_proto_id, r, 0); if (ret != NJS_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, @@ -3416,7 +3409,8 @@ ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc) return NGX_ERROR; } - rc = ngx_js_call(ctx->vm, event->function, njs_value_arg(&reply), 1); + rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)), + &reply, 1); ngx_js_del_event(ctx, event); @@ -3447,7 +3441,7 @@ ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_object_prop_t *prop, return NJS_DECLINED; } - njs_value_assign(retval, njs_value_arg(&ctx->request)); + njs_value_assign(retval, njs_value_arg(&ctx->args[0])); return NJS_OK; } @@ -4182,8 +4176,8 @@ ngx_http_js_periodic_handler(ngx_event_t *ev) r->count++; - rc = ngx_js_name_invoke(ctx->vm, &periodic->method, &periodic->log, - &ctx->request, 1, &ctx->retval); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &periodic->method, + &ctx->args[0], 1); if (rc == NGX_AGAIN) { rc = NGX_OK; @@ -4210,7 +4204,7 @@ ngx_http_js_periodic_write_handler(ngx_event_t *ev) ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - if (!ngx_vm_pending(ctx)) { + if (!ngx_js_ctx_pending(ctx)) { ngx_http_js_periodic_finalize(r, NGX_OK); return; } @@ -4247,9 +4241,9 @@ ngx_http_js_periodic_finalize(ngx_http_request_t *r, ngx_int_t rc) ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js periodic finalize: \"%V\" rc: %i c: %i pending: %i", &ctx->periodic->method, rc, r->count, - ngx_vm_pending(ctx)); + ngx_js_ctx_pending(ctx)); - if (r->count > 1 || (rc == NGX_OK && ngx_vm_pending(ctx))) { + if (r->count > 1 || (rc == NGX_OK && ngx_js_ctx_pending(ctx))) { return; } @@ -4426,22 +4420,51 @@ ngx_js_http_init(njs_vm_t *vm) } +static ngx_engine_t * +ngx_engine_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, + njs_int_t proto_id, void *external) +{ + njs_int_t rc; + ngx_engine_t *engine; + ngx_http_js_ctx_t *hctx; + + engine = ngx_njs_clone(ctx, cf, external); + if (engine == NULL) { + return NULL; + } + + rc = njs_vm_external_create(engine->u.njs.vm, njs_value_arg(&ctx->args[0]), + proto_id, njs_vm_external_ptr(engine->u.njs.vm), + 0); + if (rc != NJS_OK) { + return NULL; + } + + hctx = (ngx_http_js_ctx_t *) ctx; + hctx->body_filter = ngx_http_njs_body_filter; + + return engine; +} + + static ngx_int_t ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) { - njs_vm_opt_t options; + ngx_engine_opts_t options; ngx_js_main_conf_t *jmcf; - njs_vm_opt_init(&options); + memset(&options, 0, sizeof(ngx_engine_opts_t)); - jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module); - ngx_http_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; + options.engine = conf->type; - options.backtrace = 1; - options.metas = &ngx_http_js_metas; - options.addons = njs_http_js_addon_modules; - options.argv = ngx_argv; - options.argc = ngx_argc; + if (conf->type == NGX_ENGINE_NJS) { + jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module); + ngx_http_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; + + options.u.njs.metas = &ngx_http_js_metas; + options.u.njs.addons = njs_http_js_addon_modules; + options.clone = ngx_engine_njs_clone; + } return ngx_js_init_conf_vm(cf, conf, &options); } diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 4c1f29f3a..ce4988f97 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -24,7 +24,8 @@ typedef struct { typedef struct { - void *promise; + void *promise_obj; + njs_opaque_value_t promise; njs_opaque_value_t message; } ngx_js_rejected_promise_t; @@ -43,6 +44,19 @@ typedef struct { } njs_module_info_t; +static ngx_int_t ngx_engine_njs_init(ngx_engine_t *engine, + ngx_engine_opts_t *opts); +static ngx_int_t ngx_engine_njs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, + u_char *start, size_t size); +static ngx_int_t ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname, + njs_opaque_value_t *args, njs_uint_t nargs); +static void *ngx_engine_njs_external(ngx_engine_t *engine); +static ngx_int_t ngx_engine_njs_pending(ngx_engine_t *engine); +static ngx_int_t ngx_engine_njs_string(ngx_engine_t *e, + njs_opaque_value_t *value, ngx_str_t *str); +static void ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, + ngx_js_loc_conf_t *conf); + static njs_int_t ngx_js_ext_build(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_conf_file_path(njs_vm_t *vm, @@ -70,6 +84,15 @@ static njs_int_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args, static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx); +static void ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused, + njs_bool_t is_handled, njs_value_t *promise, njs_value_t *reason); +static njs_mod_t *ngx_js_module_loader(njs_vm_t *vm, + njs_external_ptr_t external, njs_str_t *name); +static njs_int_t ngx_js_module_lookup(ngx_js_loc_conf_t *conf, + njs_module_info_t *info); +static njs_int_t ngx_js_module_read(njs_mp_t *mp, int fd, njs_str_t *text); +static njs_int_t ngx_js_set_cwd(njs_mp_t *mp, ngx_js_loc_conf_t *conf, + njs_str_t *path); static void ngx_js_cleanup_vm(void *data); static njs_int_t ngx_js_core_init(njs_vm_t *vm); @@ -354,82 +377,234 @@ njs_module_t *njs_js_addon_modules_shared[] = { static njs_int_t ngx_js_console_proto_id; -ngx_int_t -ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_value_t *args, - njs_uint_t nargs) +static ngx_engine_t * +ngx_create_engine(ngx_engine_opts_t *opts) { - njs_int_t ret; - ngx_str_t exception; - ngx_connection_t *c; + njs_mp_t *mp; + ngx_int_t rc; + ngx_engine_t *engine; - ret = njs_vm_call(vm, func, args, nargs); - if (ret == NJS_ERROR) { - ngx_js_exception(vm, &exception); + mp = njs_mp_fast_create(2 * getpagesize(), 128, 512, 16); + if (mp == NULL) { + return NULL; + } - c = ngx_external_connection(vm, njs_vm_external_ptr(vm)); + engine = njs_mp_zalloc(mp, sizeof(ngx_engine_t)); + if (engine == NULL) { + return NULL; + } - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "js exception: %V", &exception); + engine->pool = mp; + engine->clone = opts->clone; + + switch (opts->engine) { + case NGX_ENGINE_NJS: + rc = ngx_engine_njs_init(engine, opts); + if (rc != NGX_OK) { + return NULL; + } + + engine->name = "njs"; + engine->type = NGX_ENGINE_NJS; + engine->compile = ngx_engine_njs_compile; + engine->call = ngx_engine_njs_call; + engine->external = ngx_engine_njs_external; + engine->pending = ngx_engine_njs_pending; + engine->string = ngx_engine_njs_string; + engine->destroy = opts->destroy ? opts->destroy + : ngx_engine_njs_destroy; + break; + + default: + return NULL; + } + + return engine; +} + + +static ngx_int_t +ngx_engine_njs_init(ngx_engine_t *engine, ngx_engine_opts_t *opts) +{ + njs_vm_t *vm; + ngx_int_t rc; + njs_vm_opt_t vm_options; + + njs_vm_opt_init(&vm_options); + + vm_options.backtrace = 1; + vm_options.addons = opts->u.njs.addons; + vm_options.metas = opts->u.njs.metas; + vm_options.file = opts->file; + vm_options.argv = ngx_argv; + vm_options.argc = ngx_argc; + + vm = njs_vm_create(&vm_options); + if (vm == NULL) { return NGX_ERROR; } - for ( ;; ) { - ret = njs_vm_execute_pending_job(vm); - if (ret <= NJS_OK) { - c = ngx_external_connection(vm, njs_vm_external_ptr(vm)); + njs_vm_set_rejection_tracker(vm, ngx_js_rejection_tracker, NULL); - if (ret == NJS_ERROR) { - ngx_js_exception(vm, &exception); + rc = ngx_js_set_cwd(njs_vm_memory_pool(vm), opts->conf, &vm_options.file); + if (rc != NGX_OK) { + return NGX_ERROR; + } - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "js job exception: %V", &exception); - return NGX_ERROR; - } + njs_vm_set_module_loader(vm, ngx_js_module_loader, opts->conf); - break; + engine->u.njs.vm = vm; + + return NJS_OK; +} + + +static ngx_int_t +ngx_engine_njs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start, + size_t size) +{ + u_char *end; + njs_vm_t *vm; + njs_int_t rc; + njs_str_t text; + ngx_uint_t i; + njs_value_t *value; + njs_opaque_value_t exception, lvalue; + ngx_js_named_path_t *import; + + static const njs_str_t line_number_key = njs_str("lineNumber"); + static const njs_str_t file_name_key = njs_str("fileName"); + + vm = conf->engine->u.njs.vm; + end = start + size; + + rc = njs_vm_compile(vm, &start, end); + + if (rc != NJS_OK) { + njs_vm_exception_get(vm, njs_value_arg(&exception)); + njs_vm_value_string(vm, &text, njs_value_arg(&exception)); + + value = njs_vm_object_prop(vm, njs_value_arg(&exception), + &file_name_key, &lvalue); + if (value == NULL) { + value = njs_vm_object_prop(vm, njs_value_arg(&exception), + &line_number_key, &lvalue); + + if (value != NULL) { + i = njs_value_number(value) - 1; + + if (i < conf->imports->nelts) { + import = conf->imports->elts; + ngx_log_error(NGX_LOG_EMERG, log, 0, + "%*s, included in %s:%ui", text.length, + text.start, import[i].file, import[i].line); + return NGX_ERROR; + } + } } + + ngx_log_error(NGX_LOG_EMERG, log, 0, "%*s", text.length, text.start); + return NGX_ERROR; + } + + if (start != end) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "extra characters in js script: \"%*s\"", + end - start, start); + return NGX_ERROR; } return NGX_OK; } -ngx_int_t -ngx_js_name_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log, - njs_opaque_value_t *args, njs_uint_t nargs) +ngx_engine_t * +ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) { - njs_opaque_value_t unused; + njs_vm_t *vm; + njs_int_t rc; + njs_str_t key; + ngx_str_t exception; + ngx_uint_t i; + ngx_engine_t *engine; + njs_opaque_value_t retval; + ngx_js_named_path_t *preload; + + vm = njs_vm_clone(cf->engine->u.njs.vm, external); + if (vm == NULL) { + return NULL; + } + + engine = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(ngx_engine_t)); + if (engine == NULL) { + return NULL; + } - return ngx_js_name_invoke(vm, fname, log, args, nargs, &unused); + memcpy(engine, cf->engine, sizeof(ngx_engine_t)); + engine->pool = njs_vm_memory_pool(vm); + engine->u.njs.vm = vm; + + /* bind objects from preload vm */ + + if (cf->preload_objects != NGX_CONF_UNSET_PTR) { + preload = cf->preload_objects->elts; + + for (i = 0; i < cf->preload_objects->nelts; i++) { + key.start = preload[i].name.data; + key.length = preload[i].name.len; + + rc = njs_vm_value(cf->preload_vm, &key, njs_value_arg(&retval)); + if (rc != NJS_OK) { + return NULL; + } + + rc = njs_vm_bind(vm, &key, njs_value_arg(&retval), 0); + if (rc != NJS_OK) { + return NULL; + } + } + } + + if (njs_vm_start(vm, njs_value_arg(&retval)) == NJS_ERROR) { + ngx_js_exception(vm, &exception); + + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception); + + return NULL; + } + + return engine; } -ngx_int_t -ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log, - njs_opaque_value_t *args, njs_uint_t nargs, njs_opaque_value_t *retval) +static ngx_int_t +ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname, + njs_opaque_value_t *args, njs_uint_t nargs) { + njs_vm_t *vm; njs_int_t ret; njs_str_t name; ngx_str_t exception; - ngx_js_ctx_t *ctx; njs_function_t *func; name.start = fname->data; name.length = fname->len; + vm = ctx->engine->u.njs.vm; + func = njs_vm_function(vm, &name); if (func == NULL) { - ngx_log_error(NGX_LOG_ERR, log, 0, + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js function \"%V\" not found", fname); return NGX_ERROR; } ret = njs_vm_invoke(vm, func, njs_value_arg(args), nargs, - njs_value_arg(retval)); + njs_value_arg(&ctx->retval)); if (ret == NJS_ERROR) { ngx_js_exception(vm, &exception); - ngx_log_error(NGX_LOG_ERR, log, 0, + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception); return NGX_ERROR; @@ -441,7 +616,7 @@ ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log, if (ret == NJS_ERROR) { ngx_js_exception(vm, &exception); - ngx_log_error(NGX_LOG_ERR, log, 0, + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js job exception: %V", &exception); return NGX_ERROR; } @@ -450,12 +625,10 @@ ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log, } } - ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); - if (ngx_js_unhandled_rejection(ctx)) { ngx_js_exception(vm, &exception); - ngx_log_error(NGX_LOG_ERR, log, 0, "js exception: %V", &exception); + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception); return NGX_ERROR; } @@ -463,6 +636,110 @@ ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log, } +static void * +ngx_engine_njs_external(ngx_engine_t *engine) +{ + return njs_vm_external_ptr(engine->u.njs.vm); +} + +static ngx_int_t +ngx_engine_njs_pending(ngx_engine_t *e) +{ + return njs_vm_pending(e->u.njs.vm); +} + + +static ngx_int_t +ngx_engine_njs_string(ngx_engine_t *e, njs_opaque_value_t *value, + ngx_str_t *str) +{ + ngx_int_t rc; + njs_str_t s; + + rc = ngx_js_string(e->u.njs.vm, njs_value_arg(value), &s); + + str->data = s.start; + str->len = s.length; + + return rc; +} + + +static void +ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, + ngx_js_loc_conf_t *conf) +{ + ngx_js_event_t *event; + njs_rbtree_node_t *node; + + if (ctx != NULL) { + node = njs_rbtree_min(&ctx->waiting_events); + + while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) { + event = (ngx_js_event_t *) ((u_char *) node + - offsetof(ngx_js_event_t, node)); + + if (event->destructor != NULL) { + event->destructor(event); + } + + node = njs_rbtree_node_successor(&ctx->waiting_events, node); + } + } + + njs_vm_destroy(e->u.njs.vm); + + /* + * when ctx !=NULL e->pool is vm pool, in such case it is destroyed + * by njs_vm_destroy(). + */ + + if (ctx == NULL) { + njs_mp_destroy(e->pool); + } +} + + +ngx_int_t +ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_opaque_value_t *args, + njs_uint_t nargs) +{ + njs_int_t ret; + ngx_str_t exception; + ngx_connection_t *c; + + ret = njs_vm_call(vm, func, njs_value_arg(args), nargs); + if (ret == NJS_ERROR) { + ngx_js_exception(vm, &exception); + + c = ngx_external_connection(vm, njs_vm_external_ptr(vm)); + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "js exception: %V", &exception); + return NGX_ERROR; + } + + for ( ;; ) { + ret = njs_vm_execute_pending_job(vm); + if (ret <= NJS_OK) { + c = ngx_external_connection(vm, njs_vm_external_ptr(vm)); + + if (ret == NJS_ERROR) { + ngx_js_exception(vm, &exception); + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "js job exception: %V", &exception); + return NGX_ERROR; + } + + break; + } + } + + return NGX_OK; +} + + ngx_int_t ngx_js_exception(njs_vm_t *vm, ngx_str_t *s) { @@ -554,33 +831,18 @@ ngx_js_event_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2) void -ngx_js_ctx_init(ngx_js_ctx_t *ctx) +ngx_js_ctx_init(ngx_js_ctx_t *ctx, ngx_log_t *log) { + ctx->log = log; ctx->event_id = 0; njs_rbtree_init(&ctx->waiting_events, ngx_js_event_rbtree_compare); } void -ngx_js_ctx_destroy(ngx_js_ctx_t *ctx) +ngx_js_ctx_destroy(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf) { - ngx_js_event_t *event; - njs_rbtree_node_t *node; - - node = njs_rbtree_min(&ctx->waiting_events); - - while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) { - event = (ngx_js_event_t *) ((u_char *) node - - offsetof(ngx_js_event_t, node)); - - if (event->destructor != NULL) { - event->destructor(njs_vm_external_ptr(event->vm), event); - } - - node = njs_rbtree_node_successor(&ctx->waiting_events, node); - } - - njs_vm_destroy(ctx->vm); + ctx->engine->destroy(ctx->engine, ctx, conf); } @@ -1047,9 +1309,10 @@ ngx_js_timer_handler(ngx_event_t *ev) event = (ngx_js_event_t *) ((u_char *) ev - offsetof(ngx_js_event_t, ev)); - vm = event->vm; + vm = event->ctx; - rc = ngx_js_call(vm, event->function, event->args, event->nargs); + rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)), + event->args, event->nargs); ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); ngx_js_del_event(ctx, event); @@ -1059,7 +1322,7 @@ ngx_js_timer_handler(ngx_event_t *ev) static void -ngx_js_clear_timer(njs_external_ptr_t external, ngx_js_event_t *event) +ngx_js_clear_timer(ngx_js_event_t *event) { if (event->ev.timer_set) { ngx_del_timer(&event->ev); @@ -1106,10 +1369,10 @@ njs_set_timer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - event->vm = vm; - event->function = njs_value_function(njs_argument(args, 1)); + event->ctx = vm; + njs_value_assign(&event->function, njs_argument(args, 1)); event->nargs = nargs; - event->args = (njs_value_t *) ((u_char *) event + sizeof(ngx_js_event_t)); + event->args = (njs_opaque_value_t *) &event[1]; event->destructor = ngx_js_clear_timer; ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); @@ -1343,6 +1606,47 @@ ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +char * +ngx_js_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_uint_t *type, m; + ngx_conf_bitmask_t *mask; + + type = (size_t *) (p + cmd->offset); + if (*type != NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + + value = cf->args->elts; + mask = cmd->post; + + for (m = 0; mask[m].name.len != 0; m++) { + + if (mask[m].name.len != value[1].len + || ngx_strcasecmp(mask[m].name.data, value[1].data) != 0) + { + continue; + } + + *type = mask[m].mask; + + break; + } + + if (mask[m].name.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[1].data); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -1552,14 +1856,16 @@ ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_js_named_path_t *import, *pi, *pij, *preload; if (conf->imports == NGX_CONF_UNSET_PTR + && conf->type == NGX_CONF_UNSET_UINT && conf->paths == NGX_CONF_UNSET_PTR && conf->preload_objects == NGX_CONF_UNSET_PTR) { - if (prev->vm != NULL) { + if (prev->engine != NULL) { conf->preload_objects = prev->preload_objects; conf->imports = prev->imports; + conf->type = prev->type; conf->paths = prev->paths; - conf->vm = prev->vm; + conf->engine = prev->engine; conf->preload_vm = prev->preload_vm; @@ -1703,9 +2009,10 @@ ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused, promise_obj = njs_value_ptr(promise); for (i = 0; i < length; i++) { - if (rejected_promise[i].promise == promise_obj) { - njs_arr_remove(ctx->rejected_promises, - &rejected_promise[i]); + if (njs_value_ptr(njs_value_arg(&rejected_promise[i].promise)) + == promise_obj) + { + njs_arr_remove(ctx->rejected_promises, &rejected_promise[i]); break; } @@ -1727,7 +2034,7 @@ ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused, return; } - rejected_promise->promise = njs_value_ptr(promise); + njs_value_assign(&rejected_promise->promise, promise); njs_value_assign(&rejected_promise->message, reason); } @@ -1918,13 +2225,13 @@ ngx_js_file_dirname(const njs_str_t *path, ngx_str_t *name) static njs_int_t -ngx_js_set_cwd(njs_vm_t *vm, ngx_js_loc_conf_t *conf, njs_str_t *path) +ngx_js_set_cwd(njs_mp_t *mp, ngx_js_loc_conf_t *conf, njs_str_t *path) { ngx_str_t cwd; ngx_js_file_dirname(path, &cwd); - conf->cwd.data = njs_mp_alloc(njs_vm_memory_pool(vm), cwd.len); + conf->cwd.data = njs_mp_alloc(mp, cwd.len); if (conf->cwd.data == NULL) { return NJS_ERROR; } @@ -1969,7 +2276,7 @@ ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) prev_cwd = conf->cwd; - ret = ngx_js_set_cwd(vm, conf, &info.file); + ret = ngx_js_set_cwd(njs_vm_memory_pool(vm), conf, &info.file); if (ret != NJS_OK) { njs_vm_internal_error(vm, "while setting cwd for \"%V\" module", &info.file); @@ -1992,22 +2299,15 @@ ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) ngx_int_t ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, - njs_vm_opt_t *options) + ngx_engine_opts_t *options) { + u_char *start, *p; size_t size; - u_char *start, *end, *p; ngx_str_t *m, file; - njs_int_t rc; - njs_str_t text; ngx_uint_t i; - njs_value_t *value; ngx_pool_cleanup_t *cln; - njs_opaque_value_t lvalue, exception; ngx_js_named_path_t *import; - static const njs_str_t line_number_key = njs_str("lineNumber"); - static const njs_str_t file_name_key = njs_str("fileName"); - if (conf->preload_objects != NGX_CONF_UNSET_PTR) { if (ngx_js_init_preload_vm(cf, (ngx_js_loc_conf_t *)conf) != NGX_OK) { return NGX_ERROR; @@ -2054,9 +2354,10 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, options->file.start = file.data; options->file.length = file.len; + options->conf = conf; - conf->vm = njs_vm_create(options); - if (conf->vm == NULL) { + conf->engine = ngx_create_engine(options); + if (conf->engine == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to create js VM"); return NGX_ERROR; } @@ -2069,17 +2370,6 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, cln->handler = ngx_js_cleanup_vm; cln->data = conf; - njs_vm_set_rejection_tracker(conf->vm, ngx_js_rejection_tracker, - NULL); - - rc = ngx_js_set_cwd(conf->vm, conf, &options->file); - if (rc != NJS_OK) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to set cwd"); - return NGX_ERROR; - } - - njs_vm_set_module_loader(conf->vm, ngx_js_module_loader, conf); - if (conf->paths != NGX_CONF_UNSET_PTR) { m = conf->paths->elts; @@ -2090,52 +2380,14 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, } } - end = start + size; - - rc = njs_vm_compile(conf->vm, &start, end); - - if (rc != NJS_OK) { - njs_vm_exception_get(conf->vm, njs_value_arg(&exception)); - njs_vm_value_string(conf->vm, &text, njs_value_arg(&exception)); - - value = njs_vm_object_prop(conf->vm, njs_value_arg(&exception), - &file_name_key, &lvalue); - if (value == NULL) { - value = njs_vm_object_prop(conf->vm, njs_value_arg(&exception), - &line_number_key, &lvalue); - - if (value != NULL) { - i = njs_value_number(value) - 1; - - if (i < conf->imports->nelts) { - import = conf->imports->elts; - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "%*s, included in %s:%ui", text.length, - text.start, import[i].file, import[i].line); - return NGX_ERROR; - } - } - } - - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "%*s", text.length, - text.start); - return NGX_ERROR; - } - - if (start != end) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "extra characters in js script: \"%*s\"", - end - start, start); - return NGX_ERROR; - } - - return NGX_OK; + return conf->engine->compile(conf, cf->log, start, size); } static njs_int_t ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx) { + njs_vm_t *vm; njs_int_t ret; njs_str_t message; ngx_js_rejected_promise_t *rejected_promise; @@ -2146,15 +2398,16 @@ ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx) return 0; } + vm = ctx->engine->u.njs.vm; rejected_promise = ctx->rejected_promises->start; - ret = njs_vm_value_to_string(ctx->vm, &message, + ret = njs_vm_value_to_string(vm, &message, njs_value_arg(&rejected_promise->message)); if (njs_slow_path(ret != NJS_OK)) { return -1; } - njs_vm_error(ctx->vm, "unhandled promise rejection: %V", &message); + njs_vm_error(vm, "unhandled promise rejection: %V", &message); njs_arr_destroy(ctx->rejected_promises); ctx->rejected_promises = NULL; @@ -2168,7 +2421,7 @@ ngx_js_cleanup_vm(void *data) { ngx_js_loc_conf_t *jscf = data; - njs_vm_destroy(jscf->vm); + jscf->engine->destroy(jscf->engine, NULL, NULL); if (jscf->preload_objects != NGX_CONF_UNSET_PTR) { njs_vm_destroy(jscf->preload_vm); @@ -2187,6 +2440,7 @@ ngx_js_create_conf(ngx_conf_t *cf, size_t size) } conf->paths = NGX_CONF_UNSET_PTR; + conf->type = NGX_CONF_UNSET_UINT; conf->imports = NGX_CONF_UNSET_PTR; conf->preload_objects = NGX_CONF_UNSET_PTR; @@ -2251,6 +2505,7 @@ ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child, ngx_js_loc_conf_t *prev = parent; ngx_js_loc_conf_t *conf = child; + ngx_conf_merge_uint_value(conf->type, prev->type, NGX_ENGINE_NJS); ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 16384); ngx_conf_merge_size_value(conf->max_response_body_size, diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h index 373dd499b..a3bbd5415 100644 --- a/nginx/ngx_js.h +++ b/nginx/ngx_js.h @@ -20,6 +20,8 @@ #include "ngx_js_shared_dict.h" +#define NGX_ENGINE_NJS 1 + #define NGX_JS_UNSET 0 #define NGX_JS_DEPRECATED 1 #define NGX_JS_STRING 2 @@ -36,9 +38,11 @@ #define ngx_js_buffer_type(btype) ((btype) & ~NGX_JS_DEPRECATED) +typedef struct ngx_js_loc_conf_s ngx_js_loc_conf_t; typedef struct ngx_js_event_s ngx_js_event_t; typedef struct ngx_js_dict_s ngx_js_dict_t; typedef struct ngx_js_ctx_s ngx_js_ctx_t; +typedef struct ngx_engine_s ngx_engine_t; typedef ngx_pool_t *(*ngx_external_pool_pt)(njs_external_ptr_t e); @@ -60,14 +64,13 @@ typedef struct { struct ngx_js_event_s { - njs_vm_t *vm; - njs_function_t *function; - njs_value_t *args; + void *ctx; + njs_opaque_value_t function; + njs_opaque_value_t *args; ngx_socket_t fd; NJS_RBTREE_NODE (node); njs_uint_t nargs; - void (*destructor)(njs_external_ptr_t external, - ngx_js_event_t *event); + void (*destructor)(ngx_js_event_t *event); ngx_event_t ev; void *data; }; @@ -79,7 +82,8 @@ struct ngx_js_event_s { #define _NGX_JS_COMMON_LOC_CONF \ - njs_vm_t *vm; \ + ngx_uint_t type; \ + ngx_engine_t *engine; \ ngx_str_t cwd; \ ngx_array_t *imports; \ ngx_array_t *paths; \ @@ -109,7 +113,10 @@ struct ngx_js_event_s { #define NGX_JS_COMMON_CTX \ - njs_vm_t *vm; \ + ngx_engine_t *engine; \ + ngx_log_t *log; \ + njs_opaque_value_t args[3]; \ + njs_opaque_value_t retval; \ njs_arr_t *rejected_promises; \ njs_rbtree_t waiting_events; \ ngx_socket_t event_id @@ -122,7 +129,7 @@ struct ngx_js_event_s { #define ngx_js_del_event(ctx, event) \ do { \ if ((event)->destructor) { \ - (event)->destructor(njs_vm_external_ptr((event)->vm), event); \ + (event)->destructor(event); \ } \ \ njs_rbtree_delete(&(ctx)->waiting_events, &(event)->node); \ @@ -134,9 +141,9 @@ typedef struct { } ngx_js_main_conf_t; -typedef struct { +struct ngx_js_loc_conf_s { NGX_JS_COMMON_LOC_CONF; -} ngx_js_loc_conf_t; +}; typedef struct { @@ -150,6 +157,54 @@ struct ngx_js_ctx_s { }; +typedef struct ngx_engine_opts_s { + unsigned engine; + union { + struct { + njs_vm_meta_t *metas; + njs_module_t **addons; + } njs; + } u; + + njs_str_t file; + ngx_js_loc_conf_t *conf; + ngx_engine_t *(*clone)(ngx_js_ctx_t *ctx, + ngx_js_loc_conf_t *cf, njs_int_t pr_id, + void *external); + void (*destroy)(ngx_engine_t *e, ngx_js_ctx_t *ctx, + ngx_js_loc_conf_t *conf); +} ngx_engine_opts_t; + + +struct ngx_engine_s { + union { + struct { + njs_vm_t *vm; + } njs; + } u; + + ngx_int_t (*compile)(ngx_js_loc_conf_t *conf, ngx_log_t *lg, + u_char *start, size_t size); + ngx_int_t (*call)(ngx_js_ctx_t *ctx, ngx_str_t *fname, + njs_opaque_value_t *args, + njs_uint_t nargs); + ngx_engine_t *(*clone)(ngx_js_ctx_t *ctx, + ngx_js_loc_conf_t *cf, njs_int_t pr_id, + void *external); + void *(*external)(ngx_engine_t *e); + ngx_int_t (*pending)(ngx_engine_t *e); + ngx_int_t (*string)(ngx_engine_t *e, + njs_opaque_value_t *value, + ngx_str_t *str); + void (*destroy)(ngx_engine_t *e, ngx_js_ctx_t *ctx, + ngx_js_loc_conf_t *conf); + + unsigned type; + const char *name; + njs_mp_t *pool; +}; + + #define ngx_external_connection(vm, e) \ (*((ngx_connection_t **) ((u_char *) (e) + njs_vm_meta(vm, 0)))) #define ngx_external_pool(vm, e) \ @@ -182,19 +237,21 @@ struct ngx_js_ctx_s { : njs_vm_value_buffer_set(vm, value, start, len)) -#define ngx_vm_pending(ctx) \ - (njs_vm_pending((ctx)->vm) || !njs_rbtree_is_empty(&(ctx)->waiting_events)) +void ngx_js_ctx_init(ngx_js_ctx_t *ctx, ngx_log_t *log); + +#define ngx_js_ctx_pending(ctx) \ + ((ctx)->engine->pending(ctx->engine) \ + || !njs_rbtree_is_empty(&(ctx)->waiting_events)) +#define ngx_js_ctx_external(ctx) \ + ((ctx)->engine->external(ctx->engine)) -void ngx_js_ctx_init(ngx_js_ctx_t *ctx); -void ngx_js_ctx_destroy(ngx_js_ctx_t *ctx); -ngx_int_t ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_value_t *args, - njs_uint_t nargs); -ngx_int_t ngx_js_name_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log, +void ngx_js_ctx_destroy(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf); +ngx_int_t ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_opaque_value_t *args, njs_uint_t nargs); -ngx_int_t ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log, - njs_opaque_value_t *args, njs_uint_t nargs, njs_opaque_value_t *retval); ngx_int_t ngx_js_exception(njs_vm_t *vm, ngx_str_t *s); +ngx_engine_t *ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, + void *external); njs_int_t ngx_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t level, njs_value_t *retval); @@ -203,13 +260,14 @@ void ngx_js_log(njs_vm_t *vm, njs_external_ptr_t external, void ngx_js_logger(ngx_connection_t *c, ngx_uint_t level, const u_char *start, size_t length); char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char * ngx_js_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_int_t ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf); ngx_int_t ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_js_loc_conf_t *prev, ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)); ngx_int_t ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, - njs_vm_opt_t *options); + ngx_engine_opts_t *opt); ngx_js_loc_conf_t *ngx_js_create_conf(ngx_conf_t *cf, size_t size); char * ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child, ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)); diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c index cea80469c..1c5a961dd 100644 --- a/nginx/ngx_js_fetch.c +++ b/nginx/ngx_js_fetch.c @@ -174,8 +174,7 @@ static njs_int_t ngx_js_headers_fill(njs_vm_t *vm, ngx_js_headers_t *headers, njs_value_t *init); static ngx_js_http_t *ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log); -static void njs_js_http_destructor(njs_external_ptr_t external, - ngx_js_event_t *event); +static void njs_js_http_destructor(ngx_js_event_t *event); static void ngx_js_resolve_handler(ngx_resolver_ctx_t *ctx); static njs_int_t ngx_js_fetch_promissified_result(njs_vm_t *vm, njs_value_t *result, njs_int_t rc, njs_value_t *retval); @@ -1309,8 +1308,8 @@ ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log) ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); - event->vm = vm; - event->function = callback; + event->ctx = vm; + njs_value_function_set(njs_value_arg(&event->function), callback); event->destructor = njs_js_http_destructor; event->fd = ctx->event_id++; event->data = http; @@ -1439,7 +1438,7 @@ ngx_js_http_close_connection(ngx_connection_t *c) static void -njs_js_http_destructor(njs_external_ptr_t external, ngx_js_event_t *event) +njs_js_http_destructor(ngx_js_event_t *event) { ngx_js_http_t *http; @@ -1530,7 +1529,8 @@ ngx_js_http_fetch_done(ngx_js_http_t *http, njs_opaque_value_t *retval, vm = http->vm; event = http->event; - rc = ngx_js_call(vm, event->function, njs_value_arg(&arguments), 2); + rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)), + &arguments[0], 2); ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); ngx_js_del_event(ctx, event); diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index 295b2fcfb..565f4e661 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -12,6 +12,8 @@ #include "ngx_js.h" +typedef struct ngx_stream_js_ctx_s ngx_stream_js_ctx_t; + typedef struct { NGX_JS_COMMON_LOC_CONF; @@ -22,7 +24,7 @@ typedef struct { typedef struct { - njs_function_t *function; + njs_opaque_value_t function; ngx_uint_t data_type; } ngx_stream_js_ev_t; @@ -47,30 +49,34 @@ typedef struct { } ngx_js_periodic_t; -typedef struct { +struct ngx_stream_js_ctx_s { NGX_JS_COMMON_CTX; - njs_opaque_value_t retval; - njs_opaque_value_t args[3]; ngx_buf_t *buf; ngx_chain_t **last_out; ngx_chain_t *free; ngx_chain_t *upstream_busy; ngx_chain_t *downstream_busy; ngx_int_t status; + ngx_int_t (*run_event)(ngx_stream_session_t *s, + ngx_stream_js_ctx_t *ctx, + ngx_stream_js_ev_t *event, + ngx_uint_t from_upstream); + ngx_int_t (*body_filter)(ngx_stream_session_t *s, + ngx_stream_js_ctx_t *ctx, + ngx_chain_t *in, + ngx_uint_t from_upstream); #define NGX_JS_EVENT_UPLOAD 0 #define NGX_JS_EVENT_DOWNLOAD 1 #define NGX_JS_EVENT_MAX 2 - ngx_stream_js_ev_t events[2]; + ngx_stream_js_ev_t events[NGX_JS_EVENT_MAX]; unsigned filter:1; unsigned in_progress:1; ngx_js_periodic_t *periodic; -} ngx_stream_js_ctx_t; +}; #define ngx_stream_pending(ctx) \ - (ngx_vm_pending(ctx) \ - || (ctx)->events[NGX_JS_EVENT_UPLOAD].function != NULL \ - || (ctx)->events[NGX_JS_EVENT_DOWNLOAD].function != NULL) + (ngx_js_ctx_pending(ctx) || ngx_stream_js_pending_events(ctx)) static ngx_int_t ngx_stream_js_access_handler(ngx_stream_session_t *s); @@ -79,6 +85,8 @@ static ngx_int_t ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name); static ngx_int_t ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in, ngx_uint_t from_upstream); +static ngx_int_t ngx_stream_njs_body_filter(ngx_stream_session_t *s, + ngx_stream_js_ctx_t *ctx, ngx_chain_t *in, ngx_uint_t from_upstream); static ngx_int_t ngx_stream_js_next_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_chain_t *out, ngx_uint_t from_upstream); static ngx_int_t ngx_stream_js_variable_set(ngx_stream_session_t *s, @@ -87,12 +95,13 @@ static ngx_int_t ngx_stream_js_variable_var(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id); +static ngx_int_t ngx_stream_js_pending_events(ngx_stream_js_ctx_t *ctx); static void ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx); static void ngx_stream_js_cleanup(void *data); -static njs_int_t ngx_stream_js_run_event(ngx_stream_session_t *s, +static ngx_int_t ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream); -static njs_function_t **ngx_stream_js_event(ngx_stream_session_t *s, +static ngx_stream_js_ev_t *ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event); static njs_int_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm, @@ -156,6 +165,11 @@ static char *ngx_stream_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, static ngx_ssl_t *ngx_stream_js_ssl(ngx_stream_session_t *s); static ngx_flag_t ngx_stream_js_ssl_verify(ngx_stream_session_t *s); +static ngx_conf_bitmask_t ngx_stream_js_engines[] = { + { ngx_string("njs"), NGX_ENGINE_NJS }, + { ngx_null_string, 0 } +}; + #if (NGX_STREAM_SSL) static ngx_conf_bitmask_t ngx_stream_js_ssl_protocols[] = { @@ -170,6 +184,13 @@ static ngx_conf_bitmask_t ngx_stream_js_ssl_protocols[] = { static ngx_command_t ngx_stream_js_commands[] = { + { ngx_string("js_engine"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_js_engine, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_js_srv_conf_t, type), + &ngx_stream_js_engines }, + { ngx_string("js_import"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE13, ngx_js_import, @@ -693,10 +714,7 @@ ngx_stream_js_preread_handler(ngx_stream_session_t *s) static ngx_int_t ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name) { - ngx_str_t exception; - njs_int_t ret; ngx_int_t rc; - ngx_connection_t *c; ngx_stream_js_ctx_t *ctx; if (name->len == 0) { @@ -711,8 +729,6 @@ ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name) return rc; } - c = s->connection; - ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (!ctx->in_progress) { @@ -723,36 +739,33 @@ ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name) ctx->status = NGX_ERROR; - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "stream js phase call \"%V\"", name); - rc = ngx_js_name_call(ctx->vm, name, c->log, &ctx->args[0], 1); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, name, &ctx->args[0], 1); if (rc == NGX_ERROR) { return rc; } } - ret = ngx_stream_js_run_event(s, ctx, &ctx->events[NGX_JS_EVENT_UPLOAD], 0); - if (ret != NJS_OK) { - ngx_js_exception(ctx->vm, &exception); - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V", - &exception); - + rc = ctx->run_event(s, ctx, &ctx->events[NGX_JS_EVENT_UPLOAD], 0); + if (rc != NGX_OK) { return NGX_ERROR; } if (ngx_stream_pending(ctx)) { ctx->in_progress = 1; - rc = ctx->events[NGX_JS_EVENT_UPLOAD].function ? NGX_AGAIN : NGX_DONE; + rc = (ctx->events[NGX_JS_EVENT_UPLOAD].data_type != NGX_JS_UNSET) + ? NGX_AGAIN + : NGX_DONE; } else { ctx->in_progress = 0; rc = ctx->status; } - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream js phase rc: %i", + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "stream js phase rc: %i", rc); return rc; @@ -769,11 +782,8 @@ ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in, ngx_uint_t from_upstream) { ngx_int_t rc; - ngx_str_t exception; - njs_int_t ret; - ngx_chain_t *out, *cl; + ngx_chain_t *out; ngx_connection_t *c; - ngx_stream_js_ev_t *event; ngx_stream_js_ctx_t *ctx; ngx_stream_js_srv_conf_t *jscf; @@ -803,7 +813,8 @@ ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in, ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream js filter call \"%V\"" , &jscf->filter); - rc = ngx_js_name_call(ctx->vm, &jscf->filter, c->log, &ctx->args[0], 1); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jscf->filter, + &ctx->args[0], 1); if (rc == NGX_ERROR) { return rc; @@ -814,37 +825,9 @@ ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in, ctx->last_out = &out; - while (in) { - ctx->buf = in->buf; - - event = ngx_stream_event(from_upstream); - - if (event->function != NULL) { - ret = ngx_stream_js_run_event(s, ctx, event, from_upstream); - if (ret != NJS_OK) { - ngx_js_exception(ctx->vm, &exception); - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V", - &exception); - - return NGX_ERROR; - } - - ctx->buf->pos = ctx->buf->last; - - } else { - cl = ngx_alloc_chain_link(c->pool); - if (cl == NULL) { - return NGX_ERROR; - } - - cl->buf = ctx->buf; - - *ctx->last_out = cl; - ctx->last_out = &cl->next; - } - - in = in->next; + rc = ctx->body_filter(s, ctx, in, from_upstream); + if (rc != NGX_OK) { + return NGX_ERROR; } ctx->buf = NULL; @@ -895,8 +878,7 @@ ngx_stream_js_variable_set(ngx_stream_session_t *s, ngx_int_t rc; njs_int_t pending; - ngx_str_t *fname; - njs_str_t value; + ngx_str_t *fname, value; ngx_stream_js_ctx_t *ctx; fname = &vdata->fname; @@ -919,8 +901,7 @@ ngx_stream_js_variable_set(ngx_stream_session_t *s, pending = ngx_stream_pending(ctx); - rc = ngx_js_name_invoke(ctx->vm, fname, s->connection->log, &ctx->args[0], - 1, &ctx->retval); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, fname, &ctx->args[0], 1); if (rc == NGX_ERROR) { v->not_found = 1; @@ -933,15 +914,15 @@ ngx_stream_js_variable_set(ngx_stream_session_t *s, return NGX_ERROR; } - if (ngx_js_string(ctx->vm, njs_value_arg(&ctx->retval), &value) != NGX_OK) { + if (ctx->engine->string(ctx->engine, &ctx->retval, &value) != NGX_OK) { return NGX_ERROR; } - v->len = value.length; + v->len = value.len; v->valid = 1; v->no_cacheable = vdata->flags & NGX_NJS_VAR_NOCACHE; v->not_found = 0; - v->data = value.start; + v->data = value.data; return NGX_OK; } @@ -977,18 +958,12 @@ ngx_stream_js_variable_var(ngx_stream_session_t *s, static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id) { - njs_int_t rc; - njs_str_t key; - ngx_str_t exception; - ngx_uint_t i; - njs_opaque_value_t retval; ngx_pool_cleanup_t *cln; - ngx_js_named_path_t *preload; ngx_stream_js_ctx_t *ctx; ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); - if (jscf->vm == NULL) { + if (jscf->engine == NULL) { return NGX_DECLINED; } @@ -1000,24 +975,24 @@ ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id) return NGX_ERROR; } - ngx_js_ctx_init((ngx_js_ctx_t *) ctx); - - njs_value_invalid_set(njs_value_arg(&ctx->retval)); + ngx_js_ctx_init((ngx_js_ctx_t *) ctx, s->connection->log); ngx_stream_set_ctx(s, ctx, ngx_stream_js_module); } - if (ctx->vm) { + if (ctx->engine) { return NGX_OK; } - ctx->vm = njs_vm_clone(jscf->vm, s); - if (ctx->vm == NULL) { + ctx->engine = jscf->engine->clone((ngx_js_ctx_t *) ctx, + (ngx_js_loc_conf_t *) jscf, proto_id, s); + if (ctx->engine == NULL) { return NGX_ERROR; } - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, - "stream js vm clone: %p from: %p", ctx->vm, jscf->vm); + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "stream js vm clone: %p from: %p", ctx->engine, + jscf->engine); cln = ngx_pool_cleanup_add(s->connection->pool, 0); if (cln == NULL) { @@ -1027,43 +1002,22 @@ ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id) cln->handler = ngx_stream_js_cleanup; cln->data = s; - /* bind objects from preload vm */ - - if (jscf->preload_objects != NGX_CONF_UNSET_PTR) { - preload = jscf->preload_objects->elts; + return NGX_OK; +} - for (i = 0; i < jscf->preload_objects->nelts; i++) { - key.start = preload[i].name.data; - key.length = preload[i].name.len; - rc = njs_vm_value(jscf->preload_vm, &key, njs_value_arg(&retval)); - if (rc != NJS_OK) { - return NGX_ERROR; - } +static ngx_int_t +ngx_stream_js_pending_events(ngx_stream_js_ctx_t *ctx) +{ + ngx_uint_t i; - rc = njs_vm_bind(ctx->vm, &key, njs_value_arg(&retval), 0); - if (rc != NJS_OK) { - return NGX_ERROR; - } + for (i = 0; i < NGX_JS_EVENT_MAX; i++) { + if (ctx->events[i].data_type != NGX_JS_UNSET) { + return 1; } } - if (njs_vm_start(ctx->vm, njs_value_arg(&retval)) == NJS_ERROR) { - ngx_js_exception(ctx->vm, &exception); - - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "js exception: %V", &exception); - - return NGX_ERROR; - } - - rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->args[0]), - proto_id, s, 0); - if (rc != NJS_OK) { - return NGX_ERROR; - } - - return NGX_OK; + return 0; } @@ -1073,9 +1027,11 @@ ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx) ngx_uint_t i; for (i = 0; i < NGX_JS_EVENT_MAX; i++) { - if (ctx->events[i].function != NULL) { - ctx->events[i].function = NULL; - } + /* + * event[i].data_type = NGX_JS_UNSET + * event[i].function = JS_NULL + */ + memset(&ctx->events[i], 0, sizeof(ngx_stream_js_ev_t)); } } @@ -1083,38 +1039,41 @@ ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx) static void ngx_stream_js_cleanup(void *data) { - ngx_stream_js_ctx_t *ctx; + ngx_stream_js_ctx_t *ctx; + ngx_stream_js_srv_conf_t *jscf; ngx_stream_session_t *s = data; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); - ngx_stream_js_drop_events(ctx); - - if (ngx_vm_pending(ctx)) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "pending events"); + if (ngx_js_ctx_pending(ctx)) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events"); } - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, - "stream js vm destroy: %p", ctx->vm); + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "stream js vm destroy: %p", ctx->engine); + + jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); - ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx); + ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx, (ngx_js_loc_conf_t *) jscf); } -static njs_int_t +static ngx_int_t ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream) { size_t len; u_char *p; + njs_vm_t *vm; njs_int_t ret; + ngx_str_t exception; ngx_buf_t *b; uintptr_t flags; ngx_connection_t *c; - if (event->function == NULL) { - return NJS_OK; + if (!njs_value_is_function(njs_value_arg(&event->function))) { + return NGX_OK; } c = s->connection; @@ -1122,36 +1081,90 @@ ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, len = b ? b->last - b->pos : 0; + vm = ctx->engine->u.njs.vm; + p = ngx_pnalloc(c->pool, len); if (p == NULL) { - njs_vm_memory_error(ctx->vm); - return NJS_ERROR; + njs_vm_memory_error(vm); + goto error; } if (len) { ngx_memcpy(p, b->pos, len); } - ret = ngx_js_prop(ctx->vm, event->data_type, njs_value_arg(&ctx->args[1]), + ret = ngx_js_prop(vm, event->data_type, njs_value_arg(&ctx->args[1]), p, len); if (ret != NJS_OK) { - return ret; + goto error; } flags = from_upstream << 1 | (uintptr_t) (b && b->last_buf); - ret = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->args[2]), + ret = njs_vm_external_create(vm, njs_value_arg(&ctx->args[2]), ngx_stream_js_session_flags_proto_id, (void *) flags, 0); if (ret != NJS_OK) { + goto error; + } + + ret = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)), + &ctx->args[1], 2); + + if (ret == NJS_ERROR) { +error: + ngx_js_exception(vm, &exception); + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V", + &exception); + return NGX_ERROR; } - return ngx_js_call(ctx->vm, event->function, njs_value_arg(&ctx->args[1]), - 2); + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_njs_body_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, + ngx_chain_t *in, ngx_uint_t from_upstream) +{ + ngx_int_t rc; + ngx_chain_t *cl; + ngx_stream_js_ev_t *event; + + while (in) { + ctx->buf = in->buf; + + event = ngx_stream_event(from_upstream); + + if (njs_value_is_function(njs_value_arg(&event->function))) { + rc = ngx_stream_js_run_event(s, ctx, event, from_upstream); + if (rc != NGX_OK) { + return NGX_ERROR; + } + + ctx->buf->pos = ctx->buf->last; + + } else { + cl = ngx_alloc_chain_link(s->connection->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->buf; + + *ctx->last_out = cl; + ctx->last_out = &cl->next; + } + + in = in->next; + } + + return NGX_OK; } -static njs_function_t ** +static ngx_stream_js_ev_t * ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event) { ngx_uint_t i, n, type; @@ -1204,7 +1217,7 @@ ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event) } if (i == n) { - njs_vm_error(ctx->vm, "unknown event \"%V\"", event); + njs_vm_error(ctx->engine->u.njs.vm, "unknown event \"%V\"", event); return NULL; } @@ -1213,13 +1226,13 @@ ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event) for (n = 0; n < NGX_JS_EVENT_MAX; n++) { type = ctx->events[n].data_type; if (type != NGX_JS_UNSET && type != events[i].data_type) { - njs_vm_error(ctx->vm, "mixing string and buffer events" - " is not allowed"); + njs_vm_error(ctx->engine->u.njs.vm, "mixing string and buffer" + " events is not allowed"); return NULL; } } - return &ctx->events[events[i].id].function; + return &ctx->events[events[i].id]; } @@ -1305,10 +1318,10 @@ static njs_int_t ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { - njs_str_t name; - njs_value_t *callback; - njs_function_t **cb; - ngx_stream_session_t *s; + njs_str_t name; + njs_value_t *callback; + ngx_stream_js_ev_t *event; + ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_session_proto_id, njs_argument(args, 0)); @@ -1328,17 +1341,17 @@ ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - cb = ngx_stream_js_event(s, &name); - if (cb == NULL) { + event = ngx_stream_js_event(s, &name); + if (event == NULL) { return NJS_ERROR; } - if (*cb != NULL) { + if (njs_value_is_function(njs_value_arg(&event->function))) { njs_vm_error(vm, "event handler \"%V\" is already set", &name); return NJS_ERROR; } - *cb = njs_value_function(callback); + njs_value_assign(&event->function, callback); njs_value_undefined_set(retval); @@ -1350,9 +1363,9 @@ static njs_int_t ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { - njs_str_t name; - njs_function_t **callback; - ngx_stream_session_t *s; + njs_str_t name; + ngx_stream_js_ev_t *event; + ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_session_proto_id, njs_argument(args, 0)); @@ -1366,12 +1379,13 @@ ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - callback = ngx_stream_js_event(s, &name); - if (callback == NULL) { + event = ngx_stream_js_event(s, &name); + if (event == NULL) { return NJS_ERROR; } - *callback = NULL; + njs_value_null_set(njs_value_arg(&event->function)); + event->data_type = NGX_JS_UNSET; njs_value_undefined_set(retval); @@ -1764,22 +1778,52 @@ ngx_js_stream_init(njs_vm_t *vm) } +static ngx_engine_t * +ngx_engine_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, + njs_int_t proto_id, void *external) +{ + njs_int_t rc; + ngx_engine_t *engine; + ngx_stream_js_ctx_t *sctx; + + engine = ngx_njs_clone(ctx, cf, external); + if (engine == NULL) { + return NULL; + } + + sctx = (ngx_stream_js_ctx_t *) ctx; + sctx->run_event = ngx_stream_js_run_event; + sctx->body_filter = ngx_stream_njs_body_filter; + + rc = njs_vm_external_create(engine->u.njs.vm, njs_value_arg(&ctx->args[0]), + proto_id, njs_vm_external_ptr(engine->u.njs.vm), + 0); + if (rc != NJS_OK) { + return NULL; + } + + return engine; +} + + static ngx_int_t ngx_stream_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) { - njs_vm_opt_t options; + ngx_engine_opts_t options; ngx_js_main_conf_t *jmcf; - njs_vm_opt_init(&options); + memset(&options, 0, sizeof(ngx_engine_opts_t)); - jmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_js_module); - ngx_stream_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; + options.engine = conf->type; - options.backtrace = 1; - options.metas = &ngx_stream_js_metas; - options.addons = njs_stream_js_addon_modules; - options.argv = ngx_argv; - options.argc = ngx_argc; + if (conf->type == NGX_ENGINE_NJS) { + jmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_js_module); + ngx_stream_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; + + options.u.njs.metas = &ngx_stream_js_metas; + options.u.njs.addons = njs_stream_js_addon_modules; + options.clone = ngx_engine_njs_clone; + } return ngx_js_init_conf_vm(cf, conf, &options); } @@ -1881,8 +1925,8 @@ ngx_stream_js_periodic_handler(ngx_event_t *ev) s->received++; - rc = ngx_js_name_invoke(ctx->vm, &periodic->method, &periodic->log, - &ctx->args[0], 1, &ctx->retval); + rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &periodic->method, + &ctx->args[0], 1); if (rc == NGX_AGAIN) { rc = NGX_OK; @@ -1925,7 +1969,7 @@ ngx_stream_js_periodic_event_handler(ngx_event_t *ev) ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); - if (!ngx_vm_pending(ctx)) { + if (!ngx_js_ctx_pending(ctx)) { ngx_stream_js_periodic_finalize(s, NGX_OK); return; } @@ -1942,9 +1986,9 @@ ngx_stream_js_periodic_finalize(ngx_stream_session_t *s, ngx_int_t rc) ngx_log_debug4(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream js periodic finalize: \"%V\" rc: %i c: %i " "pending: %i", &ctx->periodic->method, rc, s->received, - ngx_vm_pending(ctx)); + ngx_js_ctx_pending(ctx)); - if (s->received > 1 || (rc == NGX_OK && ngx_vm_pending(ctx))) { + if (s->received > 1 || (rc == NGX_OK && ngx_js_ctx_pending(ctx))) { return; } @@ -2348,6 +2392,7 @@ ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_stream_js_srv_conf_t *prev = parent; ngx_stream_js_srv_conf_t *conf = child; + ngx_conf_merge_uint_value(conf->type, prev->type, NGX_ENGINE_NJS); ngx_conf_merge_str_value(conf->access, prev->access, ""); ngx_conf_merge_str_value(conf->preread, prev->preread, ""); ngx_conf_merge_str_value(conf->filter, prev->filter, "");