diff options
author | Russell King <rmk@armlinux.org.uk> | 2021-09-11 13:57:42 +0100 |
---|---|---|
committer | Russell King <rmk@armlinux.org.uk> | 2021-09-11 13:57:42 +0100 |
commit | 2f45bcf78b77193ad6107fbb92ad0f93eb1ad4fc (patch) | |
tree | 18b115356a92e67e2276a2dbe97139d2237c0f24 | |
parent | acc52e700121120726b42a6dadd8c12f030ba349 (diff) |
Restrict updates to the newest updater
Prevent old update connections from providing updates, closing them if
they attempt an update.
In doing so, this also limits resource usage when we have multiple
updaters connected (which should never happen.)
Signed-off-by: Russell King <rmk@armlinux.org.uk>
-rw-r--r-- | event-httpd.c | 14 | ||||
-rw-r--r-- | resource.c | 34 | ||||
-rw-r--r-- | resource.h | 3 |
3 files changed, 44 insertions, 7 deletions
diff --git a/event-httpd.c b/event-httpd.c index 27590dd..865f82f 100644 --- a/event-httpd.c +++ b/event-httpd.c @@ -97,19 +97,28 @@ static void update(GObject *source, GAsyncResult *res, gpointer user_data) GError *error = NULL; char *line; gsize len; + int r; line = g_data_input_stream_read_line_finish(c->data, res, &len, &error); if (error || !line) { + if (c->resource->ops->update_close) + c->resource->ops->update_close(c, c->resource); + if (error) g_free(error); close_client(c); return; } - c->resource->ops->update(c, c->resource, line); + r = c->resource->ops->update(c, c->resource, line); g_free(line); + // If the update function returns an error, close this connection. + // This could be because we have a "newer" updater, or an error. + if (r == -1) + close_client(c); + g_data_input_stream_read_line_async(c->data, 0, NULL, update, c); } @@ -257,6 +266,9 @@ static void receive(GObject *source, GAsyncResult *res, gpointer user_data) return; } + if (resource->ops->update_open) + resource->ops->update_open(c, resource); + g_data_input_stream_read_line_async(c->data, 0, NULL, update, c); break; @@ -73,6 +73,20 @@ static int object_v1_get(struct client *c, struct resource *r) return 1; } +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 void object_v1_update_open(struct client *c, struct resource *r) +{ + // Keep track of the latest updater + r->updater = c; +} + // Update all attached clients with the new resource object data static int object_v1_update(struct client *c, struct resource *r, const char *m) { @@ -80,6 +94,12 @@ static int object_v1_update(struct client *c, struct resource *r, const char *m) GString *string; char *n; + // Only allow the latest updater to provide updates, in case we + // end up with multiple connections. This also allows us to quickly + // release the resources of a stale connection. + if (r->updater != c) + return -1; + // Remove any trailing whitespace. n = g_strchomp(g_strdup(m)); @@ -97,18 +117,20 @@ static int object_v1_update(struct client *c, struct resource *r, const char *m) return 0; } -static void object_v1_close(struct client *c, struct resource *r) +static void object_v1_update_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); + // If the updater goes away, clear the data so we don't serve + // stale data. + if (r->updater == c) + r->updater = NULL; } static const struct resource_ops resource_v1_ops = { .get = object_v1_get, - .update = object_v1_update, .close = object_v1_close, + .update_open = object_v1_update_open, + .update = object_v1_update, + .update_close = object_v1_update_close, }; static struct resource resource_position1 = { @@ -9,11 +9,14 @@ struct resource; struct resource_ops { int (*get)(struct client *c, struct resource *r); void (*close)(struct client *c, struct resource *r); + void (*update_open)(struct client *c, struct resource *r); int (*update)(struct client *c, struct resource *r, const char *m); + void (*update_close)(struct client *c, struct resource *r); }; struct resource { const struct resource_ops *ops; + struct client *updater; void *data; }; |