blob: 6799b1820716ac1b546226a51dc8eff2cae9058d [file] [log] [blame]
/*
* Copyright (C) Roman Arutyunyan
* Copyright (C) Dmitry Volyntsev
* Copyright (C) NGINX, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_js.h"
#include "ngx_js_fetch.h"
extern njs_module_t njs_webcrypto_module;
static njs_external_t ngx_js_ext_core[] = {
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("log"),
.writable = 1,
.configurable = 1,
.enumerable = 1,
.u.method = {
.native = ngx_js_ext_log,
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("INFO"),
.u.property = {
.handler = ngx_js_ext_constant,
.magic32 = NGX_LOG_INFO,
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("WARN"),
.u.property = {
.handler = ngx_js_ext_constant,
.magic32 = NGX_LOG_WARN,
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("ERR"),
.u.property = {
.handler = ngx_js_ext_constant,
.magic32 = NGX_LOG_ERR,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("fetch"),
.writable = 1,
.configurable = 1,
.enumerable = 1,
.u.method = {
.native = ngx_js_ext_fetch,
}
},
};
njs_module_t *njs_js_addon_modules[] = {
&njs_webcrypto_module,
NULL,
};
ngx_int_t
ngx_js_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
njs_opaque_value_t *args, njs_uint_t nargs)
{
njs_int_t ret;
njs_str_t name;
ngx_str_t exception;
njs_function_t *func;
name.start = fname->data;
name.length = fname->len;
func = njs_vm_function(vm, &name);
if (func == NULL) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"js function \"%V\" not found", fname);
return NGX_ERROR;
}
ret = njs_vm_call(vm, func, njs_value_arg(args), nargs);
if (ret == NJS_ERROR) {
ngx_js_retval(vm, NULL, &exception);
ngx_log_error(NGX_LOG_ERR, log, 0,
"js exception: %V", &exception);
return NGX_ERROR;
}
ret = njs_vm_run(vm);
if (ret == NJS_ERROR) {
ngx_js_retval(vm, NULL, &exception);
ngx_log_error(NGX_LOG_ERR, log, 0,
"js exception: %V", &exception);
return NGX_ERROR;
}
return (ret == NJS_AGAIN) ? NGX_AGAIN : NGX_OK;
}
ngx_int_t
ngx_js_retval(njs_vm_t *vm, njs_opaque_value_t *retval, ngx_str_t *s)
{
njs_int_t ret;
njs_str_t str;
if (retval != NULL && njs_value_is_valid(njs_value_arg(retval))) {
ret = njs_vm_value_string(vm, &str, njs_value_arg(retval));
} else {
ret = njs_vm_retval_string(vm, &str);
}
if (ret != NJS_OK) {
return NGX_ERROR;
}
s->data = str.start;
s->len = str.length;
return NGX_OK;
}
ngx_int_t
ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n)
{
if (!njs_value_is_valid_number(value)) {
njs_vm_error(vm, "is not a number");
return NGX_ERROR;
}
*n = njs_value_number(value);
return NGX_OK;
}
ngx_int_t
ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str)
{
if (value != NULL && !njs_value_is_null_or_undefined(value)) {
if (njs_vm_value_to_bytes(vm, str, value) == NJS_ERROR) {
return NGX_ERROR;
}
} else {
str->start = NULL;
str->length = 0;
}
return NGX_OK;
}
ngx_int_t
ngx_js_core_init(njs_vm_t *vm, ngx_log_t *log)
{
ngx_int_t rc;
njs_int_t ret, proto_id;
njs_str_t name;
njs_opaque_value_t value;
rc = ngx_js_fetch_init(vm, log);
if (rc != NGX_OK) {
return NGX_ERROR;
}
proto_id = njs_vm_external_prototype(vm, ngx_js_ext_core,
njs_nitems(ngx_js_ext_core));
if (proto_id < 0) {
ngx_log_error(NGX_LOG_EMERG, log, 0, "failed to add js core proto");
return NGX_ERROR;
}
ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
if (njs_slow_path(ret != NJS_OK)) {
ngx_log_error(NGX_LOG_EMERG, log, 0,
"njs_vm_external_create() failed\n");
return NGX_ERROR;
}
name.length = 3;
name.start = (u_char *) "ngx";
ret = njs_vm_bind(vm, &name, njs_value_arg(&value), 1);
if (njs_slow_path(ret != NJS_OK)) {
ngx_log_error(NGX_LOG_EMERG, log, 0, "njs_vm_bind() failed\n");
return NGX_ERROR;
}
return NGX_OK;
}
njs_int_t
ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
njs_value_t *setval, njs_value_t *retval)
{
char *p;
ngx_str_t *field;
p = njs_vm_external(vm, NJS_PROTO_ID_ANY, value);
if (p == NULL) {
njs_value_undefined_set(retval);
return NJS_DECLINED;
}
field = (ngx_str_t *) (p + njs_vm_prop_magic32(prop));
return njs_vm_value_string_set(vm, retval, field->data, field->len);
}
njs_int_t
ngx_js_ext_uint(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
njs_value_t *setval, njs_value_t *retval)
{
char *p;
ngx_uint_t field;
p = njs_vm_external(vm, NJS_PROTO_ID_ANY, value);
if (p == NULL) {
njs_value_undefined_set(retval);
return NJS_DECLINED;
}
field = *(ngx_uint_t *) (p + njs_vm_prop_magic32(prop));
njs_value_number_set(retval, field);
return NJS_OK;
}
njs_int_t
ngx_js_ext_constant(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
njs_value_number_set(retval, njs_vm_prop_magic32(prop));
return NJS_OK;
}
njs_int_t
ngx_js_ext_boolean(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
njs_value_boolean_set(retval, njs_vm_prop_magic32(prop));
return NJS_OK;
}
njs_int_t
ngx_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t level)
{
char *p;
ngx_int_t lvl;
njs_str_t msg;
njs_value_t *value;
ngx_connection_t *c;
ngx_log_handler_pt handler;
p = njs_vm_external(vm, NJS_PROTO_ID_ANY, njs_argument(args, 0));
if (p == NULL) {
njs_vm_error(vm, "\"this\" is not an external");
return NJS_ERROR;
}
value = njs_arg(args, nargs, (level != 0) ? 1 : 2);
if (level == 0) {
if (ngx_js_integer(vm, njs_arg(args, nargs, 1), &lvl) != NGX_OK) {
return NJS_ERROR;
}
level = lvl;
}
if (ngx_js_string(vm, value, &msg) != NGX_OK) {
return NJS_ERROR;
}
c = ngx_external_connection(vm, p);
handler = c->log->handler;
c->log->handler = NULL;
ngx_log_error((ngx_uint_t) level, c->log, 0, "js: %*s",
msg.length, msg.start);
c->log->handler = handler;
njs_value_undefined_set(njs_vm_retval(vm));
return NJS_OK;
}