// Copyright (C) 2021 Russell King. // Licensed under GPL version 2. See COPYING. #include #include "event-httpd.h" #include "resource.h" struct resource_object { GList *client_list; GString *string; }; static struct resource_object position_object; static struct resource_object signal_object; static void resource_obj_send(struct client *c, struct resource_object *obj) { GString *string = obj->string; if (!string) return; respond_chunk(c, string); } // Update this client with the new resource object data static void resource_obj_update(gpointer data, gpointer user_data) { struct resource_object *obj = user_data; struct client *c = data; resource_obj_send(c, obj); } static void resource_obj_set_string(struct resource_object *obj, GString *str) { GString *old; old = obj->string; obj->string = str; g_string_free(old, TRUE); g_list_foreach(obj->client_list, resource_obj_update, obj); } static void resource_obj_add_client(struct resource_object *obj, struct client *c) { obj->client_list = g_list_append(obj->client_list, c); } static void resource_obj_del_client(struct resource_object *obj, struct client *c) { obj->client_list = g_list_remove(obj->client_list, c); } 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"); resource_obj_send(c, obj); // Add this client to the object list so it receives updates resource_obj_add_client(obj, c); return 1; } // 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; GString *string; char *n; // Remove any trailing whitespace. n = g_strchomp(g_strdup(m)); // Format the text/event-stream response // We prefix the string with the "data:" tag in preparation for // sending to via the text/event-stream connection. // The double newline terminates this event // See https://www.html5rocks.com/en/tutorials/eventsource/basics/ string = g_string_new(NULL); g_string_printf(string, "data:%s\n\n", n); g_free(n); resource_obj_set_string(obj, string); 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 resource_obj_del_client(obj, 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); }