summaryrefslogtreecommitdiff
path: root/resource.c
blob: 249029e9c795e4326e743eabde37a1ed5d1bb3b1 (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// 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 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);
}