@cryptotaxi247 / netdata-1 / commits / 4e6bab273

added web_client structures caching, to avoid memory allocations on reconnects

Costa Tsaousis (ktsaou) committed Jan 10, 2018 at 04:19 UTC 4e6bab273ccdc526622313eed476c6fb4d5de6d0
5 files changed +588 -465
src/registry.c
+2 -2
@@ -27,10 +27,10 @@ static void registry_set_cookie(struct web_client *w, const char *guid) {
27 struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
28 strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
29
30 - snprintfz(w->cookie1, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s", guid, edate);
30 + snprintfz(w->cookie1, NETDATA_WEB_REQUEST_COOKIE_SIZE, NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s", guid, edate);
31
32 if(registry.registry_domain && registry.registry_domain[0])
33 - snprintfz(w->cookie2, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Domain=%s; Expires=%s", guid, registry.registry_domain, edate);
33 + snprintfz(w->cookie2, NETDATA_WEB_REQUEST_COOKIE_SIZE, NETDATA_REGISTRY_COOKIE_NAME "=%s; Domain=%s; Expires=%s", guid, registry.registry_domain, edate);
34 }
35
36 static inline void registry_set_person_cookie(struct web_client *w, REGISTRY_PERSON *p) {
src/web_client.c
+18 -411
@@ -1,26 +1,23 @@
1 #include "common.h"
2
3 -#define INITIAL_WEB_DATA_LENGTH 16384
4 -#define WEB_REQUEST_LENGTH 16384
5 -#define TOO_BIG_REQUEST 16384
3 +// this is an async I/O implementation of the web server request parser
4 +// it is used by all netdata web servers
5
7 -int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS;
6 int respect_web_browser_do_not_track_policy = 0;
7 char *web_x_frame_options = NULL;
8
11 -SIMPLE_PATTERN *web_allow_connections_from = NULL;
12 -SIMPLE_PATTERN *web_allow_streaming_from = NULL;
13 -SIMPLE_PATTERN *web_allow_netdataconf_from = NULL;
14 -
15 -// WEB_CLIENT_ACL
16 -SIMPLE_PATTERN *web_allow_dashboard_from = NULL;
17 -SIMPLE_PATTERN *web_allow_registry_from = NULL;
18 -SIMPLE_PATTERN *web_allow_badges_from = NULL;
19 -
9 #ifdef NETDATA_WITH_ZLIB
10 int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY;
11 #endif /* NETDATA_WITH_ZLIB */
12
13 +inline int web_client_permission_denied(struct web_client *w) {
14 + w->response.data->contenttype = CT_TEXT_PLAIN;
15 + buffer_flush(w->response.data);
16 + buffer_strcat(w->response.data, "You are not allowed to access this resource.");
17 + w->response.code = 403;
18 + return 403;
19 +}
20 +
21 static inline int web_client_crock_socket(struct web_client *w) {
22 #ifdef TCP_CORK
23 if(likely(web_client_is_corkable(w) && !w->tcp_cork && w->ofd != -1)) {
@@ -56,240 +53,6 @@ static inline int web_client_uncrock_socket(struct web_client *w) {
53 return 0;
54 }
55
59 -inline int web_client_permission_denied(struct web_client *w) {
60 - w->response.data->contenttype = CT_TEXT_PLAIN;
61 - buffer_flush(w->response.data);
62 - buffer_strcat(w->response.data, "You are not allowed to access this resource.");
63 - w->response.code = 403;
64 - return 403;
65 -}
66 -
67 -static void log_connection(struct web_client *w, const char *msg) {
68 - log_access("%llu: %d '[%s]:%s' '%s'", w->id, gettid(), w->client_ip, w->client_port, msg);
69 -}
70 -
71 -static void web_client_update_acl_matches(struct web_client *w) {
72 - w->acl = WEB_CLIENT_ACL_NONE;
73 -
74 - if(!web_allow_dashboard_from || simple_pattern_matches(web_allow_dashboard_from, w->client_ip))
75 - w->acl |= WEB_CLIENT_ACL_DASHBOARD;
76 -
77 - if(!web_allow_registry_from || simple_pattern_matches(web_allow_registry_from, w->client_ip))
78 - w->acl |= WEB_CLIENT_ACL_REGISTRY;
79 -
80 - if(!web_allow_badges_from || simple_pattern_matches(web_allow_badges_from, w->client_ip))
81 - w->acl |= WEB_CLIENT_ACL_BADGE;
82 -}
83 -
84 -static void web_client_initialize_connection(struct web_client *w) {
85 - int flag = 1;
86 - if(setsockopt(w->ifd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0)
87 - error("%llu: failed to enable TCP_NODELAY on socket fd %d.", w->id, w->ifd);
88 -
89 - flag = 1;
90 - if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0)
91 - error("%llu: failed to enable SO_KEEPALIVE on socket fd %d.", w->id, w->ifd);
92 -
93 - web_client_update_acl_matches(w);
94 -
95 - w->origin[0] = '*'; w->origin[1] = '\0';
96 - web_client_enable_wait_receive(w);
97 -
98 - log_connection(w, "CONNECTED");
99 -}
100 -
101 -struct clients_cache {
102 - struct web_client *used;
103 - size_t used_count;
104 -
105 - struct web_client *avail;
106 - size_t avail_count;
107 -};
108 -
109 -__thread struct clients_cache web_clients_cache = {
110 - .used = 0,
111 - .used_count = 0,
112 -
113 - .avail = 0,
114 - .avail_count = 0
115 -};
116 -
117 -static void web_client_free(struct web_client *w) {
118 - buffer_free(w->response.header_output);
119 - buffer_free(w->response.header);
120 - buffer_free(w->response.data);
121 - freez(w);
122 -}
123 -
124 -void web_client_cache_destroy(void) {
125 - struct web_client *w, *t;
126 -
127 - w = web_clients_cache.used;
128 - while(w) {
129 - t = w;
130 - w = w->next;
131 - web_client_free(t);
132 - }
133 - web_clients_cache.used = NULL;
134 - web_clients_cache.used_count = 0;
135 -
136 - w = web_clients_cache.avail;
137 - while(w) {
138 - t = w;
139 - w = w->next;
140 - web_client_free(t);
141 - }
142 - web_clients_cache.avail = NULL;
143 - web_clients_cache.avail_count = 0;
144 -}
145 -
146 -void web_client_multi_threaded_web_server_stop_all_threads(void) {
147 - struct web_client *w;
148 -
149 - int found = 1, max = 2 * USEC_PER_SEC, step = 50000;
150 - for(w = web_clients_cache.used; w ; w = w->next) {
151 - if(w->running) {
152 - found++;
153 - info("stopping web client %s, id %llu", w->client_ip, w->id);
154 - netdata_thread_cancel(w->thread);
155 - }
156 - }
157 -
158 - while(found && max > 0) {
159 - max -= step;
160 - info("Waiting %d web threads to finish...", found);
161 - sleep_usec(step);
162 - found = 0;
163 - for(w = web_clients_cache.used; w ; w = w->next)
164 - if(w->running) found++;
165 - }
166 -
167 - if(found)
168 - error("%d web threads are taking too long to finish. Giving up.", found);
169 -}
170 -
171 -static void web_client_return_to_cache_or_free(struct web_client *w) {
172 - // unlink it from the used;
173 - if (w == web_clients_cache.used) web_clients_cache.used = w->next;
174 - if(w->prev) w->prev->next = w->next;
175 - if(w->next) w->next->prev = w->prev;
176 - web_clients_cache.used_count--;
177 -
178 - if(web_clients_cache.avail_count > 100) {
179 - // we have too many of them - free it
180 - web_client_free(w);
181 - }
182 - else {
183 - // link it to the avail
184 - if (web_clients_cache.avail) web_clients_cache.avail->prev = w;
185 - w->next = web_clients_cache.avail;
186 - w->prev = NULL;
187 - web_clients_cache.avail = w;
188 - web_clients_cache.avail_count++;
189 - }
190 -}
191 -
192 -static struct web_client *web_client_get_from_cache_or_allocate() {
193 - struct web_client *w = web_clients_cache.avail;
194 -
195 - if(w) {
196 - // unlink it from avail
197 - if (w == web_clients_cache.avail) web_clients_cache.avail = w->next;
198 - if(w->prev) w->prev->next = w->next;
199 - if(w->next) w->next->prev = w->prev;
200 - web_clients_cache.avail_count--;
201 -
202 - // zero everything about it - but keep the buffers
203 -
204 - BUFFER *b1 = w->response.data;
205 - BUFFER *b2 = w->response.header;
206 - BUFFER *b3 = w->response.header_output;
207 -
208 - buffer_flush(b1);
209 - buffer_flush(b2);
210 - buffer_flush(b3);
211 -
212 - memset(w, 0, sizeof(struct web_client));
213 -
214 - w->response.data = b1;
215 - w->response.header = b2;
216 - w->response.header_output = b3;
217 - }
218 - else {
219 - w = callocz(1, sizeof(struct web_client));
220 - w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH);
221 - w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
222 - w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
223 - }
224 -
225 - // link it to used web clients
226 - if (web_clients_cache.used) web_clients_cache.used->prev = w;
227 - w->next = web_clients_cache.used;
228 - w->prev = NULL;
229 - web_clients_cache.used = w;
230 - web_clients_cache.used_count++;
231 -
232 - // initialize it
233 - w->id = web_client_connected();
234 - w->mode = WEB_CLIENT_MODE_NORMAL;
235 - return w;
236 -}
237 -
238 -void web_client_release(struct web_client *w) {
239 - debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port);
240 -
241 - web_client_request_done(w);
242 - web_client_disconnected();
243 -
244 - if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
245 - if (w->ifd != -1) close(w->ifd);
246 - if (w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
247 - }
248 -
249 - web_client_return_to_cache_or_free(w);
250 -}
251 -
252 -struct web_client *web_client_create_on_fd(int fd, const char *client_ip, const char *client_port) {
253 - struct web_client *w;
254 -
255 - w = web_client_get_from_cache_or_allocate();
256 - w->ifd = w->ofd = fd;
257 -
258 - strncpyz(w->client_ip, client_ip, sizeof(w->client_ip) - 1);
259 - strncpyz(w->client_port, client_port, sizeof(w->client_port) - 1);
260 -
261 - if(unlikely(!*w->client_ip)) strcpy(w->client_ip, "-");
262 - if(unlikely(!*w->client_port)) strcpy(w->client_port, "-");
263 -
264 - web_client_initialize_connection(w);
265 - return(w);
266 -}
267 -
268 -struct web_client *web_client_create_on_listenfd(int listener) {
269 - struct web_client *w;
270 -
271 - w = web_client_get_from_cache_or_allocate();
272 - w->ifd = w->ofd = accept_socket(listener, SOCK_NONBLOCK, w->client_ip, sizeof(w->client_ip), w->client_port, sizeof(w->client_port), web_allow_connections_from);
273 -
274 - if(unlikely(!*w->client_ip)) strcpy(w->client_ip, "-");
275 - if(unlikely(!*w->client_port)) strcpy(w->client_port, "-");
276 -
277 - if (w->ifd == -1) {
278 - if(errno == EPERM)
279 - log_connection(w, "ACCESS DENIED");
280 - else {
281 - log_connection(w, "CONNECTION FAILED");
282 - error("%llu: Failed to accept new incoming connection.", w->id);
283 - }
284 -
285 - web_client_release(w);
286 - return NULL;
287 - }
288 -
289 - web_client_initialize_connection(w);
290 - return(w);
291 -}
292 -
56 void web_client_request_done(struct web_client *w) {
57 web_client_uncrock_socket(w);
58
@@ -999,7 +762,7 @@ static inline char *http_header_parse(struct web_client *w, char *s) {
762 uint32_t hash = simple_uhash(s);
763
764 if(hash == hash_origin && !strcasecmp(s, "Origin"))
1002 - strncpyz(w->origin, v, ORIGIN_MAX);
765 + strncpyz(w->origin, v, NETDATA_WEB_REQUEST_ORIGIN_HEADER_SIZE);
766
767 else if(hash == hash_connection && !strcasecmp(s, "Connection")) {
768 if(strcasestr(v, "keep-alive"))
@@ -1096,12 +859,12 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
859 // a valid complete HTTP request found
860
861 *ue = '\0';
1099 - url_decode_r(w->decoded_url, encoded_url, URL_MAX + 1);
862 + url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1);
863 *ue = ' ';
864
865 // copy the URL - we are going to overwrite parts of it
866 // FIXME -- we should avoid it
1104 - strncpyz(w->last_url, w->decoded_url, URL_MAX);
867 + strncpyz(w->last_url, w->decoded_url, NETDATA_WEB_REQUEST_URL_SIZE);
868
869 web_client_disable_wait_receive(w);
870 return HTTP_VALIDATION_OK;
@@ -1290,7 +1053,7 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
1053
1054 // copy the URL, we need it to serve files
1055 w->last_url[0] = '/';
1293 - if(url && *url) strncpyz(&w->last_url[1], url, URL_MAX - 1);
1056 + if(url && *url) strncpyz(&w->last_url[1], url, NETDATA_WEB_REQUEST_URL_SIZE - 1);
1057 else w->last_url[1] = '\0';
1058
1059 uint32_t hash = simple_hash(tok);
@@ -1506,7 +1269,7 @@ void web_client_process_request(struct web_client *w) {
1269 break;
1270
1271 case HTTP_VALIDATION_INCOMPLETE:
1509 - if(w->response.data->len > TOO_BIG_REQUEST) {
1272 + if(w->response.data->len > NETDATA_WEB_REQUEST_MAX_SIZE) {
1273 strcpy(w->last_url, "too big request");
1274
1275 debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
@@ -1714,7 +1477,7 @@ ssize_t web_client_send_deflate(struct web_client *w)
1477
1478 // reset the compressor output buffer
1479 w->response.zstream.next_out = w->response.zbuffer;
1717 - w->response.zstream.avail_out = ZLIB_CHUNK;
1480 + w->response.zstream.avail_out = NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE;
1481
1482 // ask for FINISH if we have all the input
1483 int flush = Z_SYNC_FLUSH;
@@ -1734,7 +1497,7 @@ ssize_t web_client_send_deflate(struct web_client *w)
1497 return(-1);
1498 }
1499
1737 - w->response.zhave = ZLIB_CHUNK - w->response.zstream.avail_out;
1500 + w->response.zhave = NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE - w->response.zstream.avail_out;
1501 w->response.zsent = 0;
1502
1503 // keep track of the bytes passed through the compressor
@@ -1880,7 +1643,7 @@ ssize_t web_client_receive(struct web_client *w)
1643 return web_client_read_file(w);
1644
1645 // do we have any space for more data?
1883 - buffer_need_bytes(w->response.data, WEB_REQUEST_LENGTH);
1646 + buffer_need_bytes(w->response.data, NETDATA_WEB_REQUEST_RECEIVE_SIZE);
1647
1648 ssize_t left = w->response.data->size - w->response.data->len;
1649 ssize_t bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
@@ -1902,159 +1665,3 @@ ssize_t web_client_receive(struct web_client *w)
1665
1666 return(bytes);
1667 }
1905 -
1906 -// --------------------------------------------------------------------------------------
1907 -// the thread of a single client - for the MULTI-THREADED web server
1908 -
1909 -// 1. waits for input and output, using async I/O
1910 -// 2. it processes HTTP requests
1911 -// 3. it generates HTTP responses
1912 -// 4. it copies data from input to output if mode is FILECOPY
1913 -
1914 -static void web_client_main_cleanup(void *ptr) {
1915 - struct web_client *w = ptr;
1916 -
1917 - if(!web_client_check_obsolete(w)) {
1918 - WEB_CLIENT_IS_OBSOLETE(w);
1919 - }
1920 -
1921 - w->running = 0;
1922 -}
1923 -
1924 -void *web_client_main(void *ptr) {
1925 - netdata_thread_cleanup_push(web_client_main_cleanup, ptr);
1926 -
1927 - struct web_client *w = ptr;
1928 - w->running = 1;
1929 -
1930 - struct pollfd fds[2], *ifd, *ofd;
1931 - int retval, timeout;
1932 - nfds_t fdmax = 0;
1933 -
1934 - while(!netdata_exit) {
1935 - if(unlikely(web_client_check_dead(w))) {
1936 - debug(D_WEB_CLIENT, "%llu: client is dead.", w->id);
1937 - break;
1938 - }
1939 - else if(unlikely(!web_client_has_wait_receive(w) && !web_client_has_wait_send(w))) {
1940 - debug(D_WEB_CLIENT, "%llu: client is not set for neither receiving nor sending data.", w->id);
1941 - break;
1942 - }
1943 -
1944 - if(unlikely(w->ifd < 0 || w->ofd < 0)) {
1945 - error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd", w->id, w->ifd, w->ofd);
1946 - break;
1947 - }
1948 -
1949 - if(w->ifd == w->ofd) {
1950 - fds[0].fd = w->ifd;
1951 - fds[0].events = 0;
1952 - fds[0].revents = 0;
1953 -
1954 - if(web_client_has_wait_receive(w)) fds[0].events |= POLLIN;
1955 - if(web_client_has_wait_send(w)) fds[0].events |= POLLOUT;
1956 -
1957 - fds[1].fd = -1;
1958 - fds[1].events = 0;
1959 - fds[1].revents = 0;
1960 -
1961 - ifd = ofd = &fds[0];
1962 -
1963 - fdmax = 1;
1964 - }
1965 - else {
1966 - fds[0].fd = w->ifd;
1967 - fds[0].events = 0;
1968 - fds[0].revents = 0;
1969 - if(web_client_has_wait_receive(w)) fds[0].events |= POLLIN;
1970 - ifd = &fds[0];
1971 -
1972 - fds[1].fd = w->ofd;
1973 - fds[1].events = 0;
1974 - fds[1].revents = 0;
1975 - if(web_client_has_wait_send(w)) fds[1].events |= POLLOUT;
1976 - ofd = &fds[1];
1977 -
1978 - fdmax = 2;
1979 - }
1980 -
1981 - debug(D_WEB_CLIENT, "%llu: Waiting socket async I/O for %s %s", w->id, web_client_has_wait_receive(w)?"INPUT":"", web_client_has_wait_send(w)?"OUTPUT":"");
1982 - errno = 0;
1983 - timeout = web_client_timeout * 1000;
1984 - retval = poll(fds, fdmax, timeout);
1985 -
1986 - if(unlikely(netdata_exit)) break;
1987 -
1988 - if(unlikely(retval == -1)) {
1989 - if(errno == EAGAIN || errno == EINTR) {
1990 - debug(D_WEB_CLIENT, "%llu: EAGAIN received.", w->id);
1991 - continue;
1992 - }
1993 -
1994 - debug(D_WEB_CLIENT, "%llu: LISTENER: poll() failed (input fd = %d, output fd = %d). Closing client.", w->id, w->ifd, w->ofd);
1995 - break;
1996 - }
1997 - else if(unlikely(!retval)) {
1998 - debug(D_WEB_CLIENT, "%llu: Timeout while waiting socket async I/O for %s %s", w->id, web_client_has_wait_receive(w)?"INPUT":"", web_client_has_wait_send(w)?"OUTPUT":"");
1999 - break;
2000 - }
2001 -
2002 - if(unlikely(netdata_exit)) break;
2003 -
2004 - int used = 0;
2005 - if(web_client_has_wait_send(w) && ofd->revents & POLLOUT) {
2006 - used++;
2007 - if(web_client_send(w) < 0) {
2008 - debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
2009 - break;
2010 - }
2011 - }
2012 -
2013 - if(unlikely(netdata_exit)) break;
2014 -
2015 - if(web_client_has_wait_receive(w) && (ifd->revents & POLLIN || ifd->revents & POLLPRI)) {
2016 - used++;
2017 - if(web_client_receive(w) < 0) {
2018 - debug(D_WEB_CLIENT, "%llu: Cannot receive data from client. Closing client.", w->id);
2019 - break;
2020 - }
2021 -
2022 - if(w->mode == WEB_CLIENT_MODE_NORMAL) {
2023 - debug(D_WEB_CLIENT, "%llu: Attempting to process received data.", w->id);
2024 - web_client_process_request(w);
2025 -
2026 - // if the sockets are closed, may have transferred this client
2027 - // to plugins.d
2028 - if(unlikely(w->mode == WEB_CLIENT_MODE_STREAM))
2029 - break;
2030 - }
2031 - }
2032 -
2033 - if(unlikely(!used)) {
2034 - debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on socket.", w->id);
2035 - break;
2036 - }
2037 - }
2038 -
2039 - if(w->mode != WEB_CLIENT_MODE_STREAM)
2040 - log_connection(w, "DISCONNECTED");
2041 -
2042 - web_client_request_done(w);
2043 -
2044 - debug(D_WEB_CLIENT, "%llu: done...", w->id);
2045 -
2046 - // close the sockets/files now
2047 - // to free file descriptors
2048 - if(w->ifd == w->ofd) {
2049 - if(w->ifd != -1) close(w->ifd);
2050 - }
2051 - else {
2052 - if(w->ifd != -1) close(w->ifd);
2053 - if(w->ofd != -1) close(w->ofd);
2054 - }
2055 - w->ifd = -1;
2056 - w->ofd = -1;
2057 -
2058 - netdata_thread_cleanup_pop(1);
2059 - return NULL;
2060 -}
src/web_client.h
+14 -37
@@ -1,9 +1,6 @@
1 #ifndef NETDATA_WEB_CLIENT_H
2 #define NETDATA_WEB_CLIENT_H 1
3
4 -#define DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS 60
5 -extern int web_client_timeout;
6 -
4 #ifdef NETDATA_WITH_ZLIB
5 extern int web_enable_gzip,
6 web_gzip_level,
@@ -21,10 +18,6 @@ typedef enum web_client_mode {
18 } WEB_CLIENT_MODE;
19
20 typedef enum web_client_flags {
24 - WEB_CLIENT_FLAG_OBSOLETE = 1 << 0, // if set, the listener will remove this client
25 - // after setting this, you should not touch
26 - // this web_client
27 -
21 WEB_CLIENT_FLAG_DEAD = 1 << 1, // if set, this client is dead
22
23 WEB_CLIENT_FLAG_KEEPALIVE = 1 << 2, // if set, the web client will be re-used
@@ -49,9 +42,6 @@ typedef enum web_client_flags {
42 #define web_client_flag_clear(w, flag) (w)->flags &= ~flag
43 //#endif
44
52 -#define WEB_CLIENT_IS_OBSOLETE(w) web_client_flag_set(w, WEB_CLIENT_FLAG_OBSOLETE)
53 -#define web_client_check_obsolete(w) web_client_flag_check(w, WEB_CLIENT_FLAG_OBSOLETE)
54 -
45 #define WEB_CLIENT_IS_DEAD(w) web_client_flag_set(w, WEB_CLIENT_FLAG_DEAD)
46 #define web_client_check_dead(w) web_client_flag_check(w, WEB_CLIENT_FLAG_DEAD)
47
@@ -81,11 +71,14 @@ typedef enum web_client_flags {
71
72 #define web_client_is_corkable(w) web_client_flag_check(w, WEB_CLIENT_FLAG_TCP_CLIENT)
73
84 -#define URL_MAX 8192
85 -#define ZLIB_CHUNK 16384
86 -#define HTTP_RESPONSE_HEADER_SIZE 4096
87 -#define COOKIE_MAX 1024
88 -#define ORIGIN_MAX 1024
74 +#define NETDATA_WEB_REQUEST_URL_SIZE 8192
75 +#define NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE 16384
76 +#define NETDATA_WEB_RESPONSE_HEADER_SIZE 4096
77 +#define NETDATA_WEB_REQUEST_COOKIE_SIZE 1024
78 +#define NETDATA_WEB_REQUEST_ORIGIN_HEADER_SIZE 1024
79 +#define NETDATA_WEB_RESPONSE_INITIAL_SIZE 16384
80 +#define NETDATA_WEB_REQUEST_RECEIVE_SIZE 16384
81 +#define NETDATA_WEB_REQUEST_MAX_SIZE 16384
82
83 struct response {
84 BUFFER *header; // our response header
@@ -100,7 +93,7 @@ struct response {
93 int zoutput; // if set to 1, web_client_send() will send compressed data
94 #ifdef NETDATA_WITH_ZLIB
95 z_stream zstream; // zlib stream for sending compressed output to client
103 - Bytef zbuffer[ZLIB_CHUNK]; // temporary buffer for storing compressed output
96 + Bytef zbuffer[NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE]; // temporary buffer for storing compressed output
97 size_t zsent; // the compressed bytes we have sent to the client
98 size_t zhave; // the compressed bytes that we have received from zlib
99 int zinitialized:1;
@@ -141,14 +134,14 @@ struct web_client {
134 char client_ip[NI_MAXHOST+1];
135 char client_port[NI_MAXSERV+1];
136
144 - char decoded_url[URL_MAX + 1]; // we decode the URL in this buffer
145 - char last_url[URL_MAX+1]; // we keep a copy of the decoded URL here
137 + char decoded_url[NETDATA_WEB_REQUEST_URL_SIZE + 1]; // we decode the URL in this buffer
138 + char last_url[NETDATA_WEB_REQUEST_URL_SIZE+1]; // we keep a copy of the decoded URL here
139
140 struct timeval tv_in, tv_ready;
141
149 - char cookie1[COOKIE_MAX+1];
150 - char cookie2[COOKIE_MAX+1];
151 - char origin[ORIGIN_MAX+1];
142 + char cookie1[NETDATA_WEB_REQUEST_COOKIE_SIZE+1];
143 + char cookie2[NETDATA_WEB_REQUEST_COOKIE_SIZE+1];
144 + char origin[NETDATA_WEB_REQUEST_ORIGIN_HEADER_SIZE+1];
145
146 struct response response;
147
@@ -168,22 +161,11 @@ struct web_client {
161 size_t pollinfo_filecopy_slot; // POLLINFO slot of the file read
162 };
163
171 -extern SIMPLE_PATTERN *web_allow_connections_from;
172 -extern SIMPLE_PATTERN *web_allow_dashboard_from;
173 -extern SIMPLE_PATTERN *web_allow_registry_from;
174 -extern SIMPLE_PATTERN *web_allow_badges_from;
175 -extern SIMPLE_PATTERN *web_allow_streaming_from;
176 -extern SIMPLE_PATTERN *web_allow_netdataconf_from;
177 -
164 extern uid_t web_files_uid(void);
165 extern uid_t web_files_gid(void);
166
167 extern int web_client_permission_denied(struct web_client *w);
168
183 -extern struct web_client *web_client_create_on_fd(int fd, const char *client_ip, const char *client_port);
184 -extern struct web_client *web_client_create_on_listenfd(int listener);
185 -extern void web_client_release(struct web_client *w);
186 -
169 extern ssize_t web_client_send(struct web_client *w);
170 extern ssize_t web_client_receive(struct web_client *w);
171 extern ssize_t web_client_read_file(struct web_client *w);
@@ -191,8 +173,6 @@ extern ssize_t web_client_read_file(struct web_client *w);
173 extern void web_client_process_request(struct web_client *w);
174 extern void web_client_request_done(struct web_client *w);
175
194 -extern void *web_client_main(void *ptr);
195 -
176 extern int web_client_api_request_v1_data_group(char *name, int def);
177 extern const char *group_method2string(int group);
178
@@ -200,7 +180,4 @@ extern void buffer_data_options2string(BUFFER *wb, uint32_t options);
180
181 extern int mysendfile(struct web_client *w, char *filename);
182
203 -extern void web_client_multi_threaded_web_server_stop_all_threads(void);
204 -extern void web_client_cache_destroy(void);
205 -
183 #endif
src/web_server.c
+544 -15
@@ -1,11 +1,10 @@
1 #include "common.h"
2
3 -static LISTEN_SOCKETS api_sockets = {
4 - .config_section = CONFIG_SECTION_WEB,
5 - .default_bind_to = "*",
6 - .default_port = API_LISTEN_PORT,
7 - .backlog = API_LISTEN_BACKLOG
8 -};
3 +// this file includes 3 web servers:
4 +//
5 +// 1. single-threaded, based on select()
6 +// 2. multi-threaded, based on poll() that spawns threads to handle the requests, based on select()
7 +// 3. static-threaded, based on poll() using a fixed number of threads (configured at netdata.conf)
8
9 WEB_SERVER_MODE web_server_mode = WEB_SERVER_MODE_STATIC_THREADED;
10
@@ -40,6 +39,14 @@ const char *web_server_mode_name(WEB_SERVER_MODE id) {
39 }
40
41 // --------------------------------------------------------------------------------------
42 +// API sockets
43 +
44 +static LISTEN_SOCKETS api_sockets = {
45 + .config_section = CONFIG_SECTION_WEB,
46 + .default_bind_to = "*",
47 + .default_port = API_LISTEN_PORT,
48 + .backlog = API_LISTEN_BACKLOG
49 +};
50
51 int api_listen_sockets_setup(void) {
52 int socks = listen_sockets_setup(&api_sockets);
@@ -50,6 +57,479 @@ int api_listen_sockets_setup(void) {
57 return socks;
58 }
59
60 +
61 +// --------------------------------------------------------------------------------------
62 +// access lists
63 +
64 +SIMPLE_PATTERN *web_allow_connections_from = NULL;
65 +SIMPLE_PATTERN *web_allow_streaming_from = NULL;
66 +SIMPLE_PATTERN *web_allow_netdataconf_from = NULL;
67 +
68 +// WEB_CLIENT_ACL
69 +SIMPLE_PATTERN *web_allow_dashboard_from = NULL;
70 +SIMPLE_PATTERN *web_allow_registry_from = NULL;
71 +SIMPLE_PATTERN *web_allow_badges_from = NULL;
72 +
73 +static void web_client_update_acl_matches(struct web_client *w) {
74 + w->acl = WEB_CLIENT_ACL_NONE;
75 +
76 + if(!web_allow_dashboard_from || simple_pattern_matches(web_allow_dashboard_from, w->client_ip))
77 + w->acl |= WEB_CLIENT_ACL_DASHBOARD;
78 +
79 + if(!web_allow_registry_from || simple_pattern_matches(web_allow_registry_from, w->client_ip))
80 + w->acl |= WEB_CLIENT_ACL_REGISTRY;
81 +
82 + if(!web_allow_badges_from || simple_pattern_matches(web_allow_badges_from, w->client_ip))
83 + w->acl |= WEB_CLIENT_ACL_BADGE;
84 +}
85 +
86 +
87 +// --------------------------------------------------------------------------------------
88 +
89 +static void log_connection(struct web_client *w, const char *msg) {
90 + log_access("%llu: %d '[%s]:%s' '%s'", w->id, gettid(), w->client_ip, w->client_port, msg);
91 +}
92 +
93 +// ----------------------------------------------------------------------------
94 +// allocate and free web_clients
95 +
96 +static void web_client_zero(struct web_client *w) {
97 + // zero everything about it - but keep the buffers
98 +
99 + // remember the pointers to the buffers
100 + BUFFER *b1 = w->response.data;
101 + BUFFER *b2 = w->response.header;
102 + BUFFER *b3 = w->response.header_output;
103 +
104 + // empty the buffers
105 + buffer_flush(b1);
106 + buffer_flush(b2);
107 + buffer_flush(b3);
108 +
109 + // zero everything
110 + memset(w, 0, sizeof(struct web_client));
111 +
112 + // restore the pointers of the buffers
113 + w->response.data = b1;
114 + w->response.header = b2;
115 + w->response.header_output = b3;
116 +}
117 +
118 +static void web_client_free(struct web_client *w) {
119 + buffer_free(w->response.header_output);
120 + buffer_free(w->response.header);
121 + buffer_free(w->response.data);
122 + freez(w);
123 +}
124 +
125 +static struct web_client *web_client_alloc(void) {
126 + struct web_client *w = callocz(1, sizeof(struct web_client));
127 + w->response.data = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE);
128 + w->response.header = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
129 + w->response.header_output = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
130 + return w;
131 +}
132 +
133 +// ----------------------------------------------------------------------------
134 +// web clients caching
135 +
136 +// When clients connect and disconnect, avoid allocating and releasing memory.
137 +// Instead, when new clients get connected, reuse any memory previously allocated
138 +// for serving web clients that are now disconnected.
139 +
140 +// The size of the cache is adaptive. It caches the structures of 2x
141 +// the number of currently connected clients.
142 +
143 +// Comments per server:
144 +// SINGLE-THREADED : 1 cache is maintained
145 +// MULTI-THREADED : 1 cache is maintained
146 +// STATIC-THREADED : 1 cache for each thred of the web server
147 +
148 +struct clients_cache {
149 + pid_t pid;
150 +
151 + struct web_client *used; // the structures of the currently connected clients
152 + size_t used_count; // the count the currently connected clients
153 +
154 + struct web_client *avail; // the cached structures, available for future clients
155 + size_t avail_count; // the number of cached structures
156 +
157 + size_t reused; // the number of re-uses
158 + size_t allocated; // the number of allocations
159 +};
160 +
161 +static __thread struct clients_cache web_clients_cache = {
162 + .pid = 0,
163 + .used = NULL,
164 + .used_count = 0,
165 + .avail = NULL,
166 + .avail_count = 0,
167 + .allocated = 0,
168 + .reused = 0
169 +};
170 +
171 +static inline void web_client_cache_verify(int force) {
172 +#ifdef NETDATA_INTERNAL_CHECKS
173 + static __thread size_t count = 0;
174 + count++;
175 +
176 + if(unlikely(force || count > 1000)) {
177 + count = 0;
178 +
179 + struct web_client *w;
180 + size_t used = 0, avail = 0;
181 + for(w = web_clients_cache.used; w ; w = w->next) used++;
182 + for(w = web_clients_cache.avail; w ; w = w->next) avail++;
183 +
184 + info("web_client_cache has %zu (%zu) used and %zu (%zu) available clients, allocated %zu, reused %zu (hit %zu%%)."
185 + , used, web_clients_cache.used_count
186 + , avail, web_clients_cache.avail_count
187 + , web_clients_cache.allocated
188 + , web_clients_cache.reused
189 + , (web_clients_cache.allocated + web_clients_cache.reused)?(web_clients_cache.reused * 100 / (web_clients_cache.allocated + web_clients_cache.reused)):0
190 + );
191 + }
192 +#else
193 + if(unlikely(force)) {
194 + info("web_client_cache has %zu used and %zu available clients, allocated %zu, reused %zu (hit %zu%%)."
195 + , web_clients_cache.used_count
196 + , web_clients_cache.avail_count
197 + , web_clients_cache.allocated
198 + , web_clients_cache.reused
199 + , (web_clients_cache.allocated + web_clients_cache.reused)?(web_clients_cache.reused * 100 / (web_clients_cache.allocated + web_clients_cache.reused)):0
200 + );
201 + }
202 +#endif
203 +}
204 +
205 +// destroy the cache and free all the memory it uses
206 +static void web_client_cache_destroy(void) {
207 +#ifdef NETDATA_INTERNAL_CHECKS
208 + if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
209 + error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
210 +
211 + web_client_cache_verify(1);
212 +#endif
213 +
214 + struct web_client *w, *t;
215 +
216 + w = web_clients_cache.used;
217 + while(w) {
218 + t = w;
219 + w = w->next;
220 + web_client_free(t);
221 + }
222 + web_clients_cache.used = NULL;
223 + web_clients_cache.used_count = 0;
224 +
225 + w = web_clients_cache.avail;
226 + while(w) {
227 + t = w;
228 + w = w->next;
229 + web_client_free(t);
230 + }
231 + web_clients_cache.avail = NULL;
232 + web_clients_cache.avail_count = 0;
233 +}
234 +
235 +static struct web_client *web_client_get_from_cache_or_allocate() {
236 +
237 +#ifdef NETDATA_INTERNAL_CHECKS
238 + if(unlikely(web_clients_cache.pid == 0))
239 + web_clients_cache.pid = gettid();
240 +
241 + if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
242 + error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
243 +#endif
244 +
245 + struct web_client *w = web_clients_cache.avail;
246 +
247 + if(w) {
248 + // get it from avail
249 + if (w == web_clients_cache.avail) web_clients_cache.avail = w->next;
250 + if(w->prev) w->prev->next = w->next;
251 + if(w->next) w->next->prev = w->prev;
252 + web_clients_cache.avail_count--;
253 + web_client_zero(w);
254 + web_clients_cache.reused++;
255 + }
256 + else {
257 + // allocate it
258 + w = web_client_alloc();
259 + web_clients_cache.allocated++;
260 + }
261 +
262 + // link it to used web clients
263 + if (web_clients_cache.used) web_clients_cache.used->prev = w;
264 + w->next = web_clients_cache.used;
265 + w->prev = NULL;
266 + web_clients_cache.used = w;
267 + web_clients_cache.used_count++;
268 +
269 + // initialize it
270 + w->id = web_client_connected();
271 + w->mode = WEB_CLIENT_MODE_NORMAL;
272 + return w;
273 +}
274 +
275 +static void web_client_release(struct web_client *w) {
276 +#ifdef NETDATA_INTERNAL_CHECKS
277 + if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid()))
278 + error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid());
279 +
280 + if(unlikely(w->running))
281 + error("%llu: releasing web client from %s port %s, but it still running.", w->id, w->client_ip, w->client_port);
282 +#endif
283 +
284 + debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port);
285 +
286 + web_client_request_done(w);
287 + web_client_disconnected();
288 +
289 + if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
290 + if (w->ifd != -1) close(w->ifd);
291 + if (w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
292 + }
293 +
294 + // unlink it from the used
295 + if (w == web_clients_cache.used) web_clients_cache.used = w->next;
296 + if(w->prev) w->prev->next = w->next;
297 + if(w->next) w->next->prev = w->prev;
298 + web_clients_cache.used_count--;
299 +
300 + if(web_clients_cache.avail_count >= 2 * web_clients_cache.used_count) {
301 + // we have too many of them - free it
302 + web_client_free(w);
303 + }
304 + else {
305 + // link it to the avail
306 + if (web_clients_cache.avail) web_clients_cache.avail->prev = w;
307 + w->next = web_clients_cache.avail;
308 + w->prev = NULL;
309 + web_clients_cache.avail = w;
310 + web_clients_cache.avail_count++;
311 + }
312 +}
313 +
314 +
315 +// ----------------------------------------------------------------------------
316 +// high level web clients connection management
317 +
318 +static void web_client_initialize_connection(struct web_client *w) {
319 + int flag = 1;
320 + if(setsockopt(w->ifd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0)
321 + error("%llu: failed to enable TCP_NODELAY on socket fd %d.", w->id, w->ifd);
322 +
323 + flag = 1;
324 + if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0)
325 + error("%llu: failed to enable SO_KEEPALIVE on socket fd %d.", w->id, w->ifd);
326 +
327 + web_client_update_acl_matches(w);
328 +
329 + w->origin[0] = '*'; w->origin[1] = '\0';
330 + web_client_enable_wait_receive(w);
331 +
332 + log_connection(w, "CONNECTED");
333 +
334 + web_client_cache_verify(0);
335 +}
336 +
337 +static struct web_client *web_client_create_on_fd(int fd, const char *client_ip, const char *client_port) {
338 + struct web_client *w;
339 +
340 + w = web_client_get_from_cache_or_allocate();
341 + w->ifd = w->ofd = fd;
342 +
343 + strncpyz(w->client_ip, client_ip, sizeof(w->client_ip) - 1);
344 + strncpyz(w->client_port, client_port, sizeof(w->client_port) - 1);
345 +
346 + if(unlikely(!*w->client_ip)) strcpy(w->client_ip, "-");
347 + if(unlikely(!*w->client_port)) strcpy(w->client_port, "-");
348 +
349 + web_client_initialize_connection(w);
350 + return(w);
351 +}
352 +
353 +static struct web_client *web_client_create_on_listenfd(int listener) {
354 + struct web_client *w;
355 +
356 + w = web_client_get_from_cache_or_allocate();
357 + w->ifd = w->ofd = accept_socket(listener, SOCK_NONBLOCK, w->client_ip, sizeof(w->client_ip), w->client_port, sizeof(w->client_port), web_allow_connections_from);
358 +
359 + if(unlikely(!*w->client_ip)) strcpy(w->client_ip, "-");
360 + if(unlikely(!*w->client_port)) strcpy(w->client_port, "-");
361 +
362 + if (w->ifd == -1) {
363 + if(errno == EPERM)
364 + log_connection(w, "ACCESS DENIED");
365 + else {
366 + log_connection(w, "CONNECTION FAILED");
367 + error("%llu: Failed to accept new incoming connection.", w->id);
368 + }
369 +
370 + web_client_release(w);
371 + return NULL;
372 + }
373 +
374 + web_client_initialize_connection(w);
375 + return(w);
376 +}
377 +
378 +
379 +// --------------------------------------------------------------------------------------
380 +// the thread of a single client - for the MULTI-THREADED web server
381 +
382 +// 1. waits for input and output, using async I/O
383 +// 2. it processes HTTP requests
384 +// 3. it generates HTTP responses
385 +// 4. it copies data from input to output if mode is FILECOPY
386 +
387 +int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS;
388 +
389 +static void multi_threaded_web_client_worker_main_cleanup(void *ptr) {
390 + struct web_client *w = ptr;
391 + WEB_CLIENT_IS_DEAD(w);
392 + w->running = 0;
393 +}
394 +
395 +static void *multi_threaded_web_client_worker_main(void *ptr) {
396 + netdata_thread_cleanup_push(multi_threaded_web_client_worker_main_cleanup, ptr);
397 +
398 + struct web_client *w = ptr;
399 + w->running = 1;
400 +
401 + struct pollfd fds[2], *ifd, *ofd;
402 + int retval, timeout;
403 + nfds_t fdmax = 0;
404 +
405 + while(!netdata_exit) {
406 + if(unlikely(web_client_check_dead(w))) {
407 + debug(D_WEB_CLIENT, "%llu: client is dead.", w->id);
408 + break;
409 + }
410 + else if(unlikely(!web_client_has_wait_receive(w) && !web_client_has_wait_send(w))) {
411 + debug(D_WEB_CLIENT, "%llu: client is not set for neither receiving nor sending data.", w->id);
412 + break;
413 + }
414 +
415 + if(unlikely(w->ifd < 0 || w->ofd < 0)) {
416 + error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd", w->id, w->ifd, w->ofd);
417 + break;
418 + }
419 +
420 + if(w->ifd == w->ofd) {
421 + fds[0].fd = w->ifd;
422 + fds[0].events = 0;
423 + fds[0].revents = 0;
424 +
425 + if(web_client_has_wait_receive(w)) fds[0].events |= POLLIN;
426 + if(web_client_has_wait_send(w)) fds[0].events |= POLLOUT;
427 +
428 + fds[1].fd = -1;
429 + fds[1].events = 0;
430 + fds[1].revents = 0;
431 +
432 + ifd = ofd = &fds[0];
433 +
434 + fdmax = 1;
435 + }
436 + else {
437 + fds[0].fd = w->ifd;
438 + fds[0].events = 0;
439 + fds[0].revents = 0;
440 + if(web_client_has_wait_receive(w)) fds[0].events |= POLLIN;
441 + ifd = &fds[0];
442 +
443 + fds[1].fd = w->ofd;
444 + fds[1].events = 0;
445 + fds[1].revents = 0;
446 + if(web_client_has_wait_send(w)) fds[1].events |= POLLOUT;
447 + ofd = &fds[1];
448 +
449 + fdmax = 2;
450 + }
451 +
452 + debug(D_WEB_CLIENT, "%llu: Waiting socket async I/O for %s %s", w->id, web_client_has_wait_receive(w)?"INPUT":"", web_client_has_wait_send(w)?"OUTPUT":"");
453 + errno = 0;
454 + timeout = web_client_timeout * 1000;
455 + retval = poll(fds, fdmax, timeout);
456 +
457 + if(unlikely(netdata_exit)) break;
458 +
459 + if(unlikely(retval == -1)) {
460 + if(errno == EAGAIN || errno == EINTR) {
461 + debug(D_WEB_CLIENT, "%llu: EAGAIN received.", w->id);
462 + continue;
463 + }
464 +
465 + debug(D_WEB_CLIENT, "%llu: LISTENER: poll() failed (input fd = %d, output fd = %d). Closing client.", w->id, w->ifd, w->ofd);
466 + break;
467 + }
468 + else if(unlikely(!retval)) {
469 + debug(D_WEB_CLIENT, "%llu: Timeout while waiting socket async I/O for %s %s", w->id, web_client_has_wait_receive(w)?"INPUT":"", web_client_has_wait_send(w)?"OUTPUT":"");
470 + break;
471 + }
472 +
473 + if(unlikely(netdata_exit)) break;
474 +
475 + int used = 0;
476 + if(web_client_has_wait_send(w) && ofd->revents & POLLOUT) {
477 + used++;
478 + if(web_client_send(w) < 0) {
479 + debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
480 + break;
481 + }
482 + }
483 +
484 + if(unlikely(netdata_exit)) break;
485 +
486 + if(web_client_has_wait_receive(w) && (ifd->revents & POLLIN || ifd->revents & POLLPRI)) {
487 + used++;
488 + if(web_client_receive(w) < 0) {
489 + debug(D_WEB_CLIENT, "%llu: Cannot receive data from client. Closing client.", w->id);
490 + break;
491 + }
492 +
493 + if(w->mode == WEB_CLIENT_MODE_NORMAL) {
494 + debug(D_WEB_CLIENT, "%llu: Attempting to process received data.", w->id);
495 + web_client_process_request(w);
496 +
497 + // if the sockets are closed, may have transferred this client
498 + // to plugins.d
499 + if(unlikely(w->mode == WEB_CLIENT_MODE_STREAM))
500 + break;
501 + }
502 + }
503 +
504 + if(unlikely(!used)) {
505 + debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on socket.", w->id);
506 + break;
507 + }
508 + }
509 +
510 + if(w->mode != WEB_CLIENT_MODE_STREAM)
511 + log_connection(w, "DISCONNECTED");
512 +
513 + web_client_request_done(w);
514 +
515 + debug(D_WEB_CLIENT, "%llu: done...", w->id);
516 +
517 + // close the sockets/files now
518 + // to free file descriptors
519 + if(w->ifd == w->ofd) {
520 + if(w->ifd != -1) close(w->ifd);
521 + }
522 + else {
523 + if(w->ifd != -1) close(w->ifd);
524 + if(w->ofd != -1) close(w->ofd);
525 + }
526 + w->ifd = -1;
527 + w->ofd = -1;
528 +
529 + netdata_thread_cleanup_pop(1);
530 + return NULL;
531 +}
532 +
533 // --------------------------------------------------------------------------------------
534 // the main socket listener - MULTI-THREADED
535
@@ -58,6 +538,44 @@ int api_listen_sockets_setup(void) {
538 // 3. spawns a new netdata_thread to serve the client (this is optimal for keep-alive clients)
539 // 4. cleans up old web_clients that their netdata_threads have been exited
540
541 +static void web_client_multi_threaded_web_server_release_clients(void) {
542 + struct web_client *w;
543 + for(w = web_clients_cache.used; w ; ) {
544 + if(unlikely(!w->running && web_client_check_dead(w))) {
545 + struct web_client *t = w->next;
546 + web_client_release(w);
547 + w = t;
548 + }
549 + else
550 + w = w->next;
551 + }
552 +}
553 +
554 +static void web_client_multi_threaded_web_server_stop_all_threads(void) {
555 + struct web_client *w;
556 +
557 + int found = 1, max = 2 * USEC_PER_SEC, step = 50000;
558 + for(w = web_clients_cache.used; w ; w = w->next) {
559 + if(w->running) {
560 + found++;
561 + info("stopping web client %s, id %llu", w->client_ip, w->id);
562 + netdata_thread_cancel(w->thread);
563 + }
564 + }
565 +
566 + while(found && max > 0) {
567 + max -= step;
568 + info("Waiting %d web threads to finish...", found);
569 + sleep_usec(step);
570 + found = 0;
571 + for(w = web_clients_cache.used; w ; w = w->next)
572 + if(w->running) found++;
573 + }
574 +
575 + if(found)
576 + error("%d web threads are taking too long to finish. Giving up.", found);
577 +}
578 +
579 static struct pollfd *socket_listen_main_multi_threaded_fds = NULL;
580
581 static void socket_listen_main_multi_threaded_cleanup(void *data) {
@@ -83,6 +601,7 @@ static void socket_listen_main_multi_threaded_cleanup(void *data) {
601 }
602 }
603
604 +#define CLEANUP_EVERY_EVENTS 60
605 void *socket_listen_main_multi_threaded(void *ptr) {
606 netdata_thread_cleanup_push(socket_listen_main_multi_threaded_cleanup, ptr);
607
@@ -90,7 +609,7 @@ void *socket_listen_main_multi_threaded(void *ptr) {
609 web_server_is_multithreaded = 1;
610
611 struct web_client *w;
93 - int retval;
612 + int retval, counter = 0;
613
614 if(!api_sockets.opened)
615 fatal("LISTENER: No sockets to listen to.");
@@ -106,7 +625,7 @@ void *socket_listen_main_multi_threaded(void *ptr) {
625 info("Listening on '%s'", (api_sockets.fds_names[i])?api_sockets.fds_names[i]:"UNKNOWN");
626 }
627
109 - int timeout = 10 * 1000;
628 + int timeout = 1 * 1000;
629
630 while(!netdata_exit) {
631
@@ -119,6 +638,7 @@ void *socket_listen_main_multi_threaded(void *ptr) {
638 }
639 else if(unlikely(!retval)) {
640 debug(D_WEB_CLIENT, "LISTENER: poll() timeout.");
641 + counter++;
642 continue;
643 }
644
@@ -143,24 +663,33 @@ void *socket_listen_main_multi_threaded(void *ptr) {
663 char tag[NETDATA_THREAD_TAG_MAX + 1];
664 snprintfz(tag, NETDATA_THREAD_TAG_MAX, "WEB_CLIENT[%llu,[%s]:%s]", w->id, w->client_ip, w->client_port);
665
146 - if(netdata_thread_create(&w->thread, tag, NETDATA_THREAD_OPTION_DONT_LOG, web_client_main, w) != 0)
147 - WEB_CLIENT_IS_OBSOLETE(w);
666 + w->running = 1;
667 + if(netdata_thread_create(&w->thread, tag, NETDATA_THREAD_OPTION_DONT_LOG, multi_threaded_web_client_worker_main, w) != 0) {
668 + w->running = 0;
669 + web_client_release(w);
670 + }
671 }
672 }
673 +
674 + counter++;
675 + if(counter > CLEANUP_EVERY_EVENTS) {
676 + counter = 0;
677 + web_client_multi_threaded_web_server_release_clients();
678 + }
679 }
680
681 netdata_thread_cleanup_pop(1);
682 return NULL;
683 }
684
685 +
686 // --------------------------------------------------------------------------------------
687 // the main socket listener - SINGLE-THREADED
688
689 struct web_client *single_threaded_clients[FD_SETSIZE];
690
691 static inline int single_threaded_link_client(struct web_client *w, fd_set *ifds, fd_set *ofds, fd_set *efds, int *max) {
162 - if(unlikely(web_client_check_obsolete(w) || web_client_check_dead(w) || (!web_client_has_wait_receive(w) && !web_client_has_wait_send(w)))) {
163 - // error("refusing to link obsolete/dead client");
692 + if(unlikely(web_client_check_dead(w) || (!web_client_has_wait_receive(w) && !web_client_has_wait_send(w)))) {
693 return 1;
694 }
695
@@ -196,8 +725,7 @@ static inline int single_threaded_unlink_client(struct web_client *w, fd_set *if
725 single_threaded_clients[w->ifd] = NULL;
726 single_threaded_clients[w->ofd] = NULL;
727
199 - if(unlikely(web_client_check_obsolete(w) || web_client_check_dead(w) || (!web_client_has_wait_receive(w) && !web_client_has_wait_send(w)))) {
200 - // error("unlinked client is obsolete/dead");
728 + if(unlikely(web_client_check_dead(w) || (!web_client_has_wait_receive(w) && !web_client_has_wait_send(w)))) {
729 return 1;
730 }
731
@@ -347,6 +875,7 @@ void *socket_listen_main_single_threaded(void *ptr) {
875 return NULL;
876 }
877
878 +
879 // --------------------------------------------------------------------------------------
880 // the main socket listener - STATIC-THREADED
881
@@ -372,7 +901,7 @@ static __thread struct web_server_static_threaded_worker *worker_private = NULL;
901 // ----------------------------------------------------------------------------
902
903 static inline int web_server_check_client_status(struct web_client *w) {
375 - if(unlikely(web_client_check_obsolete(w) || web_client_check_dead(w) || (!web_client_has_wait_receive(w) && !web_client_has_wait_send(w))))
904 + if(unlikely(web_client_check_dead(w) || (!web_client_has_wait_receive(w) && !web_client_has_wait_send(w))))
905 return -1;
906
907 return 0;
src/web_server.h
+10
@@ -21,6 +21,13 @@ typedef enum web_server_mode {
21 WEB_SERVER_MODE_NONE
22 } WEB_SERVER_MODE;
23
24 +extern SIMPLE_PATTERN *web_allow_connections_from;
25 +extern SIMPLE_PATTERN *web_allow_dashboard_from;
26 +extern SIMPLE_PATTERN *web_allow_registry_from;
27 +extern SIMPLE_PATTERN *web_allow_badges_from;
28 +extern SIMPLE_PATTERN *web_allow_streaming_from;
29 +extern SIMPLE_PATTERN *web_allow_netdataconf_from;
30 +
31 extern WEB_SERVER_MODE web_server_mode;
32
33 extern WEB_SERVER_MODE web_server_mode_id(const char *mode);
@@ -31,4 +38,7 @@ extern void *socket_listen_main_single_threaded(void *ptr);
38 extern void *socket_listen_main_static_threaded(void *ptr);
39 extern int api_listen_sockets_setup(void);
40
41 +#define DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS 60
42 +extern int web_client_timeout;
43 +
44 #endif /* NETDATA_WEB_SERVER_H */