mirror of
https://github.com/netdata/netdata.git
synced 2025-05-05 01:30:32 +00:00

* new routing for web requests * renamed and better error control * add missing return statements * do not serve files when no file extension is given * restore the api of the functions; use internal web_client flags to keep state; support redirects to fix directories * add window.location.hash to url redirect * do not redirect when sending to specific dashboard version and there are data after the version * uniform function to append slash to URL * remove obsolete proxy https flag
149 lines
5.1 KiB
C
149 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#define WEB_SERVER_INTERNALS 1
|
|
#include "web_client_cache.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// allocate and free web_clients
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// web clients caching
|
|
|
|
// When clients connect and disconnect, avoid allocating and releasing memory.
|
|
// Instead, when new clients get connected, reuse any memory previously allocated
|
|
// for serving web clients that are now disconnected.
|
|
|
|
// The size of the cache is adaptive. It caches the structures of 2x
|
|
// the number of currently connected clients.
|
|
|
|
static struct clients_cache {
|
|
struct {
|
|
SPINLOCK spinlock;
|
|
struct web_client *head; // the structures of the currently connected clients
|
|
size_t count; // the count the currently connected clients
|
|
|
|
size_t allocated; // the number of allocations
|
|
size_t reused; // the number of re-uses
|
|
} used;
|
|
|
|
struct {
|
|
SPINLOCK spinlock;
|
|
struct web_client *head; // the cached structures, available for future clients
|
|
size_t count; // the number of cached structures
|
|
} avail;
|
|
} web_clients_cache = {
|
|
.used = {
|
|
.spinlock = NETDATA_SPINLOCK_INITIALIZER,
|
|
.head = NULL,
|
|
.count = 0,
|
|
.reused = 0,
|
|
.allocated = 0,
|
|
},
|
|
.avail = {
|
|
.spinlock = NETDATA_SPINLOCK_INITIALIZER,
|
|
.head = NULL,
|
|
.count = 0,
|
|
},
|
|
};
|
|
|
|
// destroy the cache and free all the memory it uses
|
|
void web_client_cache_destroy(void) {
|
|
internal_error(true, "web_client_cache has %zu used and %zu available clients, allocated %zu, reused %zu (hit %zu%%)."
|
|
, web_clients_cache.used.count
|
|
, web_clients_cache.avail.count
|
|
, web_clients_cache.used.allocated
|
|
, web_clients_cache.used.reused
|
|
, (web_clients_cache.used.allocated + web_clients_cache.used.reused)?(web_clients_cache.used.reused * 100 / (web_clients_cache.used.allocated + web_clients_cache.used.reused)):0
|
|
);
|
|
|
|
struct web_client *w, *t;
|
|
|
|
spinlock_lock(&web_clients_cache.avail.spinlock);
|
|
w = web_clients_cache.avail.head;
|
|
while(w) {
|
|
t = w;
|
|
w = w->cache.next;
|
|
web_client_free(t);
|
|
}
|
|
web_clients_cache.avail.head = NULL;
|
|
web_clients_cache.avail.count = 0;
|
|
spinlock_unlock(&web_clients_cache.avail.spinlock);
|
|
|
|
// DO NOT FREE THEM IF THEY ARE USED
|
|
// spinlock_lock(&web_clients_cache.used.spinlock);
|
|
// w = web_clients_cache.used.head;
|
|
// while(w) {
|
|
// t = w;
|
|
// w = w->next;
|
|
// web_client_free(t);
|
|
// }
|
|
// web_clients_cache.used.head = NULL;
|
|
// web_clients_cache.used.count = 0;
|
|
// web_clients_cache.used.reused = 0;
|
|
// web_clients_cache.used.allocated = 0;
|
|
// spinlock_unlock(&web_clients_cache.used.spinlock);
|
|
}
|
|
|
|
struct web_client *web_client_get_from_cache(void) {
|
|
spinlock_lock(&web_clients_cache.avail.spinlock);
|
|
struct web_client *w = web_clients_cache.avail.head;
|
|
if(w) {
|
|
// get it from avail
|
|
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next);
|
|
web_clients_cache.avail.count--;
|
|
spinlock_unlock(&web_clients_cache.avail.spinlock);
|
|
|
|
web_client_reuse_from_cache(w);
|
|
|
|
spinlock_lock(&web_clients_cache.used.spinlock);
|
|
web_clients_cache.used.reused++;
|
|
}
|
|
else {
|
|
spinlock_unlock(&web_clients_cache.avail.spinlock);
|
|
|
|
// allocate it
|
|
w = web_client_create(&netdata_buffers_statistics.buffers_web);
|
|
|
|
spinlock_lock(&web_clients_cache.used.spinlock);
|
|
web_clients_cache.used.allocated++;
|
|
}
|
|
|
|
// link it to used web clients
|
|
DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
|
|
web_clients_cache.used.count++;
|
|
spinlock_unlock(&web_clients_cache.used.spinlock);
|
|
|
|
// initialize it
|
|
w->use_count++;
|
|
w->id = global_statistics_web_client_connected();
|
|
w->mode = WEB_CLIENT_MODE_GET;
|
|
|
|
return w;
|
|
}
|
|
|
|
void web_client_release_to_cache(struct web_client *w) {
|
|
|
|
#ifdef ENABLE_HTTPS
|
|
netdata_ssl_close(&w->ssl);
|
|
#endif
|
|
|
|
// unlink it from the used
|
|
spinlock_lock(&web_clients_cache.used.spinlock);
|
|
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
|
|
ssize_t used_count = (ssize_t)--web_clients_cache.used.count;
|
|
spinlock_unlock(&web_clients_cache.used.spinlock);
|
|
|
|
spinlock_lock(&web_clients_cache.avail.spinlock);
|
|
if(w->use_count > 100 || (used_count > 0 && web_clients_cache.avail.count >= 2 * (size_t)used_count) || (used_count <= 10 && web_clients_cache.avail.count >= 20)) {
|
|
spinlock_unlock(&web_clients_cache.avail.spinlock);
|
|
|
|
// we have too many of them - free it
|
|
web_client_free(w);
|
|
}
|
|
else {
|
|
// link it to the avail
|
|
DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next);
|
|
web_clients_cache.avail.count++;
|
|
spinlock_unlock(&web_clients_cache.avail.spinlock);
|
|
}
|
|
}
|