web_client_cache.c 8.9 KB

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