summaryrefslogtreecommitdiff
path: root/resource.c
blob: c1473e7b7066208a0c55398c35ec7d83a51959a8 (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;
	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);
}