/* * Serve static files. * * Takes an object containing mongoose http server options. * Commonly used properties: * - `document_root`: Path to the web root directory * - `enable_directory_listing`: Set to "no" to disable directory listing. * Enabled by default. * - `extra_headers`: Extra HTTP headers to add to each server response. * * For the full option object definition see: * https://docs.cesanta.com/mongoose/dev/index.html#/c-api/http.h/struct_mg_serve_http_opts/ */ SJ_PRIVATE enum v7_err Http_response_serve(struct v7 *v7, v7_val_t *res) { struct mg_serve_http_opts opts; struct http_message hm; enum v7_err rcode = V7_OK; DECLARE_CONN(); size_t i, n; v7_val_t request = v7_get(v7, v7_get_this(v7), "_r", ~0); v7_val_t url_v = v7_get(v7, request, "url", ~0); const char *url = v7_get_string_data(v7, &url_v, &n); const char *quest = strchr(url, '?'); memset(&opts, 0, sizeof(opts)); memset(&hm, 0, sizeof(hm)); /* Set up "fake" parsed HTTP message */ hm.uri.p = url; hm.uri.len = quest == NULL ? n : n - (quest - url); if (v7_argc(v7) > 0) { populate_opts_from_js_argument(v7, v7_arg(v7, 0), &opts); } mg_serve_http(c, &hm, opts); for (i = 0; i < ARRAY_SIZE(s_map); i++) { free(*(char **) ((char *) &opts + s_map[i].offset)); } *res = v7_get_this(v7); clean: return rcode; }
SJ_PRIVATE enum v7_err Http_response_writeHead(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; DECLARE_CONN(); unsigned long code = 200; v7_val_t arg0 = v7_arg(v7, 0), arg1 = v7_arg(v7, 1); if (v7_is_truthy(v7, v7_get(v7, v7_get_this(v7), "_whd", ~0))) { rcode = v7_throwf(v7, "Error", "Headers already sent"); goto clean; } if (v7_is_number(arg0)) { code = v7_to_number(arg0); } write_http_status(c, code); http_write_headers(v7, arg1, c); mg_send(c, "\r\n", 2); v7_set(v7, v7_get_this(v7), "_whd", ~0, v7_mk_boolean(1)); *res = v7_get_this(v7); clean: return rcode; }
SJ_PRIVATE enum v7_err Http_response_write(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; DECLARE_CONN(); if (!v7_is_truthy(v7, v7_get(v7, v7_get_this(v7), "_whd", ~0))) { write_http_status(c, 200); mg_send(c, "\r\n", 2); v7_set(v7, v7_get_this(v7), "_whd", ~0, v7_mk_boolean(1)); } Http_write_data(v7, c); *res = v7_get_this(v7); clean: return rcode; }
/* * `on` method on a mqtt client object registers an event handler * for a given event type. Recognized event types: * * - `connect`: invoked on sucessfull connection (mqtt connack message) * - `error`: invoked on connection error * - `close`: invoked when the connection gets closed * - `message`: invoked when a new message is received. The callback * receives (topic, message) arguments, both strings */ enum v7_err MQTT_on(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; v7_val_t evv = v7_arg(v7, 0); v7_val_t cb = v7_arg(v7, 1); const char *ev, *key = NULL; ev = v7_get_cstring(v7, &evv); if (strcmp(ev, "connect") == 0) { key = SJ_MQTT_CONNECT_CB; } else if (strcmp(ev, "message") == 0) { key = SJ_MQTT_MESSAGE_CB; } else if (strcmp(ev, "error") == 0) { key = SJ_MQTT_ERROR_CB; } else if (strcmp(ev, "close") == 0) { key = SJ_MQTT_CLOSE_CB; } else { rcode = v7_throwf(v7, "Error", "unsupported protocol"); goto clean; } v7_def(v7, v7_get_this(v7), key, ~0, V7_DESC_ENUMERABLE(0), cb); (void) res; clean: return rcode; }
/* * Subscribes a mqtt client to a topic. */ enum v7_err MQTT_subscribe(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; struct user_data *ud; struct mg_connection *nc; struct mg_mqtt_topic_expression expr; v7_val_t topicv = v7_arg(v7, 0); const char *topic; nc = v7_get_ptr(v7, v7_get(v7, v7_get_this(v7), "_nc", ~0)); if (nc == NULL) { rcode = v7_throwf(v7, "Error", "unsupported protocol"); goto clean; } ud = (struct user_data *) nc->user_data; topic = v7_get_cstring(v7, &topicv); if (topic == NULL || strlen(topic) == 0) { rcode = v7_throwf(v7, "TypeError", "invalid topic"); goto clean; } expr.topic = topic; expr.qos = 0; mg_mqtt_subscribe(nc, &expr, 1, ud->msgid++); *res = v7_mk_boolean(v7, 1); clean: return rcode; }
/* * Publishes a message to a topic. * * Args: * - `topic`: topic, string. * - `message`: message, string. * * Only QOS 0 is implemented at the moment. */ enum v7_err MQTT_publish(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; struct user_data *ud; struct mg_connection *nc; const char *topic; const char *message; size_t message_len; v7_val_t topicv = v7_arg(v7, 0), messagev = v7_arg(v7, 1); (void) res; topic = v7_get_cstring(v7, &topicv); if (topic == NULL || strlen(topic) == 0) { rcode = v7_throwf(v7, "TypeError", "invalid topic"); goto clean; } if (!v7_is_string(messagev)) { rcode = v7_throwf(v7, "TypeError", "invalid message"); goto clean; } message = v7_get_string(v7, &messagev, &message_len); nc = v7_get_ptr(v7, v7_get(v7, v7_get_this(v7), "_nc", ~0)); if (nc == NULL) { rcode = v7_throwf(v7, "Error", "invalid connection"); goto clean; } ud = (struct user_data *) nc->user_data; mg_mqtt_publish(nc, topic, ud->msgid++, MG_MQTT_QOS(0), message, message_len); clean: return rcode; }
/* * Usage: * * new UART("platform_specific_name") * */ static enum v7_err UART_ctor(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; v7_val_t this_obj = v7_get_this(v7); v7_val_t dev = v7_arg(v7, 0); struct user_data *ud; void *uart; const char *name; size_t len; if (!v7_is_string(dev)) { rcode = v7_throwf(v7, "Error", "device must be string"); goto clean; } ud = (struct user_data *) calloc(1, sizeof(struct user_data)); ud->v7 = v7; ud->want = 0; ud->cb = v7_create_undefined(); v7_own(v7, &ud->cb); name = v7_get_string_data(v7, &dev, &len); uart = sj_hal_open_uart(name, (void *) ud); if (uart == NULL) { rcode = v7_throwf(v7, "Error", "cannot open uart"); goto clean; } v7_set(v7, this_obj, "_ud", ~0, V7_PROPERTY_HIDDEN, v7_create_foreign(ud)); v7_set(v7, this_obj, "_dev", ~0, V7_PROPERTY_HIDDEN, v7_create_foreign(uart)); clean: return rcode; }
/* * Read the content of the UART. It does not block. * Optional `max_len` parameter, defaults to max size_t. */ static enum v7_err UART_read(struct v7 *v7, v7_val_t *res) { v7_val_t this_obj = v7_get_this(v7); v7_val_t dev = v7_get(v7, this_obj, "_dev", ~0), maxv = v7_arg(v7, 0); size_t max = v7_is_number(maxv) ? (size_t) v7_to_number(maxv) : ~0; *res = sj_hal_read_uart(v7, v7_to_foreign(dev), max); return V7_OK; }
static enum v7_err esp_sj_uart_get_state(struct v7 *v7, struct esp_sj_uart_state **us) { int uart_no = v7_to_number(v7_get(v7, v7_get_this(v7), "_u", 2)); if (uart_no < 0 || uart_no > 1) { return v7_throwf(v7, "Error", "Invalid UART number"); } *us = &sj_us[uart_no]; return V7_OK; }
SJ_PRIVATE enum v7_err Http_request_abort(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; DECLARE_CONN(); c->flags |= MG_F_CLOSE_IMMEDIATELY; *res = v7_get_this(v7); clean: return rcode; }
SJ_PRIVATE enum v7_err Http_request_write(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; DECLARE_CONN(); Http_write_data(v7, c); *res = v7_get_this(v7); clean: return rcode; }
/* 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; }
static enum v7_err UART_write(struct v7 *v7, v7_val_t *res) { v7_val_t this_obj = v7_get_this(v7); v7_val_t dev = v7_get(v7, this_obj, "_dev", ~0), data = v7_arg(v7, 0); size_t len; const char *d = v7_get_string_data(v7, &data, &len); (void) v7; (void) this_obj; sj_hal_write_uart(v7_to_foreign(dev), d, len); return V7_OK; }
SJ_PRIVATE enum v7_err Http_request_set_timeout(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; DECLARE_CONN(); struct user_data *ud = (struct user_data *) c->user_data; mg_set_timer(c, time(NULL) + v7_to_number(v7_arg(v7, 0)) / 1000.0); ud->timeout_callback = v7_arg(v7, 1); v7_own(v7, &ud->timeout_callback); *res = v7_get_this(v7); clean: return rcode; }
static enum v7_err MyThing_ctor(struct v7 *v7, v7_val_t *res) { v7_val_t this_obj = v7_get_this(v7); v7_val_t arg0 = v7_arg(v7, 0); v7_def(v7, this_obj, "__arg", ~0 /* = strlen */, V7_DESC_ENUMERABLE(0), arg0); /* * A constructor function can access the newly created object with * `v7_get_this()`. The constructor can override the result of the `new` * expression by explicitly setting `res` to another object. Any non-object * value set into `res` will be ignored. This matches the JavaScript * constructor return value semantics. */ (void) res; /* no warning */ return V7_OK; }
SJ_PRIVATE enum v7_err Http_response_end(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; DECLARE_CONN(); rcode = Http_response_write(v7, res); if (rcode != V7_OK) { goto clean; } mg_send_http_chunk(c, "", 0); *res = v7_get_this(v7); clean: return rcode; }
/* * Regiter a callback to be invoked when there is at least N bytes available. * N defaults to maxint if undefined */ static enum v7_err UART_recv(struct v7 *v7, v7_val_t *res) { v7_val_t this_obj = v7_get_this(v7); v7_val_t cb = v7_arg(v7, 0); v7_val_t wantv = v7_arg(v7, 1); v7_val_t udv = v7_get(v7, this_obj, "_ud", ~0); size_t want = v7_is_number(wantv) ? (size_t) v7_to_number(wantv) : ~0; struct user_data *ud = (struct user_data *) v7_to_foreign(udv); ud->cb = cb; v7_own(v7, &ud->cb); ud->want = want; /* TODO(mkm): trigger cb if there is already something in the buffer */ return V7_OK; }
SJ_PRIVATE enum v7_err Http_request_end(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; DECLARE_CONN(); rcode = Http_request_write(v7, res); if (rcode != V7_OK) { goto clean; } mg_send_http_chunk(c, "", 0); c->flags |= MG_F_CLOSE_CONNECTION_AFTER_RESPONSE; *res = v7_get_this(v7); clean: return rcode; }
static v7_val_t MyThing_myMethod(struct v7 *v7) { v7_val_t this_obj = v7_get_this(v7); return v7_get(v7, this_obj, "__arg", ~0); }
static v7_val_t MyThing_ctor(struct v7 *v7) { v7_val_t this_obj = v7_get_this(v7); v7_val_t arg0 = v7_arg(v7, 0); v7_set(v7, this_obj, "__arg", ~0 /* = strlen */, V7_PROPERTY_DONT_ENUM, arg0); return this_obj; }
/* * Same as `get_mgconn_obj()`, but uses `this` as an `obj`. */ static struct mg_connection *get_mgconn(struct v7 *v7) { return get_mgconn_obj(v7, v7_get_this(v7)); }
static enum v7_err MyThing_myMethod(struct v7 *v7, v7_val_t *res) { v7_val_t this_obj = v7_get_this(v7); *res = v7_get(v7, this_obj, "__arg", ~0); return V7_OK; }