summaryrefslogtreecommitdiffstats
path: root/web/server
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2023-07-20 04:50:01 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2023-07-20 04:50:01 +0000
commitcd4377fab21e0f500bef7f06543fa848a039c1e0 (patch)
treeba00a55e430c052d6bed0b61c0f8bbe8ebedd313 /web/server
parentReleasing debian version 1.40.1-1. (diff)
downloadnetdata-cd4377fab21e0f500bef7f06543fa848a039c1e0.tar.xz
netdata-cd4377fab21e0f500bef7f06543fa848a039c1e0.zip
Merging upstream version 1.41.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'web/server')
-rw-r--r--web/server/h2o/h2o_utils.c60
-rw-r--r--web/server/h2o/h2o_utils.h38
-rw-r--r--web/server/h2o/http_server.c339
-rw-r--r--web/server/h2o/http_server.h10
-rw-r--r--web/server/static/static-threaded.c62
-rw-r--r--web/server/web_client.c702
-rw-r--r--web/server/web_client.h46
-rw-r--r--web/server/web_client_cache.c32
-rw-r--r--web/server/web_server.c4
9 files changed, 1007 insertions, 286 deletions
diff --git a/web/server/h2o/h2o_utils.c b/web/server/h2o/h2o_utils.c
new file mode 100644
index 000000000..943216f59
--- /dev/null
+++ b/web/server/h2o/h2o_utils.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "h2o_utils.h"
+
+#include "h2o/string_.h"
+
+#include "libnetdata/libnetdata.h"
+
+char *iovec_to_cstr(h2o_iovec_t *str)
+{
+ char *c_str = mallocz(str->len + 1);
+ memcpy(c_str, str->base, str->len);
+ c_str[str->len] = 0;
+ return c_str;
+}
+
+#define KEY_VAL_BUFFER_GROWTH_STEP 5
+h2o_iovec_pair_vector_t *parse_URL_params(h2o_mem_pool_t *pool, h2o_iovec_t params_string)
+{
+ h2o_iovec_pair_vector_t *params_vec = h2o_mem_alloc_shared(pool, sizeof(h2o_iovec_pair_vector_t), NULL);
+ memset(params_vec, 0, sizeof(h2o_iovec_pair_vector_t));
+
+ h2o_iovec_pair_t param;
+ while ((param.name.base = (char*)h2o_next_token(&params_string, '&', &param.name.len, &param.value)) != NULL) {
+ if (params_vec->capacity == params_vec->size)
+ h2o_vector_reserve(pool, params_vec, params_vec->capacity + KEY_VAL_BUFFER_GROWTH_STEP);
+
+ params_vec->entries[params_vec->size++] = param;
+ }
+
+ return params_vec;
+}
+
+h2o_iovec_pair_t *get_URL_param_by_name(h2o_iovec_pair_vector_t *params_vec, const void *needle, size_t needle_len)
+{
+ for (size_t i = 0; i < params_vec->size; i++) {
+ h2o_iovec_pair_t *ret = &params_vec->entries[i];
+ if (h2o_memis(ret->name.base, ret->name.len, needle, needle_len))
+ return ret;
+ }
+ return NULL;
+}
+
+char *url_unescape(const char *url)
+{
+ char *result = mallocz(strlen(url) + 1);
+
+ int i, j;
+ for (i = 0, j = 0; url[i] != 0; i++, j++) {
+ if (url[i] == '%' && isxdigit(url[i+1]) && isxdigit(url[i+2])) {
+ char hex[3] = { url[i+1], url[i+2], 0 };
+ result[j] = strtol(hex, NULL, 16);
+ i += 2;
+ } else
+ result[j] = url[i];
+ }
+ result[j] = 0;
+
+ return result;
+}
diff --git a/web/server/h2o/h2o_utils.h b/web/server/h2o/h2o_utils.h
new file mode 100644
index 000000000..6760ed9a9
--- /dev/null
+++ b/web/server/h2o/h2o_utils.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_H2O_UTILS_H
+#define NETDATA_H2O_UTILS_H
+
+#include "h2o/memory.h"
+
+#define __HAS_URL_PARAMS(reqptr) ((reqptr)->query_at != SIZE_MAX && ((reqptr)->path.len - (reqptr)->query_at > 1))
+#define IF_HAS_URL_PARAMS(reqptr) if __HAS_URL_PARAMS(reqptr)
+#define UNLESS_HAS_URL_PARAMS(reqptr) if (!__HAS_URL_PARAMS(reqptr))
+#define URL_PARAMS_IOVEC_INIT(reqptr) { .base = &(reqptr)->path.base[(reqptr)->query_at + 1], \
+ .len = (reqptr)->path.len - (reqptr)->query_at - 1 }
+#define URL_PARAMS_IOVEC_INIT_WITH_QUESTIONMARK(reqptr) { .base = &(reqptr)->path.base[(reqptr)->query_at], \
+ .len = (reqptr)->path.len - (reqptr)->query_at }
+
+#define PRINTF_H2O_IOVEC_FMT "%.*s"
+#define PRINTF_H2O_IOVEC(iovec) ((int)(iovec)->len), ((iovec)->base)
+
+char *iovec_to_cstr(h2o_iovec_t *str);
+
+typedef struct h2o_iovec_pair {
+ h2o_iovec_t name;
+ h2o_iovec_t value;
+} h2o_iovec_pair_t;
+
+typedef H2O_VECTOR(h2o_iovec_pair_t) h2o_iovec_pair_vector_t;
+
+// Takes the part of url behind ? (the url encoded parameters)
+// and parse it to vector of name/value pairs without copying the actual strings
+h2o_iovec_pair_vector_t *parse_URL_params(h2o_mem_pool_t *pool, h2o_iovec_t params_string);
+
+// Searches for parameter by name (provided in needle)
+// returns pointer to it or NULL
+h2o_iovec_pair_t *get_URL_param_by_name(h2o_iovec_pair_vector_t *params_vec, const void *needle, size_t needle_len);
+
+char *url_unescape(const char *url);
+
+#endif /* NETDATA_H2O_UTILS_H */
diff --git a/web/server/h2o/http_server.c b/web/server/h2o/http_server.c
new file mode 100644
index 000000000..3a46889c2
--- /dev/null
+++ b/web/server/h2o/http_server.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "daemon/common.h"
+#include "http_server.h"
+#include "h2o.h"
+
+#include "h2o_utils.h"
+
+static h2o_globalconf_t config;
+static h2o_context_t ctx;
+static h2o_accept_ctx_t accept_ctx;
+
+#define CONTENT_JSON_UTF8 H2O_STRLIT("application/json; charset=utf-8")
+#define CONTENT_TEXT_UTF8 H2O_STRLIT("text/plain; charset=utf-8")
+#define NBUF_INITIAL_SIZE_RESP (4096)
+#define API_V1_PREFIX "/api/v1/"
+#define HOST_SELECT_PREFIX "/host/"
+
+#define HTTPD_CONFIG_SECTION "httpd"
+#define HTTPD_ENABLED_DEFAULT false
+
+static void on_accept(h2o_socket_t *listener, const char *err)
+{
+ h2o_socket_t *sock;
+
+ if (err != NULL) {
+ return;
+ }
+
+ if ((sock = h2o_evloop_socket_accept(listener)) == NULL)
+ return;
+ h2o_accept(&accept_ctx, sock);
+}
+
+static int create_listener(const char *ip, int port)
+{
+ struct sockaddr_in addr;
+ int fd, reuseaddr_flag = 1;
+ h2o_socket_t *sock;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(ip);
+ addr.sin_port = htons(port);
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ||
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_flag, sizeof(reuseaddr_flag)) != 0 ||
+ bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0 || listen(fd, SOMAXCONN) != 0) {
+ return -1;
+ }
+
+ sock = h2o_evloop_socket_create(ctx.loop, fd, H2O_SOCKET_FLAG_DONT_READ);
+ h2o_socket_read_start(sock, on_accept);
+
+ return 0;
+}
+
+static int ssl_init()
+{
+ if (!config_get_boolean(HTTPD_CONFIG_SECTION, "ssl", false))
+ return 0;
+
+ char default_fn[FILENAME_MAX + 1];
+
+ snprintfz(default_fn, FILENAME_MAX, "%s/ssl/key.pem", netdata_configured_user_config_dir);
+ const char *key_fn = config_get(HTTPD_CONFIG_SECTION, "ssl key", default_fn);
+
+ snprintfz(default_fn, FILENAME_MAX, "%s/ssl/cert.pem", netdata_configured_user_config_dir);
+ const char *cert_fn = config_get(HTTPD_CONFIG_SECTION, "ssl certificate", default_fn);
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110
+ accept_ctx.ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+#else
+ accept_ctx.ssl_ctx = SSL_CTX_new(TLS_server_method());
+#endif
+
+ SSL_CTX_set_options(accept_ctx.ssl_ctx, SSL_OP_NO_SSLv2);
+
+ /* load certificate and private key */
+ if (SSL_CTX_use_PrivateKey_file(accept_ctx.ssl_ctx, key_fn, SSL_FILETYPE_PEM) != 1) {
+ netdata_log_error("Could not load server key from \"%s\"", key_fn);
+ return -1;
+ }
+ if (SSL_CTX_use_certificate_file(accept_ctx.ssl_ctx, cert_fn, SSL_FILETYPE_PEM) != 1) {
+ netdata_log_error("Could not load certificate from \"%s\"", cert_fn);
+ return -1;
+ }
+
+ h2o_ssl_register_alpn_protocols(accept_ctx.ssl_ctx, h2o_http2_alpn_protocols);
+
+ netdata_log_info("SSL support enabled");
+
+ return 0;
+}
+
+// I did not find a way to do wildcard paths to make common handler for urls like:
+// /api/v1/info
+// /host/child/api/v1/info
+// /host/uuid/api/v1/info
+// ideally we could do something like "/*/api/v1/info" subscription
+// so we do it "manually" here with uberhandler
+static inline int _netdata_uberhandler(h2o_req_t *req, RRDHOST **host)
+{
+ if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")))
+ return -1;
+
+ static h2o_generator_t generator = { NULL, NULL };
+
+ h2o_iovec_t norm_path = req->path_normalized;
+
+ if (norm_path.len > strlen(HOST_SELECT_PREFIX) && !memcmp(norm_path.base, HOST_SELECT_PREFIX, strlen(HOST_SELECT_PREFIX))) {
+ h2o_iovec_t host_id; // host_id can be either and UUID or a hostname of the child
+
+ norm_path.base += strlen(HOST_SELECT_PREFIX);
+ norm_path.len -= strlen(HOST_SELECT_PREFIX);
+
+ host_id = norm_path;
+
+ size_t end_loc = h2o_strstr(host_id.base, host_id.len, "/", 1);
+ if (end_loc != SIZE_MAX) {
+ host_id.len = end_loc;
+ norm_path.base += end_loc;
+ norm_path.len -= end_loc;
+ }
+
+ char *c_host_id = iovec_to_cstr(&host_id);
+ *host = rrdhost_find_by_hostname(c_host_id);
+ if (!*host)
+ *host = rrdhost_find_by_guid(c_host_id);
+ if (!*host) {
+ req->res.status = HTTP_RESP_BAD_REQUEST;
+ req->res.reason = "Wrong host id";
+ h2o_send_inline(req, H2O_STRLIT("Host id provided was not found!\n"));
+ freez(c_host_id);
+ return 0;
+ }
+ freez(c_host_id);
+
+ // we have to rewrite URL here in case this is not an api call
+ // so that the subsequent file upload handler can send the correct
+ // files to the client
+ // if this is not an API call we will abort this handler later
+ // and let the internal serve file handler of h2o care for things
+
+ if (end_loc == SIZE_MAX) {
+ req->path.len = 1;
+ req->path_normalized.len = 1;
+ } else {
+ size_t offset = norm_path.base - req->path_normalized.base;
+ req->path.len -= offset;
+ req->path.base += offset;
+ req->query_at -= offset;
+ req->path_normalized.len -= offset;
+ req->path_normalized.base += offset;
+ }
+ }
+
+ // workaround for a dashboard bug which causes sometimes urls like
+ // "//api/v1/info" to be caled instead of "/api/v1/info"
+ if (norm_path.len > 2 &&
+ norm_path.base[0] == '/' &&
+ norm_path.base[1] == '/' ) {
+ norm_path.base++;
+ norm_path.len--;
+ }
+
+ size_t api_loc = h2o_strstr(norm_path.base, norm_path.len, H2O_STRLIT(API_V1_PREFIX));
+ if (api_loc == SIZE_MAX)
+ return 1;
+
+ h2o_iovec_t api_command = norm_path;
+ api_command.base += api_loc + strlen(API_V1_PREFIX);
+ api_command.len -= api_loc + strlen(API_V1_PREFIX);
+
+ if (!api_command.len)
+ return 1;
+
+ // this (emulating struct web_client) is a hack and will be removed
+ // in future PRs but needs bigger changes in old http_api_v1
+ // we need to make the web_client_api_request_v1 to be web server
+ // agnostic and remove the old webservers dependency creep into the
+ // individual response generators and thus remove the need to "emulate"
+ // the old webserver calling this function here and in ACLK
+ struct web_client w;
+ w.response.data = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
+ w.response.header = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
+ w.url_query_string_decoded = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
+ w.acl = WEB_CLIENT_ACL_DASHBOARD;
+
+ char *path_c_str = iovec_to_cstr(&api_command);
+ char *path_unescaped = url_unescape(path_c_str);
+ freez(path_c_str);
+
+ IF_HAS_URL_PARAMS(req) {
+ h2o_iovec_t query_params = URL_PARAMS_IOVEC_INIT_WITH_QUESTIONMARK(req);
+ char *query_c_str = iovec_to_cstr(&query_params);
+ char *query_unescaped = url_unescape(query_c_str);
+ freez(query_c_str);
+ buffer_strcat(w.url_query_string_decoded, query_unescaped);
+ freez(query_unescaped);
+ }
+
+ web_client_api_request_v1(*host, &w, path_unescaped);
+ freez(path_unescaped);
+
+ h2o_iovec_t body = buffer_to_h2o_iovec(w.response.data);
+
+ // we move msg body to req->pool managed memory as it has to
+ // live until whole response has been encrypted and sent
+ // when req is finished memory will be freed with the pool
+ void *managed = h2o_mem_alloc_shared(&req->pool, body.len, NULL);
+ memcpy(managed, body.base, body.len);
+ body.base = managed;
+
+ req->res.status = HTTP_RESP_OK;
+ req->res.reason = "OK";
+ if (w.response.data->content_type == CT_APPLICATION_JSON)
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_JSON_UTF8);
+ else
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_TEXT_UTF8);
+ h2o_start_response(req, &generator);
+ h2o_send(req, &body, 1, H2O_SEND_STATE_FINAL);
+
+ buffer_free(w.response.data);
+ buffer_free(w.response.header);
+ buffer_free(w.url_query_string_decoded);
+
+ return 0;
+}
+
+static int netdata_uberhandler(h2o_handler_t *self, h2o_req_t *req)
+{
+ UNUSED(self);
+ RRDHOST *host = localhost;
+
+ int ret = _netdata_uberhandler(req, &host);
+
+ char host_uuid_str[UUID_STR_LEN];
+ uuid_unparse_lower(host->host_uuid, host_uuid_str);
+
+ if (!ret) {
+ netdata_log_access("HTTPD OK method: " PRINTF_H2O_IOVEC_FMT
+ ", path: " PRINTF_H2O_IOVEC_FMT
+ ", as host: %s"
+ ", response: %d",
+ PRINTF_H2O_IOVEC(&req->method),
+ PRINTF_H2O_IOVEC(&req->input.path),
+ host == localhost ? "localhost" : host_uuid_str,
+ req->res.status);
+ } else {
+ netdata_log_access("HTTPD %d"
+ " method: " PRINTF_H2O_IOVEC_FMT
+ ", path: " PRINTF_H2O_IOVEC_FMT
+ ", forwarding to file handler as path: " PRINTF_H2O_IOVEC_FMT,
+ ret,
+ PRINTF_H2O_IOVEC(&req->method),
+ PRINTF_H2O_IOVEC(&req->input.path),
+ PRINTF_H2O_IOVEC(&req->path));
+ }
+
+ return ret;
+}
+
+static int hdl_netdata_conf(h2o_handler_t *self, h2o_req_t *req)
+{
+ UNUSED(self);
+ if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")))
+ return -1;
+
+ BUFFER *buf = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
+ config_generate(buf, 0);
+
+ void *managed = h2o_mem_alloc_shared(&req->pool, buf->len, NULL);
+ memcpy(managed, buf->buffer, buf->len);
+
+ req->res.status = HTTP_RESP_OK;
+ req->res.reason = "OK";
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_TEXT_UTF8);
+ h2o_send_inline(req, managed, buf->len);
+ buffer_free(buf);
+
+ return 0;
+}
+
+#define POLL_INTERVAL 100
+
+void *h2o_main(void *ptr) {
+ struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
+
+ h2o_pathconf_t *pathconf;
+ h2o_hostconf_t *hostconf;
+
+ netdata_thread_disable_cancelability();
+
+ const char *bind_addr = config_get(HTTPD_CONFIG_SECTION, "bind to", "127.0.0.1");
+ int bind_port = config_get_number(HTTPD_CONFIG_SECTION, "port", 19998);
+
+ h2o_config_init(&config);
+ hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), bind_port);
+
+ pathconf = h2o_config_register_path(hostconf, "/netdata.conf", 0);
+ h2o_handler_t *handler = h2o_create_handler(pathconf, sizeof(*handler));
+ handler->on_req = hdl_netdata_conf;
+
+ pathconf = h2o_config_register_path(hostconf, "/", 0);
+ handler = h2o_create_handler(pathconf, sizeof(*handler));
+ handler->on_req = netdata_uberhandler;
+ h2o_file_register(pathconf, netdata_configured_web_dir, NULL, NULL, H2O_FILE_FLAG_SEND_COMPRESSED);
+
+ h2o_context_init(&ctx, h2o_evloop_create(), &config);
+
+ if(ssl_init()) {
+ error_report("SSL was requested but could not be properly initialized. Aborting.");
+ return NULL;
+ }
+
+ accept_ctx.ctx = &ctx;
+ accept_ctx.hosts = config.hosts;
+
+ if (create_listener(bind_addr, bind_port) != 0) {
+ netdata_log_error("failed to create listener %s:%d", bind_addr, bind_port);
+ return NULL;
+ }
+
+ while (service_running(SERVICE_HTTPD)) {
+ int rc = h2o_evloop_run(ctx.loop, POLL_INTERVAL);
+ if (rc < 0 && errno != EINTR) {
+ netdata_log_error("h2o_evloop_run returned (%d) with errno other than EINTR. Aborting", rc);
+ break;
+ }
+ }
+
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
+ return NULL;
+}
+
+int httpd_is_enabled() {
+ return config_get_boolean(HTTPD_CONFIG_SECTION, "enabled", HTTPD_ENABLED_DEFAULT);
+}
diff --git a/web/server/h2o/http_server.h b/web/server/h2o/http_server.h
new file mode 100644
index 000000000..c9e6f0c53
--- /dev/null
+++ b/web/server/h2o/http_server.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef HTTP_SERVER_H
+#define HTTP_SERVER_H
+
+void *h2o_main(void * ptr);
+
+int httpd_is_enabled();
+
+#endif /* HTTP_SERVER_H */
diff --git a/web/server/static/static-threaded.c b/web/server/static/static-threaded.c
index 4cb3dcd92..b0e691163 100644
--- a/web/server/static/static-threaded.c
+++ b/web/server/static/static-threaded.c
@@ -41,11 +41,11 @@ static struct web_client *web_client_create_on_fd(POLLINFO *pi) {
int flag = 1;
if(unlikely(web_client_check_tcp(w) && setsockopt(w->ifd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0))
- debug(D_WEB_CLIENT, "%llu: failed to enable TCP_NODELAY on socket fd %d.", w->id, w->ifd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: failed to enable TCP_NODELAY on socket fd %d.", w->id, w->ifd);
flag = 1;
if(unlikely(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0))
- debug(D_WEB_CLIENT, "%llu: failed to enable SO_KEEPALIVE on socket fd %d.", w->id, w->ifd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: failed to enable SO_KEEPALIVE on socket fd %d.", w->id, w->ifd);
web_client_update_acl_matches(w);
web_client_enable_wait_receive(w);
@@ -101,7 +101,7 @@ static void *web_server_file_add_callback(POLLINFO *pi, short int *events, void
worker_private->files_read++;
- debug(D_WEB_CLIENT, "%llu: ADDED FILE READ ON FD %d", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: ADDED FILE READ ON FD %d", w->id, pi->fd);
*events = POLLIN;
pi->data = w;
@@ -111,14 +111,14 @@ static void *web_server_file_add_callback(POLLINFO *pi, short int *events, void
static void web_server_file_del_callback(POLLINFO *pi) {
struct web_client *w = (struct web_client *)pi->data;
- debug(D_WEB_CLIENT, "%llu: RELEASE FILE READ ON FD %d", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: RELEASE FILE READ ON FD %d", w->id, pi->fd);
worker_is_busy(WORKER_JOB_DEL_FILE);
w->pollinfo_filecopy_slot = 0;
if(unlikely(!w->pollinfo_slot)) {
- debug(D_WEB_CLIENT, "%llu: CROSS WEB CLIENT CLEANUP (iFD %d, oFD %d)", w->id, pi->fd, w->ofd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: CROSS WEB CLIENT CLEANUP (iFD %d, oFD %d)", w->id, pi->fd, w->ofd);
web_server_log_connection(w, "DISCONNECTED");
web_client_request_done(w);
web_client_release_to_cache(w);
@@ -137,18 +137,18 @@ static int web_server_file_read_callback(POLLINFO *pi, short int *events) {
// if there is no POLLINFO linked to this, it means the client disconnected
// stop the file reading too
if(unlikely(!w->pollinfo_slot)) {
- debug(D_WEB_CLIENT, "%llu: PREVENTED ATTEMPT TO READ FILE ON FD %d, ON CLOSED WEB CLIENT", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: PREVENTED ATTEMPT TO READ FILE ON FD %d, ON CLOSED WEB CLIENT", w->id, pi->fd);
retval = -1;
goto cleanup;
}
if(unlikely(w->mode != WEB_CLIENT_MODE_FILECOPY || w->ifd == w->ofd)) {
- debug(D_WEB_CLIENT, "%llu: PREVENTED ATTEMPT TO READ FILE ON FD %d, ON NON-FILECOPY WEB CLIENT", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: PREVENTED ATTEMPT TO READ FILE ON FD %d, ON NON-FILECOPY WEB CLIENT", w->id, pi->fd);
retval = -1;
goto cleanup;
}
- debug(D_WEB_CLIENT, "%llu: READING FILE ON FD %d", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: READING FILE ON FD %d", w->id, pi->fd);
worker_private->file_reads++;
ssize_t ret = unlikely(web_client_read_file(w));
@@ -157,12 +157,12 @@ static int web_server_file_read_callback(POLLINFO *pi, short int *events) {
POLLJOB *p = pi->p; // our POLLJOB
POLLINFO *wpi = pollinfo_from_slot(p, w->pollinfo_slot); // POLLINFO of the client socket
- debug(D_WEB_CLIENT, "%llu: SIGNALING W TO SEND (iFD %d, oFD %d)", w->id, pi->fd, wpi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: SIGNALING W TO SEND (iFD %d, oFD %d)", w->id, pi->fd, wpi->fd);
p->fds[wpi->slot].events |= POLLOUT;
}
if(unlikely(ret <= 0 || w->ifd == w->ofd)) {
- debug(D_WEB_CLIENT, "%llu: DONE READING FILE ON FD %d", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: DONE READING FILE ON FD %d", w->id, pi->fd);
retval = -1;
goto cleanup;
}
@@ -180,7 +180,7 @@ static int web_server_file_write_callback(POLLINFO *pi, short int *events) {
(void)events;
worker_is_busy(WORKER_JOB_WRITE_FILE);
- error("Writing to web files is not supported!");
+ netdata_log_error("Writing to web files is not supported!");
worker_is_idle();
return -1;
@@ -201,7 +201,7 @@ static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data
*events = POLLIN;
- debug(D_WEB_CLIENT_ACCESS, "LISTENER on %d: new connection.", pi->fd);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "LISTENER on %d: new connection.", pi->fd);
struct web_client *w = web_client_create_on_fd(pi);
if (!strncmp(pi->client_port, "UNIX", 4)) {
@@ -240,7 +240,7 @@ static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data
}
#endif
- debug(D_WEB_CLIENT, "%llu: ADDED CLIENT FD %d", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: ADDED CLIENT FD %d", w->id, pi->fd);
cleanup:
worker_is_idle();
@@ -260,13 +260,13 @@ static void web_server_del_callback(POLLINFO *pi) {
POLLINFO *fpi = pollinfo_from_slot(pi->p, w->pollinfo_filecopy_slot); // POLLINFO of the client socket
(void)fpi;
- debug(D_WEB_CLIENT, "%llu: THE CLIENT WILL BE FRED BY READING FILE JOB ON FD %d", w->id, fpi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: THE CLIENT WILL BE FRED BY READING FILE JOB ON FD %d", w->id, fpi->fd);
}
else {
if(web_client_flag_check(w, WEB_CLIENT_FLAG_DONT_CLOSE_SOCKET))
pi->flags |= POLLINFO_FLAG_DONT_CLOSE;
- debug(D_WEB_CLIENT, "%llu: CLOSING CLIENT FD %d", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: CLOSING CLIENT FD %d", w->id, pi->fd);
web_server_log_connection(w, "DISCONNECTED");
web_client_request_done(w);
web_client_release_to_cache(w);
@@ -289,7 +289,7 @@ static int web_server_rcv_callback(POLLINFO *pi, short int *events) {
bytes = web_client_receive(w);
if (likely(bytes > 0)) {
- debug(D_WEB_CLIENT, "%llu: processing received data on fd %d.", w->id, fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: processing received data on fd %d.", w->id, fd);
worker_is_idle();
worker_is_busy(WORKER_JOB_PROCESS);
web_client_process_request(w);
@@ -300,11 +300,11 @@ static int web_server_rcv_callback(POLLINFO *pi, short int *events) {
else if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) {
if(w->pollinfo_filecopy_slot == 0) {
- debug(D_WEB_CLIENT, "%llu: FILECOPY DETECTED ON FD %d", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: FILECOPY DETECTED ON FD %d", w->id, pi->fd);
if (unlikely(w->ifd != -1 && w->ifd != w->ofd && w->ifd != fd)) {
// add a new socket to poll_events, with the same
- debug(D_WEB_CLIENT, "%llu: CREATING FILECOPY SLOT ON FD %d", w->id, pi->fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: CREATING FILECOPY SLOT ON FD %d", w->id, pi->fd);
POLLINFO *fpi = poll_add_fd(
pi->p
@@ -325,7 +325,7 @@ static int web_server_rcv_callback(POLLINFO *pi, short int *events) {
if(fpi)
w->pollinfo_filecopy_slot = fpi->slot;
else {
- error("Failed to add filecopy fd. Closing client.");
+ netdata_log_error("Failed to add filecopy fd. Closing client.");
ret = -1;
goto cleanup;
}
@@ -366,7 +366,7 @@ static int web_server_snd_callback(POLLINFO *pi, short int *events) {
struct web_client *w = (struct web_client *)pi->data;
int fd = pi->fd;
- debug(D_WEB_CLIENT, "%llu: sending data on fd %d.", w->id, fd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: sending data on fd %d.", w->id, fd);
int ret = web_client_send(w);
@@ -394,7 +394,7 @@ cleanup:
static void socket_listen_main_static_threaded_worker_cleanup(void *ptr) {
worker_private = (struct web_server_static_threaded_worker *)ptr;
- info("stopped after %zu connects, %zu disconnects (max concurrent %zu), %zu receptions and %zu sends",
+ netdata_log_info("stopped after %zu connects, %zu disconnects (max concurrent %zu), %zu receptions and %zu sends",
worker_private->connected,
worker_private->disconnected,
worker_private->max_concurrent,
@@ -462,16 +462,16 @@ static void socket_listen_main_static_threaded_cleanup(void *ptr) {
// for(i = 1; i < static_threaded_workers_count; i++) {
// if(static_workers_private_data[i].running) {
// found++;
-// info("stopping worker %d", i + 1);
+// netdata_log_info("stopping worker %d", i + 1);
// netdata_thread_cancel(static_workers_private_data[i].thread);
// }
// else
-// info("found stopped worker %d", i + 1);
+// netdata_log_info("found stopped worker %d", i + 1);
// }
//
// while(found && max > 0) {
// max -= step;
-// info("Waiting %d static web threads to finish...", found);
+// netdata_log_info("Waiting %d static web threads to finish...", found);
// sleep_usec(step);
// found = 0;
//
@@ -483,12 +483,12 @@ static void socket_listen_main_static_threaded_cleanup(void *ptr) {
// }
//
// if(found)
-// error("%d static web threads are taking too long to finish. Giving up.", found);
+// netdata_log_error("%d static web threads are taking too long to finish. Giving up.", found);
- info("closing all web server sockets...");
+ netdata_log_info("closing all web server sockets...");
listen_sockets_close(&api_sockets);
- info("all static web threads stopped.");
+ netdata_log_info("all static web threads stopped.");
static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
}
@@ -502,7 +502,7 @@ void *socket_listen_main_static_threaded(void *ptr) {
netdata_ssl_validate_certificate = !config_get_boolean(CONFIG_SECTION_WEB, "ssl skip certificate verification", !netdata_ssl_validate_certificate);
if(!netdata_ssl_validate_certificate_sender)
- info("SSL: web server will skip SSL certificates verification.");
+ netdata_log_info("SSL: web server will skip SSL certificates verification.");
#ifdef ENABLE_HTTPS
netdata_ssl_initialize_ctx(NETDATA_SSL_WEB_SERVER_CTX);
@@ -514,7 +514,7 @@ void *socket_listen_main_static_threaded(void *ptr) {
int def_thread_count = MIN(get_netdata_cpus(), 6);
if (!strcmp(config_get(CONFIG_SECTION_WEB, "mode", ""),"single-threaded")) {
- info("Running web server with one thread, because mode is single-threaded");
+ netdata_log_info("Running web server with one thread, because mode is single-threaded");
config_set(CONFIG_SECTION_WEB, "mode", "static-threaded");
def_thread_count = 1;
}
@@ -526,7 +526,7 @@ void *socket_listen_main_static_threaded(void *ptr) {
// See https://github.com/netdata/netdata/issues/11081#issuecomment-831998240 for more details
if (OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110) {
static_threaded_workers_count = 1;
- info("You are running an OpenSSL older than 1.1.0, web server will not enable multithreading.");
+ netdata_log_info("You are running an OpenSSL older than 1.1.0, web server will not enable multithreading.");
}
#endif
@@ -546,7 +546,7 @@ void *socket_listen_main_static_threaded(void *ptr) {
char tag[50 + 1];
snprintfz(tag, 50, "WEB[%d]", i+1);
- info("starting worker %d", i+1);
+ netdata_log_info("starting worker %d", i+1);
netdata_thread_create(&static_workers_private_data[i].thread, tag, NETDATA_THREAD_OPTION_DEFAULT,
socket_listen_main_static_threaded_worker, (void *)&static_workers_private_data[i]);
}
diff --git a/web/server/web_client.c b/web/server/web_client.c
index 5dcff0b0f..1a1d63155 100644
--- a/web/server/web_client.c
+++ b/web/server/web_client.c
@@ -18,12 +18,28 @@ inline int web_client_permission_denied(struct web_client *w) {
return HTTP_RESP_FORBIDDEN;
}
-static inline int web_client_crock_socket(struct web_client *w __maybe_unused) {
+inline int web_client_bearer_required(struct web_client *w) {
+ w->response.data->content_type = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "An authorization bearer is required to access the resource.");
+ w->response.code = HTTP_RESP_UNAUTHORIZED;
+ return HTTP_RESP_UNAUTHORIZED;
+}
+
+static inline int bad_request_multiple_dashboard_versions(struct web_client *w) {
+ w->response.data->content_type = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "Multiple dashboard versions given at the URL.");
+ w->response.code = HTTP_RESP_BAD_REQUEST;
+ return HTTP_RESP_BAD_REQUEST;
+}
+
+static inline int web_client_cork_socket(struct web_client *w __maybe_unused) {
#ifdef TCP_CORK
if(likely(web_client_is_corkable(w) && !w->tcp_cork && w->ofd != -1)) {
w->tcp_cork = true;
if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
- error("%llu: failed to enable TCP_CORK on socket.", w->id);
+ netdata_log_error("%llu: failed to enable TCP_CORK on socket.", w->id);
w->tcp_cork = false;
return -1;
@@ -45,11 +61,12 @@ static inline void web_client_enable_wait_from_ssl(struct web_client *w) {
}
}
-static inline int web_client_uncrock_socket(struct web_client *w __maybe_unused) {
+static inline int web_client_uncork_socket(struct web_client *w __maybe_unused) {
#ifdef TCP_CORK
if(likely(w->tcp_cork && w->ofd != -1)) {
+ w->tcp_cork = false;
if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
- error("%llu: failed to disable TCP_CORK on socket.", w->id);
+ netdata_log_error("%llu: failed to disable TCP_CORK on socket.", w->id);
w->tcp_cork = true;
return -1;
}
@@ -140,12 +157,14 @@ static void web_client_reset_allocations(struct web_client *w, bool free_all) {
w->response.zinitialized = false;
w->flags &= ~WEB_CLIENT_CHUNKED_TRANSFER;
}
+
+ web_client_reset_path_flags(w);
}
void web_client_request_done(struct web_client *w) {
- web_client_uncrock_socket(w);
+ web_client_uncork_socket(w);
- debug(D_WEB_CLIENT, "%llu: Resetting client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Resetting client.", w->id);
if(likely(buffer_strlen(w->url_as_received))) {
struct timeval tv;
@@ -195,7 +214,7 @@ void web_client_request_done(struct web_client *w) {
}
// access log
- log_access("%llu: %d '[%s]:%s' '%s' (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %d '%s'",
+ netdata_log_access("%llu: %d '[%s]:%s' '%s' (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %d '%s'",
w->id
, gettid()
, w->client_ip
@@ -214,7 +233,7 @@ void web_client_request_done(struct web_client *w) {
if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) {
if(w->ifd != w->ofd) {
- debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
if (w->ifd != -1){
@@ -275,7 +294,7 @@ static struct {
};
static inline uint8_t contenttype_for_filename(const char *filename) {
- // info("checking filename '%s'", filename);
+ // netdata_log_info("checking filename '%s'", filename);
static int initialized = 0;
int i;
@@ -296,36 +315,172 @@ static inline uint8_t contenttype_for_filename(const char *filename) {
}
if(unlikely(!last_dot || !*last_dot || !last_dot[1])) {
- // info("no extension for filename '%s'", filename);
+ // netdata_log_info("no extension for filename '%s'", filename);
return CT_APPLICATION_OCTET_STREAM;
}
last_dot++;
- // info("extension for filename '%s' is '%s'", filename, last_dot);
+ // netdata_log_info("extension for filename '%s' is '%s'", filename, last_dot);
uint32_t hash = simple_hash(last_dot);
for(i = 0; mime_types[i].extension ; i++) {
if(unlikely(hash == mime_types[i].hash && !strcmp(last_dot, mime_types[i].extension))) {
- // info("matched extension for filename '%s': '%s'", filename, last_dot);
+ // netdata_log_info("matched extension for filename '%s': '%s'", filename, last_dot);
return mime_types[i].contenttype;
}
}
- // info("not matched extension for filename '%s': '%s'", filename, last_dot);
+ // netdata_log_info("not matched extension for filename '%s': '%s'", filename, last_dot);
return CT_APPLICATION_OCTET_STREAM;
}
-static inline int access_to_file_is_not_permitted(struct web_client *w, const char *filename) {
+static int append_slash_to_url_and_redirect(struct web_client *w) {
+ // this function returns a relative redirect
+ // it finds the last path component on the URL and just appends / to it
+ //
+ // So, if the URL is:
+ //
+ // /path/to/file?query_string
+ //
+ // It adds a Location header like this:
+ //
+ // Location: file/?query_string\r\n
+ //
+ // The web browser already knows that it is inside /path/to/
+ // so it converts the path to /path/to/file/ and executes the
+ // request again.
+
+ buffer_strcat(w->response.header, "Location: ");
+ const char *b = buffer_tostring(w->url_as_received);
+ const char *q = strchr(b, '?');
+ if(q && q > b) {
+ const char *e = q - 1;
+ while(e > b && *e != '/') e--;
+ if(*e == '/') e++;
+
+ size_t len = q - e;
+ buffer_strncat(w->response.header, e, len);
+ buffer_strncat(w->response.header, "/", 1);
+ buffer_strcat(w->response.header, q);
+ }
+ else {
+ const char *e = &b[buffer_strlen(w->url_as_received) - 1];
+ while(e > b && *e != '/') e--;
+ if(*e == '/') e++;
+
+ buffer_strcat(w->response.header, e);
+ buffer_strncat(w->response.header, "/", 1);
+ }
+
+ buffer_strncat(w->response.header, "\r\n", 2);
+
w->response.data->content_type = CT_TEXT_HTML;
- buffer_strcat(w->response.data, "Access to file is not permitted: ");
- buffer_strcat_htmlescape(w->response.data, filename);
- return HTTP_RESP_FORBIDDEN;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data,
+ "<!DOCTYPE html><html>"
+ "<body onload=\"window.location.href = window.location.origin + window.location.pathname + '/' + window.location.search + window.location.hash\">"
+ "Redirecting. In case your browser does not support redirection, please click "
+ "<a onclick=\"window.location.href = window.location.origin + window.location.pathname + '/' + window.location.search + window.location.hash\">here</a>."
+ "</body></html>");
+ return HTTP_RESP_MOVED_PERM;
}
// Work around a bug in the CMocka library by removing this function during testing.
#ifndef REMOVE_MYSENDFILE
-int mysendfile(struct web_client *w, char *filename) {
- debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
+
+static inline int dashboard_version(struct web_client *w) {
+ if(!web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
+ return -1;
+
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V0))
+ return 0;
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V1))
+ return 1;
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V2))
+ return 2;
+
+ return -1;
+}
+
+static bool find_filename_to_serve(const char *filename, char *dst, size_t dst_len, struct stat *statbuf, struct web_client *w, bool *is_dir) {
+ int d_version = dashboard_version(w);
+ bool has_extension = web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION);
+
+ int fallback = 0;
+
+ if(has_extension) {
+ if(d_version == -1)
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
+ else {
+ // check if the filename or directory exists
+ // fallback to the same path without the dashboard version otherwise
+ snprintfz(dst, dst_len, "%s/v%d/%s", netdata_configured_web_dir, d_version, filename);
+ fallback = 1;
+ }
+ }
+ else if(d_version != -1) {
+ if(filename && *filename) {
+ // check if the filename exists
+ // fallback to /vN/index.html otherwise
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
+ fallback = 2;
+ }
+ else {
+ if(filename && *filename)
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
+ snprintfz(dst, dst_len, "%s/v%d", netdata_configured_web_dir, d_version);
+ }
+ }
+ else {
+ // check if filename exists
+ // this is needed to serve {filename}/index.html, in case a user puts a html file into a directory
+ // fallback to /index.html otherwise
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
+ fallback = 3;
+ }
+
+ if (stat(dst, statbuf) != 0) {
+ if(fallback == 1) {
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
+ if (stat(dst, statbuf) != 0)
+ return false;
+ }
+ else if(fallback == 2) {
+ if(filename && *filename)
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
+ snprintfz(dst, dst_len, "%s/v%d", netdata_configured_web_dir, d_version);
+ if (stat(dst, statbuf) != 0)
+ return false;
+ }
+ else if(fallback == 3) {
+ if(filename && *filename)
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
+ snprintfz(dst, dst_len, "%s", netdata_configured_web_dir);
+ if (stat(dst, statbuf) != 0)
+ return false;
+ }
+ else
+ return false;
+ }
+
+ if((statbuf->st_mode & S_IFMT) == S_IFDIR) {
+ size_t len = strlen(dst);
+ if(len > dst_len - 11)
+ return false;
+
+ strncpyz(&dst[len], "/index.html", dst_len - len);
+
+ if (stat(dst, statbuf) != 0)
+ return false;
+
+ *is_dir = true;
+ }
+
+ return true;
+}
+
+static int mysendfile(struct web_client *w, char *filename) {
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
if(!web_client_can_access_dashboard(w))
return web_client_permission_denied(w);
@@ -337,7 +492,7 @@ int mysendfile(struct web_client *w, char *filename) {
char *s;
for(s = filename; *s ;s++) {
if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
w->response.data->content_type = CT_TEXT_HTML;
buffer_sprintf(w->response.data, "Filename contains invalid characters: ");
buffer_strcat_htmlescape(w->response.data, filename);
@@ -347,7 +502,7 @@ int mysendfile(struct web_client *w, char *filename) {
// if the filename contains a double dot refuse to serve it
if(strstr(filename, "..") != 0) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
w->response.data->content_type = CT_TEXT_HTML;
buffer_strcat(w->response.data, "Relative filenames are not supported: ");
buffer_strcat_htmlescape(w->response.data, filename);
@@ -355,60 +510,45 @@ int mysendfile(struct web_client *w, char *filename) {
}
// find the physical file on disk
- char webfilename[FILENAME_MAX + 1];
- snprintfz(webfilename, FILENAME_MAX, "%s/%s", netdata_configured_web_dir, filename);
-
+ bool is_dir = false;
+ char web_filename[FILENAME_MAX + 1];
struct stat statbuf;
- int done = 0;
- while(!done) {
- // check if the file exists
- if (lstat(webfilename, &statbuf) != 0) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
- w->response.data->content_type = CT_TEXT_HTML;
- buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
- buffer_strcat_htmlescape(w->response.data, webfilename);
- return HTTP_RESP_NOT_FOUND;
- }
-
- if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
- snprintfz(webfilename, FILENAME_MAX, "%s/%s/index.html", netdata_configured_web_dir, filename);
- continue;
- }
-
- if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
- error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
- return access_to_file_is_not_permitted(w, webfilename);
- }
-
- done = 1;
+ if(!find_filename_to_serve(filename, web_filename, FILENAME_MAX, &statbuf, w, &is_dir)) {
+ w->response.data->content_type = CT_TEXT_HTML;
+ buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
+ buffer_strcat_htmlescape(w->response.data, web_filename);
+ return HTTP_RESP_NOT_FOUND;
}
+ if(is_dir && !web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH))
+ return append_slash_to_url_and_redirect(w);
+
// open the file
- w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
+ w->ifd = open(web_filename, O_NONBLOCK, O_RDONLY);
if(w->ifd == -1) {
w->ifd = w->ofd;
if(errno == EBUSY || errno == EAGAIN) {
- error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
+ netdata_log_error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, web_filename);
w->response.data->content_type = CT_TEXT_HTML;
buffer_sprintf(w->response.header, "Location: /%s\r\n", filename);
buffer_strcat(w->response.data, "File is currently busy, please try again later: ");
- buffer_strcat_htmlescape(w->response.data, webfilename);
+ buffer_strcat_htmlescape(w->response.data, web_filename);
return HTTP_RESP_REDIR_TEMP;
}
else {
- error("%llu: Cannot open file '%s'.", w->id, webfilename);
+ netdata_log_error("%llu: Cannot open file '%s'.", w->id, web_filename);
w->response.data->content_type = CT_TEXT_HTML;
buffer_strcat(w->response.data, "Cannot open file: ");
- buffer_strcat_htmlescape(w->response.data, webfilename);
+ buffer_strcat_htmlescape(w->response.data, web_filename);
return HTTP_RESP_NOT_FOUND;
}
}
sock_setnonblock(w->ifd);
- w->response.data->content_type = contenttype_for_filename(webfilename);
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, webfilename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
+ w->response.data->content_type = contenttype_for_filename(web_filename);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, web_filename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
w->mode = WEB_CLIENT_MODE_FILECOPY;
web_client_enable_wait_receive(w);
@@ -429,12 +569,12 @@ int mysendfile(struct web_client *w, char *filename) {
void web_client_enable_deflate(struct web_client *w, int gzip) {
if(unlikely(w->response.zinitialized)) {
- debug(D_DEFLATE, "%llu: Compression has already be initialized for this client.", w->id);
+ netdata_log_debug(D_DEFLATE, "%llu: Compression has already be initialized for this client.", w->id);
return;
}
if(unlikely(w->response.sent)) {
- error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
+ netdata_log_error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
return;
}
@@ -455,13 +595,13 @@ void web_client_enable_deflate(struct web_client *w, int gzip) {
w->response.zstream.opaque = Z_NULL;
// if(deflateInit(&w->response.zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
-// error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
+// netdata_log_error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
// return;
// }
// Select GZIP compression: windowbits = 15 + 16 = 31
if(deflateInit2(&w->response.zstream, web_gzip_level, Z_DEFLATED, 15 + ((gzip)?16:0), 8, web_gzip_strategy) != Z_OK) {
- error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
+ netdata_log_error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
return;
}
@@ -470,7 +610,7 @@ void web_client_enable_deflate(struct web_client *w, int gzip) {
w->response.zinitialized = true;
w->flags |= WEB_CLIENT_CHUNKED_TRANSFER;
- debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
+ netdata_log_debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
}
void buffer_data_options2string(BUFFER *wb, uint32_t options) {
@@ -571,7 +711,7 @@ int web_client_api_request(RRDHOST *host, struct web_client *w, char *url_path_f
// get the api version
char *tok = strsep_skip_consecutive_separators(&url_path_fragment, "/");
if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
if(strcmp(tok, "v2") == 0)
return web_client_api_request_v2(host, w, url_path_fragment);
else if(strcmp(tok, "v1") == 0)
@@ -603,7 +743,7 @@ const char *web_content_type_to_string(HTTP_CONTENT_TYPE content_type) {
return "application/json; charset=utf-8";
case CT_APPLICATION_X_JAVASCRIPT:
- return "application/x-javascript; charset=utf-8";
+ return "application/javascript; charset=utf-8";
case CT_TEXT_CSS:
return "text/css; charset=utf-8";
@@ -656,35 +796,159 @@ const char *web_content_type_to_string(HTTP_CONTENT_TYPE content_type) {
case CT_PROMETHEUS:
return "text/plain; version=0.0.4";
+ case CT_AUDIO_MPEG:
+ return "audio/mpeg";
+
+ case CT_AUDIO_OGG:
+ return "audio/ogg";
+
+ case CT_VIDEO_MP4:
+ return "video/mp4";
+
+ case CT_APPLICATION_PDF:
+ return "application/pdf";
+
+ case CT_APPLICATION_ZIP:
+ return "application/zip";
+
default:
case CT_TEXT_PLAIN:
return "text/plain; charset=utf-8";
}
}
-
const char *web_response_code_to_string(int code) {
switch(code) {
- case HTTP_RESP_OK:
+ case 100:
+ return "Continue";
+ case 101:
+ return "Switching Protocols";
+ case 102:
+ return "Processing";
+ case 103:
+ return "Early Hints";
+
+ case 200:
return "OK";
-
- case HTTP_RESP_MOVED_PERM:
+ case 201:
+ return "Created";
+ case 202:
+ return "Accepted";
+ case 203:
+ return "Non-Authoritative Information";
+ case 204:
+ return "No Content";
+ case 205:
+ return "Reset Content";
+ case 206:
+ return "Partial Content";
+ case 207:
+ return "Multi-Status";
+ case 208:
+ return "Already Reported";
+ case 226:
+ return "IM Used";
+
+ case 300:
+ return "Multiple Choices";
+ case 301:
return "Moved Permanently";
-
- case HTTP_RESP_REDIR_TEMP:
+ case 302:
+ return "Found";
+ case 303:
+ return "See Other";
+ case 304:
+ return "Not Modified";
+ case 305:
+ return "Use Proxy";
+ case 306:
+ return "Switch Proxy";
+ case 307:
return "Temporary Redirect";
+ case 308:
+ return "Permanent Redirect";
- case HTTP_RESP_BAD_REQUEST:
+ case 400:
return "Bad Request";
-
- case HTTP_RESP_FORBIDDEN:
+ case 401:
+ return "Unauthorized";
+ case 402:
+ return "Payment Required";
+ case 403:
return "Forbidden";
-
- case HTTP_RESP_NOT_FOUND:
+ case 404:
return "Not Found";
-
- case HTTP_RESP_PRECOND_FAIL:
- return "Preconditions Failed";
+ case 405:
+ return "Method Not Allowed";
+ case 406:
+ return "Not Acceptable";
+ case 407:
+ return "Proxy Authentication Required";
+ case 408:
+ return "Request Timeout";
+ case 409:
+ return "Conflict";
+ case 410:
+ return "Gone";
+ case 411:
+ return "Length Required";
+ case 412:
+ return "Precondition Failed";
+ case 413:
+ return "Payload Too Large";
+ case 414:
+ return "URI Too Long";
+ case 415:
+ return "Unsupported Media Type";
+ case 416:
+ return "Range Not Satisfiable";
+ case 417:
+ return "Expectation Failed";
+ case 418:
+ return "I'm a teapot";
+ case 421:
+ return "Misdirected Request";
+ case 422:
+ return "Unprocessable Entity";
+ case 423:
+ return "Locked";
+ case 424:
+ return "Failed Dependency";
+ case 425:
+ return "Too Early";
+ case 426:
+ return "Upgrade Required";
+ case 428:
+ return "Precondition Required";
+ case 429:
+ return "Too Many Requests";
+ case 431:
+ return "Request Header Fields Too Large";
+ case 451:
+ return "Unavailable For Legal Reasons";
+
+ case 500:
+ return "Internal Server Error";
+ case 501:
+ return "Not Implemented";
+ case 502:
+ return "Bad Gateway";
+ case 503:
+ return "Service Unavailable";
+ case 504:
+ return "Gateway Timeout";
+ case 505:
+ return "HTTP Version Not Supported";
+ case 506:
+ return "Variant Also Negotiates";
+ case 507:
+ return "Insufficient Storage";
+ case 508:
+ return "Loop Detected";
+ case 510:
+ return "Not Extended";
+ case 511:
+ return "Network Authentication Required";
default:
if(code >= 100 && code < 200)
@@ -697,7 +961,7 @@ const char *web_response_code_to_string(int code) {
return "Redirection";
if(code >= 400 && code < 500)
- return "Bad Request";
+ return "Client Error";
if(code >= 500 && code < 600)
return "Server Error";
@@ -708,7 +972,7 @@ const char *web_response_code_to_string(int code) {
static inline char *http_header_parse(struct web_client *w, char *s, int parse_useragent) {
static uint32_t hash_origin = 0, hash_connection = 0, hash_donottrack = 0, hash_useragent = 0,
- hash_authorization = 0, hash_host = 0, hash_forwarded_proto = 0, hash_forwarded_host = 0;
+ hash_authorization = 0, hash_host = 0, hash_forwarded_host = 0;
static uint32_t hash_accept_encoding = 0;
if(unlikely(!hash_origin)) {
@@ -719,7 +983,6 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
hash_useragent = simple_uhash("User-Agent");
hash_authorization = simple_uhash("X-Auth-Token");
hash_host = simple_uhash("Host");
- hash_forwarded_proto = simple_uhash("X-Forwarded-Proto");
hash_forwarded_host = simple_uhash("X-Forwarded-Host");
}
@@ -783,10 +1046,6 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
// web_client_enable_deflate(w, 0);
}
}
- else if(hash == hash_forwarded_proto && !strcasecmp(s, "X-Forwarded-Proto")) {
- if(strcasestr(v, "https"))
- w->flags |= WEB_CLIENT_FLAG_PROXY_HTTPS;
- }
else if(hash == hash_forwarded_host && !strcasecmp(s, "X-Forwarded-Host")) {
char buffer[NI_MAXHOST];
strncpyz(buffer, v, ((size_t)(ve - v) < sizeof(buffer) - 1 ? (size_t)(ve - v) : sizeof(buffer) - 1));
@@ -850,7 +1109,7 @@ static inline char *web_client_valid_method(struct web_client *w, char *s) {
memcpy(hostname,"not available",13);
hostname[13] = 0x00;
}
- error("The server is configured to always use encrypted connections, please enable the SSL on child with hostname '%s'.",hostname);
+ netdata_log_error("The server is configured to always use encrypted connections, please enable the SSL on child with hostname '%s'.",hostname);
s = NULL;
}
#endif
@@ -891,7 +1150,7 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
is_it_valid = url_is_request_complete(s, &s[last_pos], w->header_parse_last_size, &w->post_payload, &w->post_payload_size);
if(!is_it_valid) {
if(w->header_parse_tries > HTTP_REQ_MAX_HEADER_FETCH_TRIES) {
- info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
+ netdata_log_info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
w->header_parse_tries = 0;
w->header_parse_last_size = 0;
web_client_disable_wait_receive(w);
@@ -1017,16 +1276,19 @@ void web_client_build_http_header(struct web_client *w) {
if(unlikely(w->response.code != HTTP_RESP_OK))
buffer_no_cacheable(w->response.data);
+ if(unlikely(!w->response.data->date))
+ w->response.data->date = now_realtime_sec();
+
// set a proper expiration date, if not already set
if(unlikely(!w->response.data->expires)) {
if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
- w->response.data->expires = w->timings.tv_ready.tv_sec + localhost->rrd_update_every;
+ w->response.data->expires = w->response.data->date + localhost->rrd_update_every;
else
- w->response.data->expires = w->timings.tv_ready.tv_sec + 86400;
+ w->response.data->expires = w->response.data->date + 86400;
}
// prepare the HTTP response header
- debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, w->response.code);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, w->response.code);
const char *content_type_string = web_content_type_to_string(w->response.data->content_type);
const char *code_msg = web_response_code_to_string(w->response.code);
@@ -1043,14 +1305,16 @@ void web_client_build_http_header(struct web_client *w) {
strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
}
- if (w->response.code == HTTP_RESP_MOVED_PERM) {
+ if (w->response.code == HTTP_RESP_HTTPS_UPGRADE) {
buffer_sprintf(w->response.header_output,
"HTTP/1.1 %d %s\r\n"
"Location: https://%s%s\r\n",
w->response.code, code_msg,
w->server_host ? w->server_host : "",
buffer_tostring(w->url_as_received));
- }else {
+ w->response.code = HTTP_RESP_MOVED_PERM;
+ }
+ else {
buffer_sprintf(w->response.header_output,
"HTTP/1.1 %d %s\r\n"
"Connection: %s\r\n"
@@ -1131,13 +1395,13 @@ static inline void web_client_send_http_header(struct web_client *w) {
web_client_build_http_header(w);
// sent the HTTP header
- debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
+ netdata_log_debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
, w->id
, buffer_strlen(w->response.header_output)
, buffer_tostring(w->response.header_output)
);
- web_client_crock_socket(w);
+ web_client_cork_socket(w);
size_t count = 0;
ssize_t bytes;
@@ -1152,7 +1416,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
count++;
if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
- error("Cannot send HTTP headers to web client.");
+ netdata_log_error("Cannot send HTTP headers to web client.");
break;
}
}
@@ -1163,7 +1427,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
count++;
if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
- error("Cannot send HTTP headers to web client.");
+ netdata_log_error("Cannot send HTTP headers to web client.");
break;
}
}
@@ -1173,7 +1437,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
count++;
if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
- error("Cannot send HTTP headers to web client.");
+ netdata_log_error("Cannot send HTTP headers to web client.");
break;
}
}
@@ -1184,8 +1448,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
w->statistics.sent_bytes += bytes;
if (bytes < 0) {
-
- error("HTTP headers failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
+ netdata_log_error("HTTP headers failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
, buffer_strlen(w->response.header_output)
, bytes);
@@ -1212,7 +1475,7 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
char *tok = strsep_skip_consecutive_separators(&url, "/");
if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
if(nodeid) {
host = find_host_by_node_id(tok);
@@ -1243,36 +1506,9 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
}
if (host) {
- if(!url) { //no delim found
- debug(D_WEB_CLIENT, "%llu: URL doesn't end with / generating redirect.", w->id);
- char *protocol, *url_host;
- protocol = (
-#ifdef ENABLE_HTTPS
- SSL_connection(&w->ssl) ||
-#endif
- (w->flags & WEB_CLIENT_FLAG_PROXY_HTTPS)) ? "https" : "http";
-
- url_host = w->forwarded_host;
- if(!url_host) {
- url_host = w->server_host;
- if(!url_host) url_host = "";
- }
-
- buffer_sprintf(w->response.header, "Location: %s://%s/%s/%s/%s",
- protocol, url_host, nodeid?"node":"host", tok, buffer_tostring(w->url_path_decoded));
-
- if(buffer_strlen(w->url_query_string_decoded)) {
- const char *query_string = buffer_tostring(w->url_query_string_decoded);
- if(*query_string) {
- if(*query_string != '?')
- buffer_fast_strcat(w->response.header, "?", 1);
- buffer_strcat(w->response.header, query_string);
- }
- }
- buffer_fast_strcat(w->response.header, "\r\n", 2);
- buffer_strcat(w->response.data, "Permanent redirect");
- return HTTP_RESP_REDIR_PERM;
- }
+ if(!url)
+ //no delim found
+ return append_slash_to_url_and_redirect(w);
size_t len = strlen(url) + 2;
char buf[len];
@@ -1311,12 +1547,12 @@ int web_client_api_request_with_node_selection(RRDHOST *host, struct web_client
if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) {
// current API
- debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
return check_host_and_call(host, w, decoded_url_path, web_client_api_request);
}
else if(unlikely((hash == hash_host && strcmp(tok, "host") == 0) || (hash == hash_node && strcmp(tok, "node") == 0))) {
// host switching
- debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
return web_client_switch_host(host, w, decoded_url_path, hash == hash_node, web_client_api_request_with_node_selection);
}
}
@@ -1335,7 +1571,10 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
hash_api = 0,
hash_netdata_conf = 0,
hash_host = 0,
- hash_node = 0;
+ hash_node = 0,
+ hash_v0 = 0,
+ hash_v1 = 0,
+ hash_v2 = 0;
#ifdef NETDATA_INTERNAL_CHECKS
static uint32_t hash_exit = 0, hash_debug = 0, hash_mirror = 0;
@@ -1346,6 +1585,9 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
hash_netdata_conf = simple_hash("netdata.conf");
hash_host = simple_hash("host");
hash_node = simple_hash("node");
+ hash_v0 = simple_hash("v0");
+ hash_v1 = simple_hash("v1");
+ hash_v2 = simple_hash("v2");
#ifdef NETDATA_INTERNAL_CHECKS
hash_exit = simple_hash("exit");
hash_debug = simple_hash("debug");
@@ -1355,26 +1597,44 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
// keep a copy of the decoded path, in case we need to serve it as a filename
char filename[FILENAME_MAX + 1];
- strncpyz(filename, buffer_tostring(w->url_path_decoded), FILENAME_MAX);
+ strncpyz(filename, decoded_url_path ? decoded_url_path : "", FILENAME_MAX);
char *tok = strsep_skip_consecutive_separators(&decoded_url_path, "/?");
if(likely(tok && *tok)) {
uint32_t hash = simple_hash(tok);
- debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
- if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) { // current API
- debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
+ if(likely(hash == hash_api && strcmp(tok, "api") == 0)) { // current API
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
return check_host_and_call(host, w, decoded_url_path, web_client_api_request);
}
else if(unlikely((hash == hash_host && strcmp(tok, "host") == 0) || (hash == hash_node && strcmp(tok, "node") == 0))) { // host switching
- debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
return web_client_switch_host(host, w, decoded_url_path, hash == hash_node, web_client_process_url);
}
+ else if(unlikely(hash == hash_v2 && strcmp(tok, "v2") == 0)) {
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
+ return bad_request_multiple_dashboard_versions(w);
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V2);
+ return web_client_process_url(host, w, decoded_url_path);
+ }
+ else if(unlikely(hash == hash_v1 && strcmp(tok, "v1") == 0)) {
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
+ return bad_request_multiple_dashboard_versions(w);
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V1);
+ return web_client_process_url(host, w, decoded_url_path);
+ }
+ else if(unlikely(hash == hash_v0 && strcmp(tok, "v0") == 0)) {
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
+ return bad_request_multiple_dashboard_versions(w);
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V0);
+ return web_client_process_url(host, w, decoded_url_path);
+ }
else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) { // netdata.conf
if(unlikely(!web_client_can_access_netdataconf(w)))
return web_client_permission_denied(w);
- debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
w->response.data->content_type = CT_TEXT_PLAIN;
buffer_flush(w->response.data);
config_generate(w->response.data, 0);
@@ -1393,7 +1653,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
else
buffer_strcat(w->response.data, "I am doing it already");
- error("web request to exit received.");
+ netdata_log_error("web request to exit received.");
netdata_cleanup_and_exit(0);
return HTTP_RESP_OK;
}
@@ -1406,7 +1666,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
// get the name of the data to show
tok = strsep_skip_consecutive_separators(&decoded_url_path, "&");
if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
// do we have such a data set?
RRDSET *st = rrdset_find_byname(host, tok);
@@ -1415,7 +1675,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
w->response.data->content_type = CT_TEXT_HTML;
buffer_strcat(w->response.data, "Chart is not found: ");
buffer_strcat_htmlescape(w->response.data, tok);
- debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
return HTTP_RESP_NOT_FOUND;
}
@@ -1429,7 +1689,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
w->response.data->content_type = CT_TEXT_HTML;
buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
buffer_strcat_htmlescape(w->response.data, tok);
- debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
return HTTP_RESP_OK;
}
@@ -1441,7 +1701,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
if(unlikely(!web_client_can_access_netdataconf(w)))
return web_client_permission_denied(w);
- debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
// replace the zero bytes with spaces
buffer_char_replace(w->response.data, '\0', ' ');
@@ -1507,7 +1767,33 @@ void web_client_process_request(struct web_client *w) {
break;
}
- w->response.code = web_client_process_url(localhost, w, (char *)buffer_tostring(w->url_path_decoded));
+ web_client_reset_path_flags(w);
+
+ // find if the URL path has a filename extension
+ char path[FILENAME_MAX + 1];
+ strncpyz(path, buffer_tostring(w->url_path_decoded), FILENAME_MAX);
+ char *s = path, *e = path;
+
+ // remove the query string and find the last char
+ for (; *e ; e++) {
+ if (*e == '?')
+ break;
+ }
+
+ if(e == s || (*(e - 1) == '/'))
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
+
+ // check if there is a filename extension
+ while (--e > s) {
+ if (*e == '/')
+ break;
+ if(*e == '.') {
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION);
+ break;
+ }
+ }
+
+ w->response.code = (short)web_client_process_url(localhost, w, path);
break;
}
break;
@@ -1517,7 +1803,7 @@ void web_client_process_request(struct web_client *w) {
buffer_flush(w->url_as_received);
buffer_strcat(w->url_as_received, "too big request");
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
size_t len = w->response.data->len;
buffer_flush(w->response.data);
@@ -1546,33 +1832,33 @@ void web_client_process_request(struct web_client *w) {
" click <a onclick=\"window.location.href ='https://'+ window.location.hostname + ':' "
" + window.location.port + window.location.pathname + window.location.search\">here</a>."
"</body></html>");
- w->response.code = HTTP_RESP_MOVED_PERM;
+ w->response.code = HTTP_RESP_HTTPS_UPGRADE;
break;
}
#endif
case HTTP_VALIDATION_MALFORMED_URL:
- debug(D_WEB_CLIENT_ACCESS, "%llu: Malformed URL '%s'.", w->id, w->response.data->buffer);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Malformed URL '%s'.", w->id, w->response.data->buffer);
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Malformed URL...\r\n");
w->response.code = HTTP_RESP_BAD_REQUEST;
break;
case HTTP_VALIDATION_EXCESS_REQUEST_DATA:
- debug(D_WEB_CLIENT_ACCESS, "%llu: Excess data in request '%s'.", w->id, w->response.data->buffer);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Excess data in request '%s'.", w->id, w->response.data->buffer);
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Excess data in request.\r\n");
w->response.code = HTTP_RESP_BAD_REQUEST;
break;
case HTTP_VALIDATION_TOO_MANY_READ_RETRIES:
- debug(D_WEB_CLIENT_ACCESS, "%llu: Too many retries to read request '%s'.", w->id, w->response.data->buffer);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Too many retries to read request '%s'.", w->id, w->response.data->buffer);
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Too many retries to read request.\r\n");
w->response.code = HTTP_RESP_BAD_REQUEST;
break;
case HTTP_VALIDATION_NOT_SUPPORTED:
- debug(D_WEB_CLIENT_ACCESS, "%llu: HTTP method requested is not supported '%s'.", w->id, w->response.data->buffer);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: HTTP method requested is not supported '%s'.", w->id, w->response.data->buffer);
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "HTTP method requested is not supported...\r\n");
@@ -1585,10 +1871,6 @@ void web_client_process_request(struct web_client *w) {
w->response.sent = 0;
- // set a proper last modified date
- if(unlikely(!w->response.data->date))
- w->response.data->date = w->timings.tv_ready.tv_sec;
-
web_client_send_http_header(w);
// enable sending immediately if we have data
@@ -1597,21 +1879,21 @@ void web_client_process_request(struct web_client *w) {
switch(w->mode) {
case WEB_CLIENT_MODE_STREAM:
- debug(D_WEB_CLIENT, "%llu: STREAM done.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: STREAM done.", w->id);
break;
case WEB_CLIENT_MODE_OPTIONS:
- debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
break;
case WEB_CLIENT_MODE_POST:
case WEB_CLIENT_MODE_GET:
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
break;
case WEB_CLIENT_MODE_FILECOPY:
if(w->response.rlen) {
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
web_client_enable_wait_receive(w);
/*
@@ -1621,14 +1903,14 @@ void web_client_process_request(struct web_client *w) {
{
long len = sendfile(w->ofd, w->ifd, NULL, w->response.data->rbytes);
if(len != w->response.data->rbytes)
- error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
+ netdata_log_error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
else
web_client_request_done(w);
}
*/
}
else
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
break;
default:
@@ -1639,7 +1921,7 @@ void web_client_process_request(struct web_client *w) {
ssize_t web_client_send_chunk_header(struct web_client *w, size_t len)
{
- debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
+ netdata_log_debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
char buf[24];
ssize_t bytes;
bytes = (ssize_t)sprintf(buf, "%zX\r\n", len);
@@ -1647,15 +1929,15 @@ ssize_t web_client_send_chunk_header(struct web_client *w, size_t len)
bytes = web_client_send_data(w,buf,strlen(buf),0);
if(bytes > 0) {
- debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
w->statistics.sent_bytes += bytes;
}
else if(bytes == 0) {
- debug(D_WEB_CLIENT, "%llu: Did not send chunk header to the client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk header to the client.", w->id);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send chunk header to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk header to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1669,15 +1951,15 @@ ssize_t web_client_send_chunk_close(struct web_client *w)
ssize_t bytes;
bytes = web_client_send_data(w,"\r\n",2,0);
if(bytes > 0) {
- debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
w->statistics.sent_bytes += bytes;
}
else if(bytes == 0) {
- debug(D_WEB_CLIENT, "%llu: Did not send chunk suffix to the client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk suffix to the client.", w->id);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send chunk suffix to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk suffix to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1691,15 +1973,15 @@ ssize_t web_client_send_chunk_finalize(struct web_client *w)
ssize_t bytes;
bytes = web_client_send_data(w,"\r\n0\r\n\r\n",7,0);
if(bytes > 0) {
- debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
w->statistics.sent_bytes += bytes;
}
else if(bytes == 0) {
- debug(D_WEB_CLIENT, "%llu: Did not send chunk finalize suffix to the client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk finalize suffix to the client.", w->id);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send chunk finalize suffix to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk finalize suffix to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1713,13 +1995,13 @@ ssize_t web_client_send_deflate(struct web_client *w)
// when using compression,
// w->response.sent is the amount of bytes passed through compression
- debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %zu, w->response.sent = %zu, w->response.zhave = %zu, w->response.zsent = %zu, w->response.zstream.avail_in = %u, w->response.zstream.avail_out = %u, w->response.zstream.total_in = %lu, w->response.zstream.total_out = %lu.",
+ netdata_log_debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %zu, w->response.sent = %zu, w->response.zhave = %zu, w->response.zsent = %zu, w->response.zstream.avail_in = %u, w->response.zstream.avail_out = %u, w->response.zstream.total_in = %lu, w->response.zstream.total_out = %lu.",
w->id, w->response.data->len, w->response.sent, w->response.zhave, w->response.zsent, w->response.zstream.avail_in, w->response.zstream.avail_out, w->response.zstream.total_in, w->response.zstream.total_out);
if(w->response.data->len - w->response.sent == 0 && w->response.zstream.avail_in == 0 && w->response.zhave == w->response.zsent && w->response.zstream.avail_out != 0) {
// there is nothing to send
- debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
// finalize the chunk
if(w->response.sent != 0) {
@@ -1729,20 +2011,20 @@ ssize_t web_client_send_deflate(struct web_client *w)
if(w->mode == WEB_CLIENT_MODE_FILECOPY && web_client_has_wait_receive(w) && w->response.rlen && w->response.rlen > w->response.data->len) {
// we have to wait, more data will come
- debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
web_client_disable_wait_send(w);
return t;
}
if(unlikely(!web_client_has_keepalive(w))) {
- debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
WEB_CLIENT_IS_DEAD(w);
return t;
}
// reset the client
web_client_request_done(w);
- debug(D_WEB_CLIENT, "%llu: Done sending all data on socket.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done sending all data on socket.", w->id);
return t;
}
@@ -1755,7 +2037,7 @@ ssize_t web_client_send_deflate(struct web_client *w)
if(t < 0) return t;
}
- debug(D_DEFLATE, "%llu: Compressing %zu new bytes starting from %zu (and %u left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
+ netdata_log_debug(D_DEFLATE, "%llu: Compressing %zu new bytes starting from %zu (and %u left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
// give the compressor all the data not passed through the compressor yet
if(w->response.data->len > w->response.sent) {
@@ -1772,15 +2054,15 @@ ssize_t web_client_send_deflate(struct web_client *w)
if((w->mode == WEB_CLIENT_MODE_GET || w->mode == WEB_CLIENT_MODE_POST)
|| (w->mode == WEB_CLIENT_MODE_FILECOPY && !web_client_has_wait_receive(w) && w->response.data->len == w->response.rlen)) {
flush = Z_FINISH;
- debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
+ netdata_log_debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
}
else {
- debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
+ netdata_log_debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
}
// compress
if(deflate(&w->response.zstream, flush) == Z_STREAM_ERROR) {
- error("%llu: Compression failed. Closing down client.", w->id);
+ netdata_log_error("%llu: Compression failed. Closing down client.", w->id);
web_client_request_done(w);
return(-1);
}
@@ -1791,30 +2073,30 @@ ssize_t web_client_send_deflate(struct web_client *w)
// keep track of the bytes passed through the compressor
w->response.sent = w->response.data->len;
- debug(D_DEFLATE, "%llu: Compression produced %zu bytes.", w->id, w->response.zhave);
+ netdata_log_debug(D_DEFLATE, "%llu: Compression produced %zu bytes.", w->id, w->response.zhave);
// open a new chunk
ssize_t t2 = web_client_send_chunk_header(w, w->response.zhave);
if(t2 < 0) return t2;
t += t2;
}
-
- debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
+
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
len = web_client_send_data(w,&w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
if(len > 0) {
w->statistics.sent_bytes += len;
w->response.zsent += len;
len += t;
- debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, len);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, len);
}
else if(len == 0) {
- debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %zu, zsent = %zu, need to send = %zu).",
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %zu, zsent = %zu, need to send = %zu).",
w->id, w->response.zhave, w->response.zsent, w->response.zhave - w->response.zsent);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1829,7 +2111,7 @@ ssize_t web_client_send(struct web_client *w) {
if(unlikely(w->response.data->len - w->response.sent == 0)) {
// there is nothing to send
- debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
// there can be two cases for this
// A. we have done everything
@@ -1837,19 +2119,19 @@ ssize_t web_client_send(struct web_client *w) {
if(w->mode == WEB_CLIENT_MODE_FILECOPY && web_client_has_wait_receive(w) && w->response.rlen && w->response.rlen > w->response.data->len) {
// we have to wait, more data will come
- debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
web_client_disable_wait_send(w);
return 0;
}
if(unlikely(!web_client_has_keepalive(w))) {
- debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
WEB_CLIENT_IS_DEAD(w);
return 0;
}
web_client_request_done(w);
- debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
return 0;
}
@@ -1857,13 +2139,13 @@ ssize_t web_client_send(struct web_client *w) {
if(likely(bytes > 0)) {
w->statistics.sent_bytes += bytes;
w->response.sent += bytes;
- debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, bytes);
}
else if(likely(bytes == 0)) {
- debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1887,8 +2169,8 @@ ssize_t web_client_read_file(struct web_client *w)
w->response.data->len += bytes;
w->response.data->buffer[w->response.data->len] = '\0';
- debug(D_WEB_CLIENT, "%llu: Read %zd bytes.", w->id, bytes);
- debug(D_WEB_DATA, "%llu: Read data: '%s'.", w->id, &w->response.data->buffer[old]);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Read %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_WEB_DATA, "%llu: Read data: '%s'.", w->id, &w->response.data->buffer[old]);
web_client_enable_wait_send(w);
@@ -1896,7 +2178,7 @@ ssize_t web_client_read_file(struct web_client *w)
web_client_disable_wait_receive(w);
}
else if(likely(bytes == 0)) {
- debug(D_WEB_CLIENT, "%llu: Out of input file data.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Out of input file data.", w->id);
// if we cannot read, it means we have an error on input.
// if however, we are copying a file from ifd to ofd, we should not return an error.
@@ -1906,7 +2188,7 @@ ssize_t web_client_read_file(struct web_client *w)
// let it finish copying...
web_client_disable_wait_receive(w);
- debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
if (w->ifd != w->ofd) close(w->ifd);
@@ -1915,7 +2197,7 @@ ssize_t web_client_read_file(struct web_client *w)
w->ifd = w->ofd;
}
else {
- debug(D_WEB_CLIENT, "%llu: read data failed.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: read data failed.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1961,23 +2243,22 @@ ssize_t web_client_receive(struct web_client *w)
w->response.data->len += bytes;
w->response.data->buffer[w->response.data->len] = '\0';
- debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
- debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
}
else if(unlikely(bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))) {
web_client_enable_wait_receive(w);
return 0;
}
else if (bytes < 0) {
- debug(D_WEB_CLIENT, "%llu: receive data failed.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: receive data failed.", w->id);
WEB_CLIENT_IS_DEAD(w);
} else
- debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
return(bytes);
}
-
void web_client_decode_path_and_query_string(struct web_client *w, const char *path_and_query_string) {
char buffer[NETDATA_WEB_REQUEST_URL_SIZE + 2];
buffer[0] = '\0';
@@ -1999,33 +2280,28 @@ void web_client_decode_path_and_query_string(struct web_client *w, const char *p
}
else {
// in non-stream mode, there is a path
-
// FIXME - the way this is implemented, query string params never accept the symbol &, not even encoded as %26
// To support the symbol & in query string params, we need to turn the url_query_string_decoded into a
// dictionary and decode each of the parameters individually.
// OR: in url_query_string_decoded use as separator a control character that cannot appear in the URL.
- char *question_mark_start = strchr(path_and_query_string, '?');
- if (question_mark_start)
- url_decode_r(buffer, question_mark_start, NETDATA_WEB_REQUEST_URL_SIZE + 1);
-
- buffer[NETDATA_WEB_REQUEST_URL_SIZE + 1] = '\0';
- buffer_strcat(w->url_query_string_decoded, buffer);
+ url_decode_r(buffer, path_and_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1);
+ char *question_mark_start = strchr(buffer, '?');
if (question_mark_start) {
+ buffer_strcat(w->url_query_string_decoded, question_mark_start);
char c = *question_mark_start;
*question_mark_start = '\0';
- url_decode_r(buffer, path_and_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1);
+ buffer_strcat(w->url_path_decoded, buffer);
*question_mark_start = c;
- } else
- url_decode_r(buffer, path_and_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1);
-
- buffer[NETDATA_WEB_REQUEST_URL_SIZE + 1] = '\0';
- buffer_strcat(w->url_path_decoded, buffer);
+ } else {
+ buffer_strcat(w->url_query_string_decoded, "");
+ buffer_strcat(w->url_path_decoded, buffer);
+ }
}
}
-void web_client_zero(struct web_client *w) {
+void web_client_reuse_from_cache(struct web_client *w) {
// zero everything about it - but keep the buffers
web_client_reset_allocations(w, false);
diff --git a/web/server/web_client.h b/web/server/web_client.h
index 4c2b06a70..68fcbfa31 100644
--- a/web/server/web_client.h
+++ b/web/server/web_client.h
@@ -33,29 +33,28 @@ typedef enum {
} HTTP_VALIDATION;
typedef enum web_client_flags {
- WEB_CLIENT_FLAG_DEAD = 1 << 1, // if set, this client is dead
-
- WEB_CLIENT_FLAG_KEEPALIVE = 1 << 2, // if set, the web client will be re-used
-
- WEB_CLIENT_FLAG_WAIT_RECEIVE = 1 << 3, // if set, we are waiting more input data
- WEB_CLIENT_FLAG_WAIT_SEND = 1 << 4, // if set, we have data to send to the client
-
- WEB_CLIENT_FLAG_DO_NOT_TRACK = 1 << 5, // if set, we should not set cookies on this client
- WEB_CLIENT_FLAG_TRACKING_REQUIRED = 1 << 6, // if set, we need to send cookies
-
- WEB_CLIENT_FLAG_TCP_CLIENT = 1 << 7, // if set, the client is using a TCP socket
- WEB_CLIENT_FLAG_UNIX_CLIENT = 1 << 8, // if set, the client is using a UNIX socket
-
- WEB_CLIENT_FLAG_DONT_CLOSE_SOCKET = 1 << 9, // don't close the socket when cleaning up (static-threaded web server)
-
- WEB_CLIENT_CHUNKED_TRANSFER = 1 << 10, // chunked transfer (used with zlib compression)
-
- WEB_CLIENT_FLAG_SSL_WAIT_RECEIVE = 1 << 11, // if set, we are waiting more input data from an ssl conn
- WEB_CLIENT_FLAG_SSL_WAIT_SEND = 1 << 12, // if set, we have data to send to the client from an ssl conn
-
- WEB_CLIENT_FLAG_PROXY_HTTPS = 1 << 13, // if set, the client reaches us via an https proxy
+ WEB_CLIENT_FLAG_DEAD = (1 << 1), // if set, this client is dead
+ WEB_CLIENT_FLAG_KEEPALIVE = (1 << 2), // if set, the web client will be re-used
+ WEB_CLIENT_FLAG_WAIT_RECEIVE = (1 << 3), // if set, we are waiting more input data
+ WEB_CLIENT_FLAG_WAIT_SEND = (1 << 4), // if set, we have data to send to the client
+ WEB_CLIENT_FLAG_DO_NOT_TRACK = (1 << 5), // if set, we should not set cookies on this client
+ WEB_CLIENT_FLAG_TRACKING_REQUIRED = (1 << 6), // if set, we need to send cookies
+ WEB_CLIENT_FLAG_TCP_CLIENT = (1 << 7), // if set, the client is using a TCP socket
+ WEB_CLIENT_FLAG_UNIX_CLIENT = (1 << 8), // if set, the client is using a UNIX socket
+ WEB_CLIENT_FLAG_DONT_CLOSE_SOCKET = (1 << 9), // don't close the socket when cleaning up (static-threaded web server)
+ WEB_CLIENT_CHUNKED_TRANSFER = (1 << 10), // chunked transfer (used with zlib compression)
+ WEB_CLIENT_FLAG_SSL_WAIT_RECEIVE = (1 << 11), // if set, we are waiting more input data from an ssl conn
+ WEB_CLIENT_FLAG_SSL_WAIT_SEND = (1 << 12), // if set, we have data to send to the client from an ssl conn
+ WEB_CLIENT_FLAG_PATH_IS_V0 = (1 << 13), // v0 dashboard found on the path
+ WEB_CLIENT_FLAG_PATH_IS_V1 = (1 << 14), // v1 dashboard found on the path
+ WEB_CLIENT_FLAG_PATH_IS_V2 = (1 << 15), // v2 dashboard found on the path
+ WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH = (1 << 16), // the path has a trailing hash
+ WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION = (1 << 17), // the path ends with a filename extension
} WEB_CLIENT_FLAGS;
+#define WEB_CLIENT_FLAG_PATH_WITH_VERSION (WEB_CLIENT_FLAG_PATH_IS_V0|WEB_CLIENT_FLAG_PATH_IS_V1|WEB_CLIENT_FLAG_PATH_IS_V2)
+#define web_client_reset_path_flags(w) (w)->flags &= ~(WEB_CLIENT_FLAG_PATH_WITH_VERSION|WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH|WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION)
+
#define web_client_flag_check(w, flag) ((w)->flags & (flag))
#define web_client_flag_set(w, flag) (w)->flags |= flag
#define web_client_flag_clear(w, flag) (w)->flags &= ~flag
@@ -200,6 +199,7 @@ struct web_client {
};
int web_client_permission_denied(struct web_client *w);
+int web_client_bearer_required(struct web_client *w);
ssize_t web_client_send(struct web_client *w);
ssize_t web_client_receive(struct web_client *w);
@@ -210,12 +210,10 @@ void web_client_request_done(struct web_client *w);
void buffer_data_options2string(BUFFER *wb, uint32_t options);
-int mysendfile(struct web_client *w, char *filename);
-
void web_client_build_http_header(struct web_client *w);
char *strip_control_characters(char *url);
-void web_client_zero(struct web_client *w);
+void web_client_reuse_from_cache(struct web_client *w);
struct web_client *web_client_create(size_t *statistics_memory_accounting);
void web_client_free(struct web_client *w);
diff --git a/web/server/web_client_cache.c b/web/server/web_client_cache.c
index 394bea32b..5aa3af22e 100644
--- a/web/server/web_client_cache.c
+++ b/web/server/web_client_cache.c
@@ -58,7 +58,7 @@ void web_client_cache_destroy(void) {
struct web_client *w, *t;
- netdata_spinlock_lock(&web_clients_cache.avail.spinlock);
+ spinlock_lock(&web_clients_cache.avail.spinlock);
w = web_clients_cache.avail.head;
while(w) {
t = w;
@@ -67,10 +67,10 @@ void web_client_cache_destroy(void) {
}
web_clients_cache.avail.head = NULL;
web_clients_cache.avail.count = 0;
- netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
+ spinlock_unlock(&web_clients_cache.avail.spinlock);
// DO NOT FREE THEM IF THEY ARE USED
-// netdata_spinlock_lock(&web_clients_cache.used.spinlock);
+// spinlock_lock(&web_clients_cache.used.spinlock);
// w = web_clients_cache.used.head;
// while(w) {
// t = w;
@@ -81,37 +81,37 @@ void web_client_cache_destroy(void) {
// web_clients_cache.used.count = 0;
// web_clients_cache.used.reused = 0;
// web_clients_cache.used.allocated = 0;
-// netdata_spinlock_unlock(&web_clients_cache.used.spinlock);
+// spinlock_unlock(&web_clients_cache.used.spinlock);
}
struct web_client *web_client_get_from_cache(void) {
- netdata_spinlock_lock(&web_clients_cache.avail.spinlock);
+ spinlock_lock(&web_clients_cache.avail.spinlock);
struct web_client *w = web_clients_cache.avail.head;
if(w) {
// get it from avail
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next);
web_clients_cache.avail.count--;
- netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
+ spinlock_unlock(&web_clients_cache.avail.spinlock);
- web_client_zero(w);
+ web_client_reuse_from_cache(w);
- netdata_spinlock_lock(&web_clients_cache.used.spinlock);
+ spinlock_lock(&web_clients_cache.used.spinlock);
web_clients_cache.used.reused++;
}
else {
- netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
+ spinlock_unlock(&web_clients_cache.avail.spinlock);
// allocate it
w = web_client_create(&netdata_buffers_statistics.buffers_web);
- netdata_spinlock_lock(&web_clients_cache.used.spinlock);
+ spinlock_lock(&web_clients_cache.used.spinlock);
web_clients_cache.used.allocated++;
}
// link it to used web clients
DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
web_clients_cache.used.count++;
- netdata_spinlock_unlock(&web_clients_cache.used.spinlock);
+ spinlock_unlock(&web_clients_cache.used.spinlock);
// initialize it
w->use_count++;
@@ -128,14 +128,14 @@ void web_client_release_to_cache(struct web_client *w) {
#endif
// unlink it from the used
- netdata_spinlock_lock(&web_clients_cache.used.spinlock);
+ spinlock_lock(&web_clients_cache.used.spinlock);
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
ssize_t used_count = (ssize_t)--web_clients_cache.used.count;
- netdata_spinlock_unlock(&web_clients_cache.used.spinlock);
+ spinlock_unlock(&web_clients_cache.used.spinlock);
- netdata_spinlock_lock(&web_clients_cache.avail.spinlock);
+ spinlock_lock(&web_clients_cache.avail.spinlock);
if(w->use_count > 100 || (used_count > 0 && web_clients_cache.avail.count >= 2 * (size_t)used_count) || (used_count <= 10 && web_clients_cache.avail.count >= 20)) {
- netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
+ spinlock_unlock(&web_clients_cache.avail.spinlock);
// we have too many of them - free it
web_client_free(w);
@@ -144,6 +144,6 @@ void web_client_release_to_cache(struct web_client *w) {
// link it to the avail
DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next);
web_clients_cache.avail.count++;
- netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
+ spinlock_unlock(&web_clients_cache.avail.spinlock);
}
}
diff --git a/web/server/web_server.c b/web/server/web_server.c
index e136f728c..b35fc252b 100644
--- a/web/server/web_server.c
+++ b/web/server/web_server.c
@@ -48,7 +48,7 @@ void debug_sockets() {
buffer_strcat(wb, (api_sockets.fds_acl_flags[i] & WEB_CLIENT_ACL_MGMT)?"management ":"");
buffer_strcat(wb, (api_sockets.fds_acl_flags[i] & WEB_CLIENT_ACL_STREAMING)?"streaming ":"");
buffer_strcat(wb, (api_sockets.fds_acl_flags[i] & WEB_CLIENT_ACL_NETDATACONF)?"netdata.conf ":"");
- debug(D_WEB_CLIENT, "Socket fd %d name '%s' acl_flags: %s",
+ netdata_log_debug(D_WEB_CLIENT, "Socket fd %d name '%s' acl_flags: %s",
i,
api_sockets.fds_names[i],
buffer_tostring(wb));
@@ -130,5 +130,5 @@ void web_client_update_acl_matches(struct web_client *w) {
// --------------------------------------------------------------------------------------
void web_server_log_connection(struct web_client *w, const char *msg) {
- log_access("%llu: %d '[%s]:%s' '%s'", w->id, gettid(), w->client_ip, w->client_port, msg);
+ netdata_log_access("%llu: %d '[%s]:%s' '%s'", w->id, gettid(), w->client_ip, w->client_port, msg);
}