/* JS signature: listen(addr, [options]) */ SJ_PRIVATE enum v7_err Http_Server_listen(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; char buf[50], *p = buf; const char *ca_cert = NULL, *cert = NULL; v7_val_t this_obj = v7_get_this(v7); v7_val_t arg0 = v7_arg(v7, 0); v7_val_t opts = v7_arg(v7, 1); if (!v7_is_number(arg0) && !v7_is_string(arg0)) { rcode = v7_throwf(v7, "TypeError", "Function expected"); goto clean; } if (!v7_is_undefined(opts) && !v7_is_object(opts)) { rcode = v7_throwf(v7, "TypeError", "Options must be an object"); goto clean; } if (!v7_is_undefined(opts)) { v7_val_t ca_cert_v = v7_get(v7, opts, "ssl_ca_cert", ~0); v7_val_t cert_v = v7_get(v7, opts, "ssl_cert", ~0); if (!v7_is_undefined(ca_cert_v) && !v7_is_string(ca_cert_v)) { rcode = v7_throwf(v7, "TypeError", "ca_cert must be a string"); goto clean; } if (!v7_is_undefined(cert_v) && !v7_is_string(cert_v)) { rcode = v7_throwf(v7, "TypeError", "cert must be a string"); goto clean; } if (!v7_is_undefined(ca_cert_v)) { ca_cert = v7_to_cstring(v7, &ca_cert_v); } if (!v7_is_undefined(cert_v)) { cert = v7_to_cstring(v7, &cert_v); } } p = v7_stringify(v7, arg0, buf, sizeof(buf), 0); rcode = start_http_server(v7, p, this_obj, ca_cert, cert); if (rcode != V7_OK) { goto clean; } *res = this_obj; clean: if (p != buf) { free(p); } return rcode; }
/* * Construct a new WebSocket object: * * url: url where to connect to * protocol: websocket subprotocol * * Example: * ws = new WebSocket('wss://localhost:1234'); * ws.onopen = function(ev) { * print("ON OPEN", ev); * } * * ws.onclose = function(ev) { * print("ON CLOSE", ev); * } * * ws.onmessage = function(ev) { * print("ON MESSAGE", ev); * } * * ws.onerror = function(ev) { * print("ON ERROR", ev); * } * */ static v7_val_t sj_ws_ctor(struct v7 *v7, v7_val_t this_obj, v7_val_t args) { struct mg_connection *nc; struct user_data *ud; v7_val_t urlv = v7_array_get(v7, args, 0); v7_val_t subprotov = v7_array_get(v7, args, 1); (void) this_obj; (void) args; if (!v7_is_string(urlv)) { v7_throw(v7, "invalid ws url string"); } if (v7_is_object(this_obj) && this_obj != v7_get_global_object(v7)) { int use_ssl = 0; size_t len; const char *url = v7_to_string(v7, &urlv, &len); if (strncmp(url, "ws://", 5) == 0) { url += 5; } else if (strncmp(url, "wss://", 6) == 0) { url += 6; use_ssl = 1; } nc = mg_connect(&sj_mgr, url, ws_ev_handler); if (nc == NULL) v7_throw(v7, "error creating the connection"); #ifdef NS_ENABLE_SSL if (use_ssl) { mg_set_ssl(nc, NULL, NULL); } #endif (void) use_ssl; mg_set_protocol_http_websocket(nc); ud = calloc(1, sizeof(*ud)); ud->v7 = v7; ud->ws = this_obj; nc->user_data = ud; v7_own(v7, &ud->ws); if (v7_is_string(subprotov)) { size_t len; const char *proto = v7_to_string(v7, &subprotov, &len); ud->proto = strdup(proto); } } else { v7_throw(v7, "WebSocket ctor called without new"); } return v7_create_undefined(); }
clubby_handle_t console_get_current_clubby(struct v7 *v7) { v7_val_t clubby_v = v7_get(v7, v7_get_global(v7), s_clubby_prop, ~0); if (!v7_is_object(clubby_v)) { LOG(LL_ERROR, ("Clubby is not set")); return NULL; } clubby_handle_t ret = sj_clubby_get_handle(v7, clubby_v); LOG(LL_DEBUG, ("clubby handle: %p", ret)); return ret; }
int update_sysconf(struct v7 *v7, const char *path, v7_val_t val) { v7_val_t sys = v7_get(v7, v7_get_global(v7), "Sys", ~0); if (!v7_is_object(sys)) { return 1; } v7_val_t conf = v7_get(v7, sys, "conf", ~0); if (!v7_is_object(conf)) { return 1; } v7_val_t prev_obj, curr_obj; prev_obj = curr_obj = conf; const char *prev_tok, *curr_tok; prev_tok = curr_tok = path; for (;;) { while (*curr_tok != 0 && *curr_tok != '.') { curr_tok++; } curr_obj = v7_get(v7, prev_obj, prev_tok, (curr_tok - prev_tok)); if (v7_is_undefined(curr_obj)) { return 1; } else if (!v7_is_object(curr_obj)) { v7_set(v7, prev_obj, prev_tok, (curr_tok - prev_tok), val); return 0; } if (*curr_tok == 0) { return 1; } curr_tok++; prev_tok = curr_tok; prev_obj = curr_obj; } return 1; }
static void http_write_headers(struct v7 *v7, v7_val_t headers_obj, struct mg_connection *c) { if (v7_is_object(headers_obj)) { void *h = NULL; v7_val_t name, value; v7_prop_attr_t attrs; while ((h = v7_next_prop(h, headers_obj, &name, &value, &attrs)) != NULL) { size_t n1, n2; const char *s1 = v7_get_string_data(v7, &name, &n1); const char *s2 = v7_get_string_data(v7, &value, &n2); mg_printf(c, "%.*s: %.*s\r\n", (int) n1, s1, (int) n2, s2); } } }
V7_PRIVATE int is_cfunction_obj(struct v7 *v7, val_t v) { int ret = 0; if (v7_is_object(v)) { /* extract the hidden property from a cfunction_object */ struct v7_property *p; p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN); if (p != NULL) { v = p->value; } ret = is_cfunction_lite(v); } return ret; }
SJ_PRIVATE enum v7_err sj_Wifi_setup(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; v7_val_t ssidv = v7_arg(v7, 0); v7_val_t passv = v7_arg(v7, 1); v7_val_t extrasv = v7_arg(v7, 2); const char *ssid, *pass; size_t ssid_len, pass_len; int permanent = 1, ret = 0; if (!v7_is_string(ssidv) || !v7_is_string(passv)) { printf("ssid/pass are not strings\n"); *res = V7_UNDEFINED; goto clean; } if (v7_is_object(extrasv)) { permanent = v7_is_truthy(v7, v7_get(v7, extrasv, "permanent", ~0)); } ssid = v7_get_string(v7, &ssidv, &ssid_len); pass = v7_get_string(v7, &passv, &pass_len); struct sys_config_wifi_sta cfg; memset(&cfg, 0, sizeof(cfg)); cfg.ssid = (char *) ssid; cfg.pass = (char *) pass; LOG(LL_INFO, ("WiFi: connecting to '%s'", ssid)); ret = sj_wifi_setup_sta(&cfg); if (ret && permanent) { struct sys_config *cfg = get_cfg(); cfg->wifi.sta.enable = 1; sj_conf_set_str(&cfg->wifi.sta.ssid, ssid); sj_conf_set_str(&cfg->wifi.sta.pass, pass); } *res = v7_mk_boolean(v7, ret); goto clean; clean: return rcode; }
V7_PRIVATE val_t mk_js_function(struct v7 *v7, struct v7_generic_object *scope, val_t proto) { struct v7_js_function *f; val_t fval = V7_NULL; struct gc_tmp_frame tf = new_tmp_frame(v7); tmp_stack_push(&tf, &proto); tmp_stack_push(&tf, &fval); f = new_function(v7); if (f == NULL) { /* fval is left `null` */ goto cleanup; } #if defined(V7_ENABLE_ENTITY_IDS) f->base.entity_id_base = V7_ENTITY_ID_PART_OBJ; f->base.entity_id_spec = V7_ENTITY_ID_PART_JS_FUNC; #endif fval = js_function_to_value(f); f->base.properties = NULL; f->scope = scope; /* * Before setting a `V7_OBJ_FUNCTION` flag, make sure we don't have * `V7_OBJ_DENSE_ARRAY` flag set */ assert(!(f->base.attributes & V7_OBJ_DENSE_ARRAY)); f->base.attributes |= V7_OBJ_FUNCTION; /* TODO(mkm): lazily create these properties on first access */ if (v7_is_object(proto)) { v7_def(v7, proto, "constructor", 11, V7_DESC_ENUMERABLE(0), fval); v7_def(v7, fval, "prototype", 9, V7_DESC_ENUMERABLE(0) | V7_DESC_CONFIGURABLE(0), proto); } cleanup: tmp_frame_cleanup(&tf); return fval; }
static enum v7_err UART_configure(struct v7 *v7, v7_val_t *res) { struct esp_sj_uart_state *us; enum v7_err ret = esp_sj_uart_get_state(v7, &us); if (ret != V7_OK) return ret; struct esp_uart_config *cfg = esp_sj_uart_default_config(us->uart_no); v7_val_t cobj = v7_arg(v7, 0); if (v7_is_object(cobj)) { v7_val_t v; #define NUM_PROP(p) \ v = v7_get(v7, cobj, #p, ~0); \ if (v7_is_number(v)) cfg->p = v7_to_number(v); #define BOOL_PROP(p) \ v = v7_get(v7, cobj, #p, ~0); \ if (!v7_is_undefined(v)) cfg->p = v7_to_boolean(v); NUM_PROP(baud_rate); NUM_PROP(rx_buf_size); BOOL_PROP(rx_fc_ena); NUM_PROP(rx_fifo_full_thresh); NUM_PROP(rx_fifo_fc_thresh); NUM_PROP(rx_fifo_alarm); NUM_PROP(rx_linger_micros); NUM_PROP(tx_buf_size); BOOL_PROP(tx_fc_ena); NUM_PROP(tx_fifo_empty_thresh); NUM_PROP(tx_fifo_full_thresh); BOOL_PROP(swap_rxtx_ctsrts); NUM_PROP(status_interval_ms); } if (esp_uart_init(cfg)) { esp_sj_uart_schedule_dispatcher(cfg->uart_no); *res = v7_mk_boolean(1); } else { free(cfg); *res = v7_mk_boolean(0); } return V7_OK; }
/* * Create request object, used by `Http.request()` and `Http.get()` */ WARN_UNUSED_RESULT static enum v7_err sj_http_request_common(struct v7 *v7, v7_val_t opts, v7_val_t cb, v7_val_t *res) { enum v7_err rcode = V7_OK; char addr[200]; struct mg_connection *c; struct user_data *ud; struct mg_connect_opts copts; #ifdef MG_ENABLE_SSL int force_ssl; #endif memset(&copts, 0, sizeof(copts)); /* * Determine type of provided `opts`, and if it's a string, then parse * it to object */ if (v7_is_string(opts)) { rcode = sj_url_parse(v7, opts, &opts); if (rcode != V7_OK) { goto clean; } } else if (!v7_is_object(opts)) { rcode = v7_throwf(v7, "Error", "opts must be an object or a string URL"); goto clean; } /* * Now, `opts` is guaranteed to be an object. * Let's retrieve needed properties */ v7_val_t v_h = v7_get(v7, opts, "hostname", ~0); if (v7_is_undefined(v_h)) { v_h = v7_get(v7, opts, "host", ~0); } v7_val_t v_p = v7_get(v7, opts, "port", ~0); v7_val_t v_uri = v7_get(v7, opts, "path", ~0); v7_val_t v_m = v7_get(v7, opts, "method", ~0); v7_val_t v_hdrs = v7_get(v7, opts, "headers", ~0); /* Perform options validation and set defaults if needed */ int port = v7_is_number(v_p) ? v7_to_number(v_p) : 80; const char *host = v7_is_string(v_h) ? v7_to_cstring(v7, &v_h) : ""; const char *uri = v7_is_string(v_uri) ? v7_to_cstring(v7, &v_uri) : "/"; const char *method = v7_is_string(v_m) ? v7_to_cstring(v7, &v_m) : "GET"; #ifdef MG_ENABLE_SSL v7_val_t v_pr = v7_get(v7, opts, "protocol", ~0); const char *protocol = v7_is_string(v_pr) ? v7_to_cstring(v7, &v_pr) : ""; force_ssl = (strcasecmp(protocol, "https") == 0); if ((rcode = fill_ssl_connect_opts(v7, opts, force_ssl, &copts)) != V7_OK) { goto clean; } #endif /* Compose address like host:port */ snprintf(addr, sizeof(addr), "%s:%d", host, port); /* * Try to connect, passing `http_ev_handler` as the callback, which will * call provided JavaScript function (we'll set it in user data below). * TODO(alashkin): change mg_connect_opt to mg_connect_http_opt */ if ((c = mg_connect_opt(&sj_mgr, addr, http_ev_handler, copts)) == NULL) { rcode = v7_throwf(v7, "Error", "Cannot connect"); goto clean; } /* * Attach mongoose's built-in HTTP event handler to the connection, and send * necessary headers */ mg_set_protocol_http_websocket(c); mg_printf(c, "%s %s HTTP/1.1\r\n", method, uri); mg_printf(c, "Host: %s\r\n", host); http_write_headers(v7, v_hdrs, c); http_write_chunked_encoding_header(c); mg_printf(c, "%s", "\r\n"); /* * Allocate and initialize user data structure that is used by the JS HTTP * interface. Create the request object (which will have the request * prototype `sj_http_request_proto`), and set provided callback function. */ c->user_data = ud = (struct user_data *) calloc(1, sizeof(*ud)); ud->v7 = v7; ud->obj = v7_mk_object(v7); ud->handler = cb; v7_own(v7, &ud->obj); v7_set_proto(v7, ud->obj, sj_http_request_proto); /* internal property: mongoose connection */ v7_set(v7, ud->obj, "_c", ~0, v7_mk_foreign(c)); /* internal property: callback function that was passed as an argument */ v7_set(v7, ud->obj, "_cb", ~0, ud->handler); *res = ud->obj; clean: return rcode; }
/* Callback for json_walk() */ static void frozen_cb(void *data, const char *name, size_t name_len, const char *path, const struct json_token *token) { struct json_parse_ctx *ctx = (struct json_parse_ctx *) data; v7_val_t v = V7_UNDEFINED; (void) path; v7_own(ctx->v7, &v); switch (token->type) { case JSON_TYPE_STRING: v = v7_mk_string(ctx->v7, token->ptr, token->len, 1 /* copy */); break; case JSON_TYPE_NUMBER: v = v7_mk_number(ctx->v7, cs_strtod(token->ptr, NULL)); break; case JSON_TYPE_TRUE: v = v7_mk_boolean(ctx->v7, 1); break; case JSON_TYPE_FALSE: v = v7_mk_boolean(ctx->v7, 0); break; case JSON_TYPE_NULL: v = V7_NULL; break; case JSON_TYPE_OBJECT_START: v = v7_mk_object(ctx->v7); break; case JSON_TYPE_ARRAY_START: v = v7_mk_array(ctx->v7); break; case JSON_TYPE_OBJECT_END: case JSON_TYPE_ARRAY_END: { /* Object or array has finished: deallocate its frame */ ctx->frame = free_json_frame(ctx, ctx->frame); } break; default: LOG(LL_ERROR, ("Wrong token type %d\n", token->type)); break; } if (!v7_is_undefined(v)) { if (name != NULL && name_len != 0) { /* Need to define a property on the current object/array */ if (v7_is_object(ctx->frame->val)) { v7_set(ctx->v7, ctx->frame->val, name, name_len, v); } else if (v7_is_array(ctx->v7, ctx->frame->val)) { /* * TODO(dfrank): consult name_len. Currently it's not a problem due to * the implementation details of frozen, but it might change */ int idx = (int) cs_strtod(name, NULL); v7_array_set(ctx->v7, ctx->frame->val, idx, v); } else { LOG(LL_ERROR, ("Current value is neither object nor array\n")); } } else { /* This is a root value */ assert(ctx->frame == NULL); /* * This value will also be the overall result of JSON parsing * (it's already owned by the `v7_alt_json_parse()`) */ ctx->result = v; } if (token->type == JSON_TYPE_OBJECT_START || token->type == JSON_TYPE_ARRAY_START) { /* New object or array has just started, so we need to allocate a frame * for it */ struct json_parse_frame *new_frame = alloc_json_frame(ctx, v); new_frame->up = ctx->frame; ctx->frame = new_frame; } } v7_disown(ctx->v7, &v); }