From 6b8b9e5c1f814ae9bf06481a4cd7dfdfc9ee3f62 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sun, 9 Apr 2017 16:20:39 +0200 Subject: [PATCH] refactor: move api_buffer to web/buffer --- include/villas/api.h | 13 ---- include/villas/web/buffer.h | 56 ++++++++++++++ lib/api.c | 37 --------- lib/web/Makefile.inc | 1 + lib/web/buffer.c | 144 ++++++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+), 50 deletions(-) create mode 100644 include/villas/web/buffer.h create mode 100644 lib/web/Makefile.inc create mode 100644 lib/web/buffer.c diff --git a/include/villas/api.h b/include/villas/api.h index 6d7c6c51d..abca157f5 100644 --- a/include/villas/api.h +++ b/include/villas/api.h @@ -19,7 +19,6 @@ struct super_node; struct api; struct api_ressource; -struct api_buffer; struct api_session; /** Callback type of command function @@ -49,12 +48,6 @@ struct api { struct super_node *super_node; }; -struct api_buffer { - char *buf; /**< A pointer to the buffer. Usually resized via realloc() */ - size_t size; /**< The allocated size of the buffer. */ - size_t len; /**< The used length of the buffer. */ -}; - /** A connection via HTTP REST or WebSockets to issue API actions. */ struct api_session { enum api_mode mode; @@ -102,12 +95,6 @@ int api_session_destroy(struct api_session *s); int api_session_run_command(struct api_session *s, json_t *req, json_t **resp); -/** Send contents of buffer over libwebsockets connection */ -int api_buffer_send(struct api_buffer *b, struct lws *w, enum lws_write_protocol prot); - -/** Append received data to buffer. */ -int api_buffer_append(struct api_buffer *b, const char *in, size_t len); - /** 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/web/buffer.h b/include/villas/web/buffer.h new file mode 100644 index 000000000..d33e2fd72 --- /dev/null +++ b/include/villas/web/buffer.h @@ -0,0 +1,56 @@ +/** WebSocket buffer. + * + * @file + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + *********************************************************************************/ + +#pragma once + +#include + +#include + +#include "common.h" + +struct web_buffer { + char *buffer; /**< A pointer to the buffer. Usually resized via realloc() */ + size_t size; /**< The allocated size of the buffer. */ + size_t len; /**< The used length of the buffer. */ + size_t prefix; /**< The used length of the buffer. */ + + enum lws_write_protocol protocol; + + enum state state; +}; + +/** Initialize a libwebsockets buffer. */ +int web_buffer_init(struct web_buffer *b, enum lws_write_protocol prot); + +/** Destroy a libwebsockets buffer. */ +int web_buffer_destroy(struct web_buffer *b); + +/** Flush the buffers contents to lws_write() */ +int web_buffer_write(struct web_buffer *b, struct lws *w); + +/** Copy \p len bytes from the beginning of the buffer and copy them to \p out. + * + * @param out The destination buffer. If NULL, we just remove \p len bytes from the buffer. + */ +int web_buffer_read(struct web_buffer *b, char *out, size_t len); + +/** Parse JSON from the beginning of the buffer. + * + * @retval -1 The buffer is empty. + * @retval -2 The buffer contains malformed JSON. + */ +int web_buffer_read_json(struct web_buffer *b, json_t **req); + +/** Append \p len bytes of \p in at the end of the buffer. + * + * The buffer is automatically resized. + */ +int web_buffer_append(struct web_buffer *b, const char *in, size_t len); + +/** Append the serialized represetnation of the JSON object \p res at the end of the buffer. */ +int web_buffer_append_json(struct web_buffer *b, json_t *res); diff --git a/lib/api.c b/lib/api.c index ed8b27d66..780a14d5c 100644 --- a/lib/api.c +++ b/lib/api.c @@ -11,46 +11,9 @@ #include "log.h" #include "config.h" -static int api_parse_request(struct api_buffer *b, json_t **req) { - json_error_t e; - - if (b->len <= 0) - return -1; - *req = json_loadb(b->buf, b->len, JSON_DISABLE_EOF_CHECK, &e); - if (!*req) - return -1; - if (e.position < b->len) { - void *dst = (void *) b->buf; - void *src = (void *) (b->buf + e.position); - - memmove(dst, src, b->len - e.position); - - b->len -= e.position; - } - else - b->len = 0; - - return 1; -} - -static int api_unparse_response(struct api_buffer *b, json_t *res) -{ - size_t len; - -retry: len = json_dumpb(res, b->buf + b->len, b->size - b->len, 0); - if (len > b->size - b->len) { - b->buf = realloc(b->buf, b->len + len); - b->size += len; - goto retry; - } - else - b->len += len; - - return 0; -} int api_session_run_command(struct api_session *s, json_t *json_in, json_t **json_out) { diff --git a/lib/web/Makefile.inc b/lib/web/Makefile.inc new file mode 100644 index 000000000..43e9306ed --- /dev/null +++ b/lib/web/Makefile.inc @@ -0,0 +1 @@ +LIB_SRCS += $(wildcard lib/web/*.c) \ No newline at end of file diff --git a/lib/web/buffer.c b/lib/web/buffer.c new file mode 100644 index 000000000..ccaface8a --- /dev/null +++ b/lib/web/buffer.c @@ -0,0 +1,144 @@ +/** API buffer for sending and receiving data from libwebsockets. + * + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + *********************************************************************************/ + +#include + +#include "compat.h" +#include "assert.h" +#include "utils.h" +#include "web/buffer.h" + +int web_buffer_init(struct web_buffer *b, enum lws_write_protocol prot) +{ + assert(b->state == STATE_DESTROYED); + + b->protocol = prot; + b->size = 0; + b->len = 0; + b->buffer = NULL; + b->prefix = b->protocol == LWS_WRITE_TEXT || b->protocol == LWS_WRITE_BINARY ? LWS_PRE : 0; + + b->state = STATE_INITIALIZED; + + return 0; +} + +int web_buffer_destroy(struct web_buffer *b) +{ + if (b->state == STATE_DESTROYED) + return 0; + + if (b->buffer) + free(b->buffer); + + b->state = STATE_DESTROYED; + + return 0; +} + +int web_buffer_write(struct web_buffer *b, struct lws *w) +{ + int ret, len, sent = 0; + unsigned char *chunk; + + assert(b->state == STATE_INITIALIZED); + + if (b->len <= 0) + return 0; + + do { + chunk = (unsigned char *) b->buffer + b->prefix + sent; + len = strlen(b->buffer + b->prefix); + + ret = lws_write(w, chunk, len, b->protocol); + if (ret < 0) + break; + + sent += ret + 1; + } while (sent < b->len); + + web_buffer_read(b, NULL, sent); /* drop sent bytes from the buffer*/ + + return sent; +} + +int web_buffer_read(struct web_buffer *b, char *out, size_t len) +{ + assert(b->state == STATE_INITIALIZED); + + if (len > b->len) + len = b->len; + + if (out) + memcpy(out, b->buffer + b->prefix, len); + + memmove(b->buffer + b->prefix, b->buffer + b->prefix + len, b->len - len); + b->len -= len; + + return 0; +} + +int web_buffer_read_json(struct web_buffer *b, json_t **req) +{ + json_error_t e; + + assert(b->state == STATE_INITIALIZED); + + if (b->len <= 0) + return -1; + + *req = json_loadb(b->buffer + b->prefix, b->len, JSON_DISABLE_EOF_CHECK, &e); + if (!*req) + return -2; + + web_buffer_read(b, NULL, e.position); + + return 1; +} + +int web_buffer_append(struct web_buffer *b, const char *in, size_t len) +{ + assert(b->state == STATE_INITIALIZED); + + /* We append a \0 to split messages */ + len++; + + if (b->size < b->len + len) { + b->buffer = realloc(b->buffer, b->prefix + b->len + len); + if (!b->buffer) + return -1; + + b->size = b->len + len; + } + + memcpy(b->buffer + b->prefix + b->len, in, len); + b->len += len; + b->buffer[b->len+b->prefix] = 0; + + return 0; +} + +int web_buffer_append_json(struct web_buffer *b, json_t *res) +{ + size_t len; + + assert(b->state == STATE_INITIALIZED); + +retry: len = json_dumpb(res, b->buffer + b->prefix + b->len, b->size - b->len, 0) + 1; + if (b->size < b->len + len) { + b->buffer = realloc(b->buffer, b->prefix + b->len + len); + if (!b->buffer) + return -1; + + b->size = b->len + len; + goto retry; + } + + b->len += len; + b->buffer[b->len+b->prefix] = 0; + + return 0; +}