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 */