diff --git a/include/villas/api.h b/include/villas/api.h index cd9f7d084..bac33baf2 100644 --- a/include/villas/api.h +++ b/include/villas/api.h @@ -20,7 +20,7 @@ struct lws; struct super_node; struct api; -struct api_ressource; +struct api_action; /** Callback type of command function * @@ -29,7 +29,7 @@ struct api_ressource; * @param[out] resp JSON command response. * @param[in] i Execution context. */ -typedef int (*api_cb_t)(struct api_ressource *c, json_t *args, json_t **resp, struct api_session *s); +typedef int (*api_cb_t)(struct api_action *c, json_t *args, json_t **resp, struct api_session *s); struct api { struct list sessions; /**< List of currently active connections */ @@ -39,11 +39,8 @@ struct api { struct super_node *super_node; }; -/** Command descriptor - * - * Every command is described by a descriptor. - */ -struct api_ressource { +/** API action descriptor */ +struct api_action { api_cb_t cb; }; @@ -59,12 +56,6 @@ int api_start(struct api *a); int api_stop(struct api *a); -int api_session_init(struct api_session *s, struct api *a, enum api_mode m); - -int api_session_destroy(struct api_session *s); - -int api_session_run_command(struct api_session *s, json_t *req, json_t **resp); - /** Libwebsockets callback for "api" endpoint */ int api_ws_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); diff --git a/include/villas/plugin.h b/include/villas/plugin.h index 70bfc8067..6ea701a0b 100644 --- a/include/villas/plugin.h +++ b/include/villas/plugin.h @@ -48,7 +48,7 @@ struct plugin { int (*unload)(struct plugin *p); union { - struct api_ressource api; + struct api_action api; struct node_type node; struct fpga_ip_type ip; struct hook_type hook; diff --git a/lib/api.c b/lib/api.c index b6e2f80a6..ae2c6bd0f 100644 --- a/lib/api.c +++ b/lib/api.c @@ -6,109 +6,70 @@ #include -#include "plugin.h" #include "api.h" #include "log.h" +#include "web.h" #include "config.h" - -{ - - - - - - - +#include "assert.h" +#include "compat.h" int api_ws_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) -{ - //struct api_session *s = (struct api_session *) user; - - switch (reason) { - default: - break; - } - - return 0; -} - -int api_http_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { int ret; + struct web *w = lws_context_user(lws_get_context(wsi)); struct api_session *s = (struct api_session *) user; switch (reason) { - case LWS_CALLBACK_ESTABLISHED: { - struct web *w = (struct web *) lws_context_user(lws_get_context(wsi)); - - if (w->api == NULL) - return -1; /** @todo return error message */ - - api_session_init(s, w->api, API_MODE_WS); - break; - } - - case LWS_CALLBACK_HTTP: { - struct web *w = (struct web *) lws_context_user(lws_get_context(wsi)); - - char *uri = (char *) in; + case LWS_CALLBACK_ESTABLISHED: + if (w->api == NULL) { + lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (unsigned char *) "API disabled", strlen("API disabled")); + return -1; + } /* Parse request URI */ - ret = sscanf(uri, "/api/v%d", (int *) &s->version); + char uri[64]; + lws_hdr_copy(wsi, uri, sizeof(uri), WSI_TOKEN_GET_URI); /* The path component of the*/ + + ret = sscanf(uri, "/v%d", (int *) &s->version); if (ret != 1) return -1; - - debug(LOG_API, "New REST API session initiated: version = %d", s->version); - api_session_init(s, w->api, API_MODE_HTTP); - - /* Prepare HTTP response header */ - const char headers[] = "HTTP/1.1 200 OK\r\n" - "Content-type: application/json\r\n" - "User-agent: " USER_AGENT "\r\n" - "\r\n"; - - api_buffer_append(&s->response.headers, headers, sizeof(headers)-1); - - /* book us a LWS_CALLBACK_HTTP_WRITEABLE callback */ - lws_callback_on_writable(wsi); + ret = api_session_init(s, w->api, API_MODE_WS); + if (ret) + return -1; + debug(LOG_API, "New API session initiated: version=%d, mode=websocket", s->version); break; - } - case LWS_CALLBACK_CLIENT_RECEIVE: - case LWS_CALLBACK_RECEIVE: - case LWS_CALLBACK_HTTP_BODY: { - api_buffer_append(&s->request.body, in, len); + case LWS_CALLBACK_CLOSED: + ret = api_session_destroy(s); + if (ret) + return -1; - json_t *req, *resp; - while (api_parse_request(&s->request.body, &req) == 1) { - api_session_run_command(s, req, &resp); - api_unparse_response(&s->response.body, resp); - - lws_callback_on_writable(wsi); - } + debug(LOG_API, "Closed API session"); break; - } - - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - s->completed = true; - break; case LWS_CALLBACK_SERVER_WRITEABLE: - case LWS_CALLBACK_HTTP_WRITEABLE: - /* We send headers only in HTTP mode */ - if (s->mode == API_MODE_HTTP) - api_buffer_send(&s->response.headers, wsi, LWS_WRITE_HTTP_HEADERS); + web_buffer_write(&s->response.body, wsi); - api_buffer_send(&s->response.body, wsi, LWS_WRITE_HTTP); - if (s->completed && s->response.body.len == 0) return -1; break; + + case LWS_CALLBACK_RECEIVE: + web_buffer_append(&s->request.body, in, len); + + json_t *req, *resp; + while (web_buffer_read_json(&s->request.body, &req) >= 0) { + api_session_run_command(s, req, &resp); + + web_buffer_append_json(&s->response.body, resp); + lws_callback_on_writable(wsi); + } + break; default: return 0; @@ -117,32 +78,75 @@ int api_http_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void return 0; } -int api_buffer_send(struct api_buffer *b, struct lws *w, enum lws_write_protocol prot) +int api_http_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { - int sent; - - if (b->len <= 0) - return 0; + int ret; - sent = lws_write(w, (unsigned char *) b->buf, b->len, prot); - if (sent > 0) { - memmove(b->buf, b->buf + sent, sent); - b->len -= sent; + struct web *w = lws_context_user(lws_get_context(wsi)); + struct api_session *s = (struct api_session *) user; + + switch (reason) { + case LWS_CALLBACK_HTTP: + if (w->api == NULL) { + lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (unsigned char *) "API disabled", strlen("API disabled")); + return -1; + } + + /* Parse request URI */ + ret = sscanf(in, "/api/v%d", (int *) &s->version); + if (ret != 1) + return -1; + + ret = api_session_init(s, w->api, API_MODE_HTTP); + if (ret) + return -1; + + debug(LOG_API, "New API session initiated: version=%d, mode=http", s->version); + + /* Prepare HTTP response header */ + const char headers[] = "HTTP/1.1 200 OK\r\n" + "Content-type: application/json\r\n" + "User-agent: " USER_AGENT "\r\n" + "\r\n"; + + web_buffer_append(&s->response.headers, headers, sizeof(headers)-1); + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_CLOSED_HTTP: + ret = api_session_destroy(s); + if (ret) + return -1; + break; + + case LWS_CALLBACK_HTTP_BODY: + web_buffer_append(&s->request.body, in, len); + + json_t *req, *resp; + while (web_buffer_read_json(&s->request.body, &req) == 1) { + api_session_run_command(s, req, &resp); + + web_buffer_append_json(&s->response.body, resp); + lws_callback_on_writable(wsi); + } + break; + + case LWS_CALLBACK_HTTP_BODY_COMPLETION: + s->completed = true; + break; + + case LWS_CALLBACK_HTTP_WRITEABLE: + web_buffer_write(&s->response.headers, wsi); + web_buffer_write(&s->response.body, wsi); + + if (s->completed && s->response.body.len == 0) + return -1; + break; + + default: + return 0; } - - return sent; -} -int api_buffer_append(struct api_buffer *b, const char *in, size_t len) -{ - b->buf = realloc(b->buf, b->len + len); - if (!b->buf) - return -1; - - memcpy(b->buf + b->len, in, len); - - b->len += len; - return 0; } @@ -186,26 +190,3 @@ int api_stop(struct api *a) return 0; } - -int api_session_init(struct api_session *s, struct api *a, enum api_mode m) -{ - s->mode = m; - s->api = a; - - s->completed = false; - - s->request.body = - s->response.body = - s->response.headers = (struct api_buffer) { - .buf = NULL, - .size = 0, - .len = 0 - }; - - return 0; -} - -int api_session_destroy(struct api_session *s) -{ - return 0; -} diff --git a/lib/apis/Makefile.inc b/lib/apis/Makefile.inc deleted file mode 100644 index bfe1a1d4f..000000000 --- a/lib/apis/Makefile.inc +++ /dev/null @@ -1 +0,0 @@ -LIB_SRCS += $(wildcard lib/apis/*.c) \ No newline at end of file diff --git a/lib/apis/config.c b/lib/apis/config.c deleted file mode 100644 index ae41e6e53..000000000 --- a/lib/apis/config.c +++ /dev/null @@ -1,31 +0,0 @@ -/** The "config" command - * - * @author Steffen Vogel - * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC - * This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited. - *********************************************************************************/ - -#include - -#include "api.h" -#include "utils.h" -#include "plugin.h" - -static int api_config(struct api_ressource *h, json_t *args, json_t **resp, struct api_session *s) -{ - config_setting_t *cfg_root = config_root_setting(&s->api->super_node->cfg); - - *resp = cfg_root ? config_to_json(cfg_root) : json_object(); - - return 0; -} - -static struct plugin p = { - .name = "config", - .description = "retrieve current VILLASnode configuration", - .type = PLUGIN_TYPE_API, - .api.cb = api_config -}; - -REGISTER_PLUGIN(&p) \ No newline at end of file diff --git a/lib/apis/nodes.c b/lib/apis/nodes.c deleted file mode 100644 index b65038d3a..000000000 --- a/lib/apis/nodes.c +++ /dev/null @@ -1,52 +0,0 @@ -/** The "nodes" command - * - * @author Steffen Vogel - * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC - * This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited. - *********************************************************************************/ - -#include - -#include "plugin.h" -#include "api.h" -#include "node.h" -#include "utils.h" - -extern struct list nodes; - -static int api_nodes(struct api_ressource *r, json_t *args, json_t **resp, struct api_session *s) -{ - json_t *json_nodes = json_array(); - - for (size_t i = 0; i < list_length(&s->api->super_node->nodes); i++) { - struct node *n = list_at(&s->api->super_node->nodes, i); - - json_t *json_node = json_pack("{ s: s, s: i, s: i, s: i, s: i }", - "name", node_name_short(n), - "state", n->state, - "vectorize", n->vectorize, - "affinity", n->affinity, - "id", i - ); - - /* Add all additional fields of node here. - * This can be used for metadata */ - json_object_update(json_node, config_to_json(n->cfg)); - - json_array_append_new(json_nodes, json_node); - } - - *resp = json_nodes; - - return 0; -} - -static struct plugin p = { - .name = "nodes", - .description = "retrieve list of all known nodes", - .type = PLUGIN_TYPE_API, - .api.cb = api_nodes -}; - -REGISTER_PLUGIN(&p) \ No newline at end of file diff --git a/lib/apis/reload.c b/lib/apis/reload.c deleted file mode 100644 index edabfc681..000000000 --- a/lib/apis/reload.c +++ /dev/null @@ -1,25 +0,0 @@ -/** The "reload" command - * - * @author Steffen Vogel - * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC - * This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited. - *********************************************************************************/ - -#include "plugin.h" -#include "api.h" - -/** @todo not implemented yet */ -static int api_reload(struct api_ressource *h, json_t *args, json_t **resp, struct api_session *s) -{ - return -1; -} - -static struct plugin p = { - .name = "reload", - .description = "restart VILLASnode with new configuration", - .type = PLUGIN_TYPE_API, - .api.cb = api_reload -}; - -REGISTER_PLUGIN(&p) \ No newline at end of file diff --git a/lib/web.c b/lib/web.c index 5860b53dd..ffddd1b71 100644 --- a/lib/web.c +++ b/lib/web.c @@ -12,7 +12,7 @@ #include "utils.h" #include "log.h" #include "web.h" -#include "api.h" +#include "api/session.h" #include "nodes/websocket.h"