123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- // SPDX-License-Identifier: GPL-3.0-or-later
- #define WEB_SERVER_INTERNALS 1
- #include "web_client_cache.h"
- // ----------------------------------------------------------------------------
- // allocate and free web_clients
- #ifdef ENABLE_HTTPS
- static void web_client_reuse_ssl(struct web_client *w) {
- if (netdata_ssl_srv_ctx) {
- if (w->ssl.conn) {
- SSL_SESSION *session = SSL_get_session(w->ssl.conn);
- SSL *old = w->ssl.conn;
- w->ssl.conn = SSL_new(netdata_ssl_srv_ctx);
- if (session) {
- #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_111
- if (SSL_SESSION_is_resumable(session))
- #endif
- SSL_set_session(w->ssl.conn, session);
- }
- SSL_free(old);
- }
- }
- }
- #endif
- static void web_client_zero(struct web_client *w) {
- // zero everything about it - but keep the buffers
- // remember the pointers to the buffers
- BUFFER *b1 = w->response.data;
- BUFFER *b2 = w->response.header;
- BUFFER *b3 = w->response.header_output;
- // empty the buffers
- buffer_flush(b1);
- buffer_flush(b2);
- buffer_flush(b3);
- freez(w->user_agent);
- // zero everything
- memset(w, 0, sizeof(struct web_client));
- // restore the pointers of the buffers
- w->response.data = b1;
- w->response.header = b2;
- w->response.header_output = b3;
- }
- static void web_client_free(struct web_client *w) {
- buffer_free(w->response.header_output);
- buffer_free(w->response.header);
- buffer_free(w->response.data);
- freez(w->user_agent);
- #ifdef ENABLE_HTTPS
- if ((!web_client_check_unix(w)) && (netdata_ssl_srv_ctx)) {
- if (w->ssl.conn) {
- SSL_free(w->ssl.conn);
- w->ssl.conn = NULL;
- }
- }
- #endif
- freez(w);
- __atomic_sub_fetch(&netdata_buffers_statistics.buffers_web, sizeof(struct web_client), __ATOMIC_RELAXED);
- }
- static struct web_client *web_client_alloc(void) {
- struct web_client *w = callocz(1, sizeof(struct web_client));
- __atomic_add_fetch(&netdata_buffers_statistics.buffers_web, sizeof(struct web_client), __ATOMIC_RELAXED);
- w->response.data = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE, &netdata_buffers_statistics.buffers_web);
- w->response.header = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE, &netdata_buffers_statistics.buffers_web);
- w->response.header_output = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE, &netdata_buffers_statistics.buffers_web);
- return w;
- }
- // ----------------------------------------------------------------------------
- // 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.
- // Comments per server:
- // SINGLE-THREADED : 1 cache is maintained
- // MULTI-THREADED : 1 cache is maintained
- // STATIC-THREADED : 1 cache for each thread of the web server
- __thread struct clients_cache web_clients_cache = {
- .pid = 0,
- .used = NULL,
- .used_count = 0,
- .avail = NULL,
- .avail_count = 0,
- .allocated = 0,
- .reused = 0
- };
- inline void web_client_cache_verify(int force) {
- #ifdef NETDATA_INTERNAL_CHECKS
- static __thread size_t count = 0;
- count++;
- if(unlikely(force || count > 1000)) {
- count = 0;
- struct web_client *w;
- size_t used = 0, avail = 0;
- for(w = web_clients_cache.used; w ; w = w->next) used++;
- for(w = web_clients_cache.avail; w ; w = w->next) avail++;
- info("web_client_cache has %zu (%zu) used and %zu (%zu) available clients, allocated %zu, reused %zu (hit %zu%%)."
- , used, web_clients_cache.used_count
- , avail, web_clients_cache.avail_count
- , web_clients_cache.allocated
- , web_clients_cache.reused
- , (web_clients_cache.allocated + web_clients_cache.reused)?(web_clients_cache.reused * 100 / (web_clients_cache.allocated + web_clients_cache.reused)):0
- );
- }
- #else
- if(unlikely(force)) {
- info("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.allocated
- , web_clients_cache.reused
- , (web_clients_cache.allocated + web_clients_cache.reused)?(web_clients_cache.reused * 100 / (web_clients_cache.allocated + web_clients_cache.reused)):0
- );
- }
- #endif
- }
- // destroy the cache and free all the memory it uses
- void web_client_cache_destroy(void) {
- #ifdef NETDATA_INTERNAL_CHECKS
- if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
- error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
- web_client_cache_verify(1);
- #endif
- netdata_thread_disable_cancelability();
- struct web_client *w, *t;
- w = web_clients_cache.used;
- while(w) {
- t = w;
- w = w->next;
- web_client_free(t);
- }
- web_clients_cache.used = NULL;
- web_clients_cache.used_count = 0;
- w = web_clients_cache.avail;
- while(w) {
- t = w;
- w = w->next;
- web_client_free(t);
- }
- web_clients_cache.avail = NULL;
- web_clients_cache.avail_count = 0;
- netdata_thread_enable_cancelability();
- }
- struct web_client *web_client_get_from_cache_or_allocate() {
- #ifdef NETDATA_INTERNAL_CHECKS
- if(unlikely(web_clients_cache.pid == 0))
- web_clients_cache.pid = gettid();
- if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
- error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
- #endif
- netdata_thread_disable_cancelability();
- struct web_client *w = web_clients_cache.avail;
- if(w) {
- // get it from avail
- if (w == web_clients_cache.avail) web_clients_cache.avail = w->next;
- if(w->prev) w->prev->next = w->next;
- if(w->next) w->next->prev = w->prev;
- web_clients_cache.avail_count--;
- #ifdef ENABLE_HTTPS
- web_client_reuse_ssl(w);
- SSL *ssl = w->ssl.conn;
- #endif
- web_client_zero(w);
- web_clients_cache.reused++;
- #ifdef ENABLE_HTTPS
- w->ssl.conn = ssl;
- w->ssl.flags = NETDATA_SSL_START;
- debug(D_WEB_CLIENT_ACCESS,"Reusing SSL structure with (w->ssl = NULL, w->accepted = %u)", w->ssl.flags);
- #endif
- }
- else {
- // allocate it
- w = web_client_alloc();
- #ifdef ENABLE_HTTPS
- w->ssl.flags = NETDATA_SSL_START;
- debug(D_WEB_CLIENT_ACCESS,"Starting SSL structure with (w->ssl = NULL, w->accepted = %u)", w->ssl.flags);
- #endif
- web_clients_cache.allocated++;
- }
- // link it to used web clients
- if (web_clients_cache.used) web_clients_cache.used->prev = w;
- w->next = web_clients_cache.used;
- w->prev = NULL;
- web_clients_cache.used = w;
- web_clients_cache.used_count++;
- // initialize it
- w->id = global_statistics_web_client_connected();
- w->mode = WEB_CLIENT_MODE_NORMAL;
- netdata_thread_enable_cancelability();
- return w;
- }
- void web_client_release(struct web_client *w) {
- #ifdef NETDATA_INTERNAL_CHECKS
- if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
- error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
- if(unlikely(w->running))
- error("%llu: releasing web client from %s port %s, but it still running.", w->id, w->client_ip, w->client_port);
- #endif
- debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port);
- web_server_log_connection(w, "DISCONNECTED");
- web_client_request_done(w);
- global_statistics_web_client_disconnected();
- netdata_thread_disable_cancelability();
- if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
- if (w->ifd != -1) close(w->ifd);
- if (w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
- w->ifd = w->ofd = -1;
- #ifdef ENABLE_HTTPS
- web_client_reuse_ssl(w);
- w->ssl.flags = NETDATA_SSL_START;
- #endif
- }
- // unlink it from the used
- if (w == web_clients_cache.used) web_clients_cache.used = w->next;
- if(w->prev) w->prev->next = w->next;
- if(w->next) w->next->prev = w->prev;
- web_clients_cache.used_count--;
- if(web_clients_cache.avail_count >= 2 * web_clients_cache.used_count) {
- // we have too many of them - free it
- web_client_free(w);
- }
- else {
- // link it to the avail
- if (web_clients_cache.avail) web_clients_cache.avail->prev = w;
- w->next = web_clients_cache.avail;
- w->prev = NULL;
- web_clients_cache.avail = w;
- web_clients_cache.avail_count++;
- }
- netdata_thread_enable_cancelability();
- }
|