summaryrefslogtreecommitdiff
path: root/resource.c
blob: 085a66a28191e73331d66b9be117e4c442cecca9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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;
	GString *string;
};

static struct resource_object position_object;
static struct resource_object signal_object;

static void object_v1_send(struct client *c, struct resource_object *obj)
{
	GString *string = obj->string;

	if (!string)
		return;

	respond_chunk(c, string);
}

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;
	GString *string, *old;
	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);
	old = obj->string;
	obj->string = string;
	g_string_free(old, TRUE);
	g_free(n);

	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);
}