summaryrefslogtreecommitdiff
path: root/resource.c
diff options
context:
space:
mode:
authorRussell King <rmk@armlinux.org.uk>2021-04-16 14:01:05 +0100
committerRussell King <rmk@armlinux.org.uk>2021-04-16 14:01:05 +0100
commit9d7b0311d5948fca09b19dbe27e5f506e2e303e0 (patch)
tree1accaf615f2f744232fceed6a9a8ceca6cde7c53 /resource.c
event-httpd: add event httpd support
Add event httpd, which is a small and basic http server designed to provide server sent events to clients. The server accepts updates through a simple "UPDATE" method which are then broadcast to clients listening using the "GET" method. Signed-off-by: Russell King <rmk@armlinux.org.uk>
Diffstat (limited to 'resource.c')
-rw-r--r--resource.c105
1 files changed, 105 insertions, 0 deletions
diff --git a/resource.c b/resource.c
new file mode 100644
index 0000000..c1473e7
--- /dev/null
+++ b/resource.c
@@ -0,0 +1,105 @@
+// Copyright (C) 2021 Russell King.
+// Licensed under GPL version 2. See COPYING.
+#include <gio/gio.h>
+#include "event-httpd.h"
+#include "resource.h"
+
+struct resource_object {
+ GList *client_list;
+ char *str;
+};
+
+static struct resource_object position_object;
+static struct resource_object signal_object;
+
+static void object_v1_send(struct client *c, struct resource_object *obj)
+{
+ const char *str = obj->str;
+ GString *s;
+
+ if (!str)
+ return;
+
+ // Format the text/event-stream response
+ // The double newline terminates this event
+ // See https://www.html5rocks.com/en/tutorials/eventsource/basics/
+ s = g_string_sized_new(1024);
+ g_string_printf(s, "data:%s\n", str);
+ g_string_append_c(s, '\n');
+
+ respond_chunk(c, s);
+ g_string_free(s, TRUE);
+}
+
+static int object_v1_get(struct client *c, struct resource *r)
+{
+ struct resource_object *obj = r->data;
+
+ respond_header(c, 200, "OK",
+ "Cache-Control: no-cache\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Type: text/event-stream; charset=UTF-8\r\n"
+ "Transfer-Encoding: chunked\r\n");
+
+ object_v1_send(c, obj);
+
+ // Add this client to the object list so it receives updates
+ obj->client_list = g_list_append(obj->client_list, c);
+
+ return 1;
+}
+
+// Update this client with the new resource object data
+static void object_v1_client_update(gpointer data, gpointer user_data)
+{
+ struct resource_object *obj = user_data;
+ struct client *c = data;
+
+ object_v1_send(c, obj);
+}
+
+// Update all attached clients with the new resource object data
+static int object_v1_update(struct client *c, struct resource *r, const char *m)
+{
+ struct resource_object *obj = r->data;
+ char *n, *o;
+
+ n = g_strdup(m);
+ g_strchomp(n);
+ o = obj->str;
+ obj->str = n;
+ if (o)
+ g_free(o);
+
+ g_list_foreach(obj->client_list, object_v1_client_update, obj);
+
+ return 0;
+}
+
+static void object_v1_close(struct client *c, struct resource *r)
+{
+ struct resource_object *obj = r->data;
+
+ // Remove this client from the object list
+ obj->client_list = g_list_remove(obj->client_list, c);
+}
+
+static struct resource resource_position1 = {
+ .get = object_v1_get,
+ .update = object_v1_update,
+ .close = object_v1_close,
+ .data = &position_object,
+};
+
+static struct resource resource_signal1 = {
+ .get = object_v1_get,
+ .update = object_v1_update,
+ .close = object_v1_close,
+ .data = &signal_object,
+};
+
+void resource_init(GHashTable *hash)
+{
+ g_hash_table_insert(hash, "/api/1/position", &resource_position1);
+ g_hash_table_insert(hash, "/api/1/signal", &resource_signal1);
+}