web_client_cache.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #define WEB_SERVER_INTERNALS 1
  3. #include "web_client_cache.h"
  4. // ----------------------------------------------------------------------------
  5. // allocate and free web_clients
  6. #ifdef ENABLE_HTTPS
  7. static void web_client_reuse_ssl(struct web_client *w) {
  8. if (netdata_srv_ctx) {
  9. if (w->ssl.conn) {
  10. SSL_clear(w->ssl.conn);
  11. }
  12. }
  13. }
  14. #endif
  15. static void web_client_zero(struct web_client *w) {
  16. // zero everything about it - but keep the buffers
  17. // remember the pointers to the buffers
  18. BUFFER *b1 = w->response.data;
  19. BUFFER *b2 = w->response.header;
  20. BUFFER *b3 = w->response.header_output;
  21. // empty the buffers
  22. buffer_flush(b1);
  23. buffer_flush(b2);
  24. buffer_flush(b3);
  25. freez(w->user_agent);
  26. // zero everything
  27. memset(w, 0, sizeof(struct web_client));
  28. // restore the pointers of the buffers
  29. w->response.data = b1;
  30. w->response.header = b2;
  31. w->response.header_output = b3;
  32. }
  33. static void web_client_free(struct web_client *w) {
  34. buffer_free(w->response.header_output);
  35. buffer_free(w->response.header);
  36. buffer_free(w->response.data);
  37. freez(w->user_agent);
  38. #ifdef ENABLE_HTTPS
  39. if ((!web_client_check_unix(w)) && ( netdata_srv_ctx )) {
  40. if (w->ssl.conn) {
  41. SSL_free(w->ssl.conn);
  42. w->ssl.conn = NULL;
  43. }
  44. }
  45. #endif
  46. freez(w);
  47. }
  48. static struct web_client *web_client_alloc(void) {
  49. struct web_client *w = callocz(1, sizeof(struct web_client));
  50. w->response.data = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE);
  51. w->response.header = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
  52. w->response.header_output = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
  53. return w;
  54. }
  55. // ----------------------------------------------------------------------------
  56. // web clients caching
  57. // When clients connect and disconnect, avoid allocating and releasing memory.
  58. // Instead, when new clients get connected, reuse any memory previously allocated
  59. // for serving web clients that are now disconnected.
  60. // The size of the cache is adaptive. It caches the structures of 2x
  61. // the number of currently connected clients.
  62. // Comments per server:
  63. // SINGLE-THREADED : 1 cache is maintained
  64. // MULTI-THREADED : 1 cache is maintained
  65. // STATIC-THREADED : 1 cache for each thread of the web server
  66. __thread struct clients_cache web_clients_cache = {
  67. .pid = 0,
  68. .used = NULL,
  69. .used_count = 0,
  70. .avail = NULL,
  71. .avail_count = 0,
  72. .allocated = 0,
  73. .reused = 0
  74. };
  75. inline void web_client_cache_verify(int force) {
  76. #ifdef NETDATA_INTERNAL_CHECKS
  77. static __thread size_t count = 0;
  78. count++;
  79. if(unlikely(force || count > 1000)) {
  80. count = 0;
  81. struct web_client *w;
  82. size_t used = 0, avail = 0;
  83. for(w = web_clients_cache.used; w ; w = w->next) used++;
  84. for(w = web_clients_cache.avail; w ; w = w->next) avail++;
  85. info("web_client_cache has %zu (%zu) used and %zu (%zu) available clients, allocated %zu, reused %zu (hit %zu%%)."
  86. , used, web_clients_cache.used_count
  87. , avail, web_clients_cache.avail_count
  88. , web_clients_cache.allocated
  89. , web_clients_cache.reused
  90. , (web_clients_cache.allocated + web_clients_cache.reused)?(web_clients_cache.reused * 100 / (web_clients_cache.allocated + web_clients_cache.reused)):0
  91. );
  92. }
  93. #else
  94. if(unlikely(force)) {
  95. info("web_client_cache has %zu used and %zu available clients, allocated %zu, reused %zu (hit %zu%%)."
  96. , web_clients_cache.used_count
  97. , web_clients_cache.avail_count
  98. , web_clients_cache.allocated
  99. , web_clients_cache.reused
  100. , (web_clients_cache.allocated + web_clients_cache.reused)?(web_clients_cache.reused * 100 / (web_clients_cache.allocated + web_clients_cache.reused)):0
  101. );
  102. }
  103. #endif
  104. }
  105. // destroy the cache and free all the memory it uses
  106. void web_client_cache_destroy(void) {
  107. #ifdef NETDATA_INTERNAL_CHECKS
  108. if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
  109. error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
  110. web_client_cache_verify(1);
  111. #endif
  112. netdata_thread_disable_cancelability();
  113. struct web_client *w, *t;
  114. w = web_clients_cache.used;
  115. while(w) {
  116. t = w;
  117. w = w->next;
  118. web_client_free(t);
  119. }
  120. web_clients_cache.used = NULL;
  121. web_clients_cache.used_count = 0;
  122. w = web_clients_cache.avail;
  123. while(w) {
  124. t = w;
  125. w = w->next;
  126. web_client_free(t);
  127. }
  128. web_clients_cache.avail = NULL;
  129. web_clients_cache.avail_count = 0;
  130. netdata_thread_enable_cancelability();
  131. }
  132. struct web_client *web_client_get_from_cache_or_allocate() {
  133. #ifdef NETDATA_INTERNAL_CHECKS
  134. if(unlikely(web_clients_cache.pid == 0))
  135. web_clients_cache.pid = gettid();
  136. if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
  137. error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
  138. #endif
  139. netdata_thread_disable_cancelability();
  140. struct web_client *w = web_clients_cache.avail;
  141. if(w) {
  142. // get it from avail
  143. if (w == web_clients_cache.avail) web_clients_cache.avail = w->next;
  144. if(w->prev) w->prev->next = w->next;
  145. if(w->next) w->next->prev = w->prev;
  146. web_clients_cache.avail_count--;
  147. #ifdef ENABLE_HTTPS
  148. web_client_reuse_ssl(w);
  149. SSL *ssl = w->ssl.conn;
  150. #endif
  151. web_client_zero(w);
  152. web_clients_cache.reused++;
  153. #ifdef ENABLE_HTTPS
  154. w->ssl.conn = ssl;
  155. w->ssl.flags = NETDATA_SSL_START;
  156. debug(D_WEB_CLIENT_ACCESS,"Reusing SSL structure with (w->ssl = NULL, w->accepted = %u)", w->ssl.flags);
  157. #endif
  158. }
  159. else {
  160. // allocate it
  161. w = web_client_alloc();
  162. #ifdef ENABLE_HTTPS
  163. w->ssl.flags = NETDATA_SSL_START;
  164. debug(D_WEB_CLIENT_ACCESS,"Starting SSL structure with (w->ssl = NULL, w->accepted = %u)", w->ssl.flags);
  165. #endif
  166. web_clients_cache.allocated++;
  167. }
  168. // link it to used web clients
  169. if (web_clients_cache.used) web_clients_cache.used->prev = w;
  170. w->next = web_clients_cache.used;
  171. w->prev = NULL;
  172. web_clients_cache.used = w;
  173. web_clients_cache.used_count++;
  174. // initialize it
  175. w->id = web_client_connected();
  176. w->mode = WEB_CLIENT_MODE_NORMAL;
  177. netdata_thread_enable_cancelability();
  178. return w;
  179. }
  180. void web_client_release(struct web_client *w) {
  181. #ifdef NETDATA_INTERNAL_CHECKS
  182. if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
  183. error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
  184. if(unlikely(w->running))
  185. error("%llu: releasing web client from %s port %s, but it still running.", w->id, w->client_ip, w->client_port);
  186. #endif
  187. debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port);
  188. web_server_log_connection(w, "DISCONNECTED");
  189. web_client_request_done(w);
  190. web_client_disconnected();
  191. netdata_thread_disable_cancelability();
  192. if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
  193. if (w->ifd != -1) close(w->ifd);
  194. if (w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
  195. w->ifd = w->ofd = -1;
  196. #ifdef ENABLE_HTTPS
  197. web_client_reuse_ssl(w);
  198. w->ssl.flags = NETDATA_SSL_START;
  199. #endif
  200. }
  201. // unlink it from the used
  202. if (w == web_clients_cache.used) web_clients_cache.used = w->next;
  203. if(w->prev) w->prev->next = w->next;
  204. if(w->next) w->next->prev = w->prev;
  205. web_clients_cache.used_count--;
  206. if(web_clients_cache.avail_count >= 2 * web_clients_cache.used_count) {
  207. // we have too many of them - free it
  208. web_client_free(w);
  209. }
  210. else {
  211. // link it to the avail
  212. if (web_clients_cache.avail) web_clients_cache.avail->prev = w;
  213. w->next = web_clients_cache.avail;
  214. w->prev = NULL;
  215. web_clients_cache.avail = w;
  216. web_clients_cache.avail_count++;
  217. }
  218. netdata_thread_enable_cancelability();
  219. }