]> arthur.barton.de Git - netdata.git/commitdiff
added rrd memory mode "none" to allow netdata act as a proxy to forward metrics to...
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Thu, 23 Feb 2017 20:39:57 +0000 (22:39 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Thu, 23 Feb 2017 20:39:57 +0000 (22:39 +0200)
14 files changed:
CMakeLists.txt
src/Makefile.am
src/common.h
src/main.c
src/rrd.c
src/rrd.h
src/rrddim.c
src/rrdset.c
src/web_api_old.c [new file with mode: 0644]
src/web_api_old.h [new file with mode: 0644]
src/web_api_v1.c [new file with mode: 0644]
src/web_api_v1.h [new file with mode: 0644]
src/web_client.c
src/web_client.h

index d5d89ac45eb07f1c731bcbe2297a250abc555e25..979144e06ed3ad8c17fd6f9c1031c5e46bbe30cd 100755 (executable)
@@ -124,7 +124,7 @@ set(NETDATA_SOURCE_FILES
         src/web_client.h
         src/web_server.c
         src/web_server.h
-        src/rrdhost.c src/rrdfamily.c src/rrdset.c src/rrddim.c src/health_log.c src/health_config.c src/health_json.c src/rrdcalc.c src/rrdcalctemplate.c src/rrdvar.c src/rrddimvar.c src/rrdsetvar.c src/rrdpush.c src/rrdpush.h)
+        src/rrdhost.c src/rrdfamily.c src/rrdset.c src/rrddim.c src/health_log.c src/health_config.c src/health_json.c src/rrdcalc.c src/rrdcalctemplate.c src/rrdvar.c src/rrddimvar.c src/rrdsetvar.c src/rrdpush.c src/rrdpush.h src/web_api_old.c src/web_api_old.h src/web_api_v1.c src/web_api_v1.h)
 
 set(APPS_PLUGIN_SOURCE_FILES
         src/appconfig.c
index a0cd3532a5a2835a5524c737e60b3ba704ddb42c..fb9240b48d07bff14766c4cc9afed4dcc74633a5 100644 (file)
@@ -79,6 +79,8 @@ netdata_SOURCES = \
        storage_number.c storage_number.h \
        unit_test.c unit_test.h \
        url.c url.h \
+       web_api_old.c web_api_old.h \
+       web_api_v1.c web_api_v1.h \
        web_buffer.c web_buffer.h \
        web_buffer_svg.c web_buffer_svg.h \
        web_client.c web_client.h \
index 8692e5edb5bdd035933a3a67c6f3001ebf723de1..e2c0a520cb5a706fe3e98dd69680a3bf97abd313 100644 (file)
 #include "inlined.h"
 #include "adaptive_resortable_list.h"
 #include "rrdpush.h"
+#include "web_api_v1.h"
+#include "web_api_old.h"
 
 extern char *netdata_configured_config_dir;
 extern char *netdata_configured_log_dir;
index e0089dbbe0c45fe8865e0e5f2ddeffef2b8be939..d8381adba604868a8ec2fcdbb9dc79463d3a27b3 100644 (file)
@@ -703,7 +703,7 @@ int main(int argc, char **argv) {
         // get default memory mode for the database
 
         if(rrdpush_exclusive) {
-            default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+            default_rrd_memory_mode = RRD_MEMORY_MODE_NONE;
             config_set("global", "memory mode", rrd_memory_mode_name(default_rrd_memory_mode));
         }
         else
index 63958e4e3032eda51b91e92f1d1c29694c11d36c..54e6b862b69436a0c33aa7464296d962e53a4350 100644 (file)
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -27,6 +27,9 @@ inline const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) {
         case RRD_MEMORY_MODE_MAP:
             return RRD_MEMORY_MODE_MAP_NAME;
 
+        case RRD_MEMORY_MODE_NONE:
+            return RRD_MEMORY_MODE_NONE_NAME;
+
         case RRD_MEMORY_MODE_SAVE:
         default:
             return RRD_MEMORY_MODE_SAVE_NAME;
@@ -38,6 +41,8 @@ RRD_MEMORY_MODE rrd_memory_mode_id(const char *name) {
         return RRD_MEMORY_MODE_RAM;
     else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
         return RRD_MEMORY_MODE_MAP;
+    else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_NONE_NAME)))
+        return RRD_MEMORY_MODE_NONE;
 
     return RRD_MEMORY_MODE_SAVE;
 }
index 7840da865d0c8f02f26512fef5e6e5253d6fd85c..46ee8e00f9cb1ac8b2b00524f9c4525b2be398f2 100644 (file)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -39,11 +39,13 @@ const char *rrdset_type_name(RRDSET_TYPE chart_type);
 // memory mode
 
 typedef enum rrd_memory_mode {
-    RRD_MEMORY_MODE_RAM  = 0,
-    RRD_MEMORY_MODE_MAP  = 1,
-    RRD_MEMORY_MODE_SAVE = 2
+    RRD_MEMORY_MODE_NONE = 0,
+    RRD_MEMORY_MODE_RAM  = 1,
+    RRD_MEMORY_MODE_MAP  = 2,
+    RRD_MEMORY_MODE_SAVE = 3
 } RRD_MEMORY_MODE;
 
+#define RRD_MEMORY_MODE_NONE_NAME "none"
 #define RRD_MEMORY_MODE_RAM_NAME "ram"
 #define RRD_MEMORY_MODE_MAP_NAME "map"
 #define RRD_MEMORY_MODE_SAVE_NAME "save"
index 70ef3742e5640665ffbf4c9d5f7685a3449c6732..287248a7dc025dc1eab85a4bb7a3afd4da4cb7e9 100644 (file)
@@ -71,7 +71,7 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe
     rrdset_strncpyz_name(filename, id, FILENAME_MAX);
     snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
 
-    if(st->rrd_memory_mode != RRD_MEMORY_MODE_RAM) {
+    if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
         rd = (RRDDIM *)mymmap(fullfilename, size, ((st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 1);
         if(likely(rd)) {
             // we have a file mapped for rd
@@ -132,7 +132,7 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe
     if(unlikely(!rd)) {
         // if we didn't manage to get a mmap'd dimension, just create one
         rd = callocz(1, size);
-        rd->rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+        rd->rrd_memory_mode = (st->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
     }
 
     rd->memsize = size;
@@ -243,6 +243,7 @@ void rrddim_free(RRDSET *st, RRDDIM *rd)
             munmap(rd, rd->memsize);
             break;
 
+        case RRD_MEMORY_MODE_NONE:
         case RRD_MEMORY_MODE_RAM:
             debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
             freez((void *)rd->id);
index d708be99bc5729f27d1c05d87eea3ea851f74f07..d16bb259a69a29f137e72212f643ffe3cc77efce 100644 (file)
@@ -325,6 +325,9 @@ RRDSET *rrdset_create(RRDHOST *host, const char *type, const char *id, const cha
     long entries = align_entries_to_pagesize(rentries);
     if(entries != rentries) entries = config_set_number(config_section, "history", entries);
 
+    if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE && entries != rentries)
+        entries = config_set_number(config_section, "history", 10);
+
     int enabled = config_get_boolean(config_section, "enabled", 1);
     if(!enabled) entries = 5;
 
@@ -339,7 +342,7 @@ RRDSET *rrdset_create(RRDHOST *host, const char *type, const char *id, const cha
     debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
 
     snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
-    if(host->rrd_memory_mode != RRD_MEMORY_MODE_RAM) {
+    if(host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
         st = (RRDSET *) mymmap(fullfilename, size, ((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 0);
         if(st) {
             memset(&st->avl, 0, sizeof(avl));
@@ -405,7 +408,7 @@ RRDSET *rrdset_create(RRDHOST *host, const char *type, const char *id, const cha
 
     if(unlikely(!st)) {
         st = callocz(1, size);
-        st->rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+        st->rrd_memory_mode = (host->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
     }
 
     st->config_section = strdup(config_section);
diff --git a/src/web_api_old.c b/src/web_api_old.c
new file mode 100644 (file)
index 0000000..127536e
--- /dev/null
@@ -0,0 +1,231 @@
+#include "common.h"
+
+int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type) {
+    if(!url || !*url) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Incomplete request.");
+        return 400;
+    }
+
+    RRDSET *st = NULL;
+
+    char *args = strchr(url, '?');
+    if(args) {
+        *args='\0';
+        args = &args[1];
+    }
+
+    // get the name of the data to show
+    char *tok = mystrsep(&url, "/");
+    if(!tok) tok = "";
+
+    // do we have such a data set?
+    if(*tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+        st = rrdset_find_byname(host, tok);
+        if(!st) st = rrdset_find(host, tok);
+    }
+
+    if(!st) {
+        // we don't have it
+        // try to send a file with that name
+        buffer_flush(w->response.data);
+        return(mysendfile(w, tok));
+    }
+
+    // we have it
+    debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
+
+    // how many entries does the client want?
+    int lines = (int)st->entries;
+    int group_count = 1;
+    time_t after = 0, before = 0;
+    int group_method = GROUP_AVERAGE;
+    int nonzero = 0;
+
+    if(url) {
+        // parse the lines required
+        tok = mystrsep(&url, "/");
+        if(tok) lines = str2i(tok);
+        if(lines < 1) lines = 1;
+    }
+    if(url) {
+        // parse the group count required
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) group_count = str2i(tok);
+        if(group_count < 1) group_count = 1;
+        //if(group_count > save_history / 20) group_count = save_history / 20;
+    }
+    if(url) {
+        // parse the grouping method required
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) {
+            if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
+            else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
+            else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
+            else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
+        }
+    }
+    if(url) {
+        // parse after time
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) after = str2ul(tok);
+        if(after < 0) after = 0;
+    }
+    if(url) {
+        // parse before time
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) before = str2ul(tok);
+        if(before < 0) before = 0;
+    }
+    if(url) {
+        // parse nonzero
+        tok = mystrsep(&url, "/");
+        if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
+    }
+
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    buffer_flush(w->response.data);
+
+    char *google_version = "0.6";
+    char *google_reqId = "0";
+    char *google_sig = "0";
+    char *google_out = "json";
+    char *google_responseHandler = "google.visualization.Query.setResponse";
+    char *google_outFileName = NULL;
+    time_t last_timestamp_in_data = 0;
+    if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
+
+        w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+
+        while(args) {
+            tok = mystrsep(&args, "&");
+            if(tok && *tok) {
+                char *name = mystrsep(&tok, "=");
+                if(name && *name && strcmp(name, "tqx") == 0) {
+                    char *key = mystrsep(&tok, ":");
+                    char *value = mystrsep(&tok, ";");
+                    if(key && value && *key && *value) {
+                        if(strcmp(key, "version") == 0)
+                            google_version = value;
+
+                        else if(strcmp(key, "reqId") == 0)
+                            google_reqId = value;
+
+                        else if(strcmp(key, "sig") == 0)
+                            google_sig = value;
+
+                        else if(strcmp(key, "out") == 0)
+                            google_out = value;
+
+                        else if(strcmp(key, "responseHandler") == 0)
+                            google_responseHandler = value;
+
+                        else if(strcmp(key, "outFileName") == 0)
+                            google_outFileName = value;
+                    }
+                }
+            }
+        }
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
+                w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
+        );
+
+        if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+            last_timestamp_in_data = strtoul(google_sig, NULL, 0);
+
+            // check the client wants json
+            if(strcmp(google_out, "json") != 0) {
+                buffer_sprintf(w->response.data,
+                        "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
+                        google_responseHandler, google_version, google_reqId, google_out);
+                return 200;
+            }
+        }
+    }
+
+    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+        buffer_sprintf(w->response.data,
+                "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
+                google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
+    }
+
+    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
+            w->id, st->name, st->id, lines, group_count, group_method, after, before);
+
+    time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
+
+    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+        if(timestamp_in_data > last_timestamp_in_data)
+            buffer_strcat(w->response.data, "});");
+
+        else {
+            // the client already has the latest data
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data,
+                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
+                    google_responseHandler, google_version, google_reqId);
+        }
+    }
+
+    return 200;
+}
+
+inline int web_client_api_old_data_request_json(RRDHOST *host, struct web_client *w, char *url) {
+    return web_client_api_old_data_request(host, w, url, DATASOURCE_JSON);
+}
+
+inline int web_client_api_old_data_request_jsonp(RRDHOST *host, struct web_client *w, char *url) {
+    return web_client_api_old_data_request(host, w, url, DATASOURCE_DATATABLE_JSONP);
+}
+
+inline int web_client_api_old_graph_request(RRDHOST *host, struct web_client *w, char *url) {
+    // get the name of the data to show
+    char *tok = mystrsep(&url, "/?&");
+    if(tok && *tok) {
+        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);
+        if(!st) st = rrdset_find(host, tok);
+        if(!st) {
+            // we don't have it
+            // try to send a file with that name
+            buffer_flush(w->response.data);
+            return mysendfile(w, tok);
+        }
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
+        w->response.data->contenttype = CT_APPLICATION_JSON;
+        buffer_flush(w->response.data);
+        rrd_stats_graph_json(st, url, w->response.data);
+        return 200;
+    }
+
+    buffer_flush(w->response.data);
+    buffer_strcat(w->response.data, "Graph name?\r\n");
+    return 400;
+}
+
+inline int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url) {
+    (void)url;
+
+    buffer_flush(w->response.data);
+    RRDSET *st;
+
+    rrdhost_rdlock(host);
+    rrdset_foreach_read(st, host) buffer_sprintf(w->response.data, "%s\n", st->name);
+    rrdhost_unlock(host);
+
+    return 200;
+}
+
+inline int web_client_api_old_all_json(RRDHOST *host, struct web_client *w, char *url) {
+    (void)url;
+
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    buffer_flush(w->response.data);
+    rrd_stats_all_json(host, w->response.data);
+    return 200;
+}
diff --git a/src/web_api_old.h b/src/web_api_old.h
new file mode 100644 (file)
index 0000000..dff48c2
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef NETDATA_WEB_API_OLD_H
+#define NETDATA_WEB_API_OLD_H
+
+#include "common.h"
+
+extern int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type);
+extern int web_client_api_old_data_request_json(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_old_data_request_jsonp(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_old_graph_request(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_old_all_json(RRDHOST *host, struct web_client *w, char *url);
+
+#endif //NETDATA_WEB_API_OLD_H
diff --git a/src/web_api_v1.c b/src/web_api_v1.c
new file mode 100644 (file)
index 0000000..c180cf3
--- /dev/null
@@ -0,0 +1,902 @@
+#include "common.h"
+
+inline int web_client_api_request_v1_data_group(char *name, int def) {
+    if(!strcmp(name, "average"))
+        return GROUP_AVERAGE;
+
+    else if(!strcmp(name, "min"))
+        return GROUP_MIN;
+
+    else if(!strcmp(name, "max"))
+        return GROUP_MAX;
+
+    else if(!strcmp(name, "sum"))
+        return GROUP_SUM;
+
+    else if(!strcmp(name, "incremental-sum"))
+        return GROUP_INCREMENTAL_SUM;
+
+    return def;
+}
+
+inline uint32_t web_client_api_request_v1_data_options(char *o) {
+    uint32_t ret = 0x00000000;
+    char *tok;
+
+    while(o && *o && (tok = mystrsep(&o, ", |"))) {
+        if(!*tok) continue;
+
+        if(!strcmp(tok, "nonzero"))
+            ret |= RRDR_OPTION_NONZERO;
+        else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
+            ret |= RRDR_OPTION_REVERSED;
+        else if(!strcmp(tok, "jsonwrap"))
+            ret |= RRDR_OPTION_JSON_WRAP;
+        else if(!strcmp(tok, "min2max"))
+            ret |= RRDR_OPTION_MIN2MAX;
+        else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
+            ret |= RRDR_OPTION_MILLISECONDS;
+        else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
+            ret |= RRDR_OPTION_ABSOLUTE;
+        else if(!strcmp(tok, "seconds"))
+            ret |= RRDR_OPTION_SECONDS;
+        else if(!strcmp(tok, "null2zero"))
+            ret |= RRDR_OPTION_NULL2ZERO;
+        else if(!strcmp(tok, "objectrows"))
+            ret |= RRDR_OPTION_OBJECTSROWS;
+        else if(!strcmp(tok, "google_json"))
+            ret |= RRDR_OPTION_GOOGLE_JSON;
+        else if(!strcmp(tok, "percentage"))
+            ret |= RRDR_OPTION_PERCENTAGE;
+        else if(!strcmp(tok, "unaligned"))
+            ret |= RRDR_OPTION_NOT_ALIGNED;
+    }
+
+    return ret;
+}
+
+inline uint32_t web_client_api_request_v1_data_format(char *name) {
+    if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
+        return DATASOURCE_DATATABLE_JSON;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
+        return DATASOURCE_DATATABLE_JSONP;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
+        return DATASOURCE_JSON;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
+        return DATASOURCE_JSONP;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
+        return DATASOURCE_SSV;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
+        return DATASOURCE_CSV;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
+        return DATASOURCE_TSV;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
+        return DATASOURCE_HTML;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
+        return DATASOURCE_JS_ARRAY;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
+        return DATASOURCE_SSV_COMMA;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
+        return DATASOURCE_CSV_JSON_ARRAY;
+
+    return DATASOURCE_JSON;
+}
+
+inline uint32_t web_client_api_request_v1_data_google_format(char *name) {
+    if(!strcmp(name, "json"))
+        return DATASOURCE_DATATABLE_JSONP;
+
+    else if(!strcmp(name, "html"))
+        return DATASOURCE_HTML;
+
+    else if(!strcmp(name, "csv"))
+        return DATASOURCE_CSV;
+
+    else if(!strcmp(name, "tsv-excel"))
+        return DATASOURCE_TSV;
+
+    return DATASOURCE_JSON;
+}
+
+
+inline int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url) {
+    int all = 0;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if (!value || !*value) continue;
+
+        if(!strcmp(value, "all")) all = 1;
+        else if(!strcmp(value, "active")) all = 0;
+    }
+
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    health_alarms2json(host, w->response.data, all);
+    return 200;
+}
+
+inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url) {
+    uint32_t after = 0;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if (!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        if(!strcmp(name, "after")) after = (uint32_t)strtoul(value, NULL, 0);
+    }
+
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    health_alarm_log2json(host, w->response.data, after);
+    return 200;
+}
+
+inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) {
+    int ret = 400;
+    char *chart = NULL;
+
+    buffer_flush(w->response.data);
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        // name and value are now the parameters
+        // they are not null and not empty
+
+        if(!strcmp(name, "chart")) chart = value;
+        //else {
+        /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
+        //  goto cleanup;
+        //}
+    }
+
+    if(!chart || !*chart) {
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(host, chart);
+    if(!st) st = rrdset_find_byname(host, chart);
+    if(!st) {
+        buffer_strcat(w->response.data, "Chart is not found: ");
+        buffer_strcat_htmlescape(w->response.data, chart);
+        ret = 404;
+        goto cleanup;
+    }
+
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    callback(st, w->response.data);
+    return 200;
+
+    cleanup:
+    return ret;
+}
+
+inline int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url) {
+    return web_client_api_request_single_chart(host, w, url, health_api_v1_chart_variables2json);
+}
+
+inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url) {
+    (void)url;
+
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    rrd_stats_api_v1_charts(host, w->response.data);
+    return 200;
+}
+
+inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) {
+    int format = ALLMETRICS_SHELL;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if (!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        if(!strcmp(name, "format")) {
+            if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
+                format = ALLMETRICS_SHELL;
+            else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
+                format = ALLMETRICS_PROMETHEUS;
+            else
+                format = 0;
+        }
+    }
+
+    buffer_flush(w->response.data);
+    buffer_no_cacheable(w->response.data);
+
+    switch(format) {
+        case ALLMETRICS_SHELL:
+            w->response.data->contenttype = CT_TEXT_PLAIN;
+            rrd_stats_api_v1_charts_allmetrics_shell(host, w->response.data);
+            return 200;
+
+        case ALLMETRICS_PROMETHEUS:
+            w->response.data->contenttype = CT_PROMETHEUS;
+            rrd_stats_api_v1_charts_allmetrics_prometheus(host, w->response.data);
+            return 200;
+
+        default:
+            w->response.data->contenttype = CT_TEXT_PLAIN;
+            buffer_strcat(w->response.data, "Which format? Only '" ALLMETRICS_FORMAT_SHELL "' and '" ALLMETRICS_FORMAT_PROMETHEUS "' is currently supported.");
+            return 400;
+    }
+}
+
+inline int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url) {
+    return web_client_api_request_single_chart(host, w, url, rrd_stats_api_v1_chart);
+}
+
+int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
+    int ret = 400;
+    buffer_flush(w->response.data);
+
+    BUFFER *dimensions = NULL;
+
+    const char *chart = NULL
+    , *before_str = NULL
+    , *after_str = NULL
+    , *points_str = NULL
+    , *multiply_str = NULL
+    , *divide_str = NULL
+    , *label = NULL
+    , *units = NULL
+    , *label_color = NULL
+    , *value_color = NULL
+    , *refresh_str = NULL
+    , *precision_str = NULL
+    , *alarm = NULL;
+
+    int group = GROUP_AVERAGE;
+    uint32_t options = 0x00000000;
+
+    while(url) {
+        char *value = mystrsep(&url, "/?&");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
+
+        // name and value are now the parameters
+        // they are not null and not empty
+
+        if(!strcmp(name, "chart")) chart = value;
+        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
+            if(!dimensions)
+                dimensions = buffer_create(100);
+
+            buffer_strcat(dimensions, "|");
+            buffer_strcat(dimensions, value);
+        }
+        else if(!strcmp(name, "after")) after_str = value;
+        else if(!strcmp(name, "before")) before_str = value;
+        else if(!strcmp(name, "points")) points_str = value;
+        else if(!strcmp(name, "group")) {
+            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
+        }
+        else if(!strcmp(name, "options")) {
+            options |= web_client_api_request_v1_data_options(value);
+        }
+        else if(!strcmp(name, "label")) label = value;
+        else if(!strcmp(name, "units")) units = value;
+        else if(!strcmp(name, "label_color")) label_color = value;
+        else if(!strcmp(name, "value_color")) value_color = value;
+        else if(!strcmp(name, "multiply")) multiply_str = value;
+        else if(!strcmp(name, "divide")) divide_str = value;
+        else if(!strcmp(name, "refresh")) refresh_str = value;
+        else if(!strcmp(name, "precision")) precision_str = value;
+        else if(!strcmp(name, "alarm")) alarm = value;
+    }
+
+    if(!chart || !*chart) {
+        buffer_no_cacheable(w->response.data);
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(host, chart);
+    if(!st) st = rrdset_find_byname(host, chart);
+    if(!st) {
+        buffer_no_cacheable(w->response.data);
+        buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1);
+        ret = 200;
+        goto cleanup;
+    }
+
+    RRDCALC *rc = NULL;
+    if(alarm) {
+        rc = rrdcalc_find(st, alarm);
+        if (!rc) {
+            buffer_no_cacheable(w->response.data);
+            buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1);
+            ret = 200;
+            goto cleanup;
+        }
+    }
+
+    long long multiply  = (multiply_str  && *multiply_str )?str2l(multiply_str):1;
+    long long divide    = (divide_str    && *divide_str   )?str2l(divide_str):1;
+    long long before    = (before_str    && *before_str   )?str2l(before_str):0;
+    long long after     = (after_str     && *after_str    )?str2l(after_str):-st->update_every;
+    int       points    = (points_str    && *points_str   )?str2i(points_str):1;
+    int       precision = (precision_str && *precision_str)?str2i(precision_str):-1;
+
+    if(!multiply) multiply = 1;
+    if(!divide) divide = 1;
+
+    int refresh = 0;
+    if(refresh_str && *refresh_str) {
+        if(!strcmp(refresh_str, "auto")) {
+            if(rc) refresh = rc->update_every;
+            else if(options & RRDR_OPTION_NOT_ALIGNED)
+                refresh = st->update_every;
+            else {
+                refresh = (int)(before - after);
+                if(refresh < 0) refresh = -refresh;
+            }
+        }
+        else {
+            refresh = str2i(refresh_str);
+            if(refresh < 0) refresh = -refresh;
+        }
+    }
+
+    if(!label) {
+        if(alarm) {
+            char *s = (char *)alarm;
+            while(*s) {
+                if(*s == '_') *s = ' ';
+                s++;
+            }
+            label = alarm;
+        }
+        else if(dimensions) {
+            const char *dim = buffer_tostring(dimensions);
+            if(*dim == '|') dim++;
+            label = dim;
+        }
+        else
+            label = st->name;
+    }
+    if(!units) {
+        if(alarm) {
+            if(rc->units)
+                units = rc->units;
+            else
+                units = "";
+        }
+        else if(options & RRDR_OPTION_PERCENTAGE)
+            units = "%";
+        else
+            units = st->units;
+    }
+
+    debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
+          , w->id
+          , chart
+          , alarm?alarm:""
+          , (dimensions)?buffer_tostring(dimensions):""
+          , after
+          , before
+          , points
+          , group
+          , options
+    );
+
+    if(rc) {
+        if (refresh > 0) {
+            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+            w->response.data->expires = now_realtime_sec() + refresh;
+        }
+        else buffer_no_cacheable(w->response.data);
+
+        if(!value_color) {
+            switch(rc->status) {
+                case RRDCALC_STATUS_CRITICAL:
+                    value_color = "red";
+                    break;
+
+                case RRDCALC_STATUS_WARNING:
+                    value_color = "orange";
+                    break;
+
+                case RRDCALC_STATUS_CLEAR:
+                    value_color = "brightgreen";
+                    break;
+
+                case RRDCALC_STATUS_UNDEFINED:
+                    value_color = "lightgrey";
+                    break;
+
+                case RRDCALC_STATUS_UNINITIALIZED:
+                    value_color = "#000";
+                    break;
+
+                default:
+                    value_color = "grey";
+                    break;
+            }
+        }
+
+        buffer_svg(w->response.data,
+                label,
+                (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
+                units,
+                label_color,
+                value_color,
+                precision);
+        ret = 200;
+    }
+    else {
+        time_t latest_timestamp = 0;
+        int value_is_null = 1;
+        calculated_number n = NAN;
+        ret = 500;
+
+        // if the collected value is too old, don't calculate its value
+        if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
+            ret = rrd2value(st,
+                    w->response.data,
+                    &n,
+                    (dimensions) ? buffer_tostring(dimensions) : NULL,
+                    points,
+                    after,
+                    before,
+                    group,
+                    options,
+                    NULL,
+                    &latest_timestamp,
+                    &value_is_null);
+
+        // if the value cannot be calculated, show empty badge
+        if (ret != 200) {
+            buffer_no_cacheable(w->response.data);
+            value_is_null = 1;
+            n = 0;
+            ret = 200;
+        }
+        else if (refresh > 0) {
+            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+            w->response.data->expires = now_realtime_sec() + refresh;
+        }
+        else buffer_no_cacheable(w->response.data);
+
+        // render the badge
+        buffer_svg(w->response.data,
+                label,
+                (value_is_null)?NAN:(n * multiply / divide),
+                units,
+                label_color,
+                value_color,
+                precision);
+    }
+
+    cleanup:
+    buffer_free(dimensions);
+    return ret;
+}
+
+// returns the HTTP code
+inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) {
+    debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
+
+    int ret = 400;
+    BUFFER *dimensions = NULL;
+
+    buffer_flush(w->response.data);
+
+    char    *google_version = "0.6",
+            *google_reqId = "0",
+            *google_sig = "0",
+            *google_out = "json",
+            *responseHandler = NULL,
+            *outFileName = NULL;
+
+    time_t last_timestamp_in_data = 0, google_timestamp = 0;
+
+    char *chart = NULL
+    , *before_str = NULL
+    , *after_str = NULL
+    , *points_str = NULL;
+
+    int group = GROUP_AVERAGE;
+    uint32_t format = DATASOURCE_JSON;
+    uint32_t options = 0x00000000;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
+
+        // name and value are now the parameters
+        // they are not null and not empty
+
+        if(!strcmp(name, "chart")) chart = value;
+        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
+            if(!dimensions) dimensions = buffer_create(100);
+            buffer_strcat(dimensions, "|");
+            buffer_strcat(dimensions, value);
+        }
+        else if(!strcmp(name, "after")) after_str = value;
+        else if(!strcmp(name, "before")) before_str = value;
+        else if(!strcmp(name, "points")) points_str = value;
+        else if(!strcmp(name, "group")) {
+            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
+        }
+        else if(!strcmp(name, "format")) {
+            format = web_client_api_request_v1_data_format(value);
+        }
+        else if(!strcmp(name, "options")) {
+            options |= web_client_api_request_v1_data_options(value);
+        }
+        else if(!strcmp(name, "callback")) {
+            responseHandler = value;
+        }
+        else if(!strcmp(name, "filename")) {
+            outFileName = value;
+        }
+        else if(!strcmp(name, "tqx")) {
+            // parse Google Visualization API options
+            // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
+            char *tqx_name, *tqx_value;
+
+            while(value) {
+                tqx_value = mystrsep(&value, ";");
+                if(!tqx_value || !*tqx_value) continue;
+
+                tqx_name = mystrsep(&tqx_value, ":");
+                if(!tqx_name || !*tqx_name) continue;
+                if(!tqx_value || !*tqx_value) continue;
+
+                if(!strcmp(tqx_name, "version"))
+                    google_version = tqx_value;
+                else if(!strcmp(tqx_name, "reqId"))
+                    google_reqId = tqx_value;
+                else if(!strcmp(tqx_name, "sig")) {
+                    google_sig = tqx_value;
+                    google_timestamp = strtoul(google_sig, NULL, 0);
+                }
+                else if(!strcmp(tqx_name, "out")) {
+                    google_out = tqx_value;
+                    format = web_client_api_request_v1_data_google_format(google_out);
+                }
+                else if(!strcmp(tqx_name, "responseHandler"))
+                    responseHandler = tqx_value;
+                else if(!strcmp(tqx_name, "outFileName"))
+                    outFileName = tqx_value;
+            }
+        }
+    }
+
+    if(!chart || !*chart) {
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(host, chart);
+    if(!st) st = rrdset_find_byname(host, chart);
+    if(!st) {
+        buffer_strcat(w->response.data, "Chart is not found: ");
+        buffer_strcat_htmlescape(w->response.data, chart);
+        ret = 404;
+        goto cleanup;
+    }
+
+    long long before = (before_str && *before_str)?str2l(before_str):0;
+    long long after  = (after_str  && *after_str) ?str2l(after_str):0;
+    int       points = (points_str && *points_str)?str2i(points_str):0;
+
+    debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
+          , w->id
+          , chart
+          , (dimensions)?buffer_tostring(dimensions):""
+          , after
+          , before
+          , points
+          , group
+          , format
+          , options
+    );
+
+    if(outFileName && *outFileName) {
+        buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
+        debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
+    }
+
+    if(format == DATASOURCE_DATATABLE_JSONP) {
+        if(responseHandler == NULL)
+            responseHandler = "google.visualization.Query.setResponse";
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
+                w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
+        );
+
+        buffer_sprintf(w->response.data,
+                "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
+                responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
+    }
+    else if(format == DATASOURCE_JSONP) {
+        if(responseHandler == NULL)
+            responseHandler = "callback";
+
+        buffer_strcat(w->response.data, responseHandler);
+        buffer_strcat(w->response.data, "(");
+    }
+
+    ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
+
+    if(format == DATASOURCE_DATATABLE_JSONP) {
+        if(google_timestamp < last_timestamp_in_data)
+            buffer_strcat(w->response.data, "});");
+
+        else {
+            // the client already has the latest data
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data,
+                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
+                    responseHandler, google_version, google_reqId);
+        }
+    }
+    else if(format == DATASOURCE_JSONP)
+        buffer_strcat(w->response.data, ");");
+
+    cleanup:
+    buffer_free(dimensions);
+    return ret;
+}
+
+inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url) {
+    static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
+            hash_switch = 0, hash_machine = 0, hash_url = 0, hash_name = 0, hash_delete_url = 0, hash_for = 0,
+            hash_to = 0 /*, hash_redirects = 0 */;
+
+    if(unlikely(!hash_action)) {
+        hash_action = simple_hash("action");
+        hash_access = simple_hash("access");
+        hash_hello = simple_hash("hello");
+        hash_delete = simple_hash("delete");
+        hash_search = simple_hash("search");
+        hash_switch = simple_hash("switch");
+        hash_machine = simple_hash("machine");
+        hash_url = simple_hash("url");
+        hash_name = simple_hash("name");
+        hash_delete_url = simple_hash("delete_url");
+        hash_for = simple_hash("for");
+        hash_to = simple_hash("to");
+/*
+        hash_redirects = simple_hash("redirects");
+*/
+    }
+
+    char person_guid[GUID_LEN + 1] = "";
+
+    debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
+
+    // FIXME
+    // The browser may send multiple cookies with our id
+
+    char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
+    if(cookie)
+        strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
+
+    char action = '\0';
+    char *machine_guid = NULL,
+            *machine_url = NULL,
+            *url_name = NULL,
+            *search_machine_guid = NULL,
+            *delete_url = NULL,
+            *to_person_guid = NULL;
+/*
+    int redirects = 0;
+*/
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if (!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if (!name || !*name) continue;
+        if (!value || !*value) continue;
+
+        debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
+
+        uint32_t hash = simple_hash(name);
+
+        if(hash == hash_action && !strcmp(name, "action")) {
+            uint32_t vhash = simple_hash(value);
+
+            if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
+            else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
+            else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
+            else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
+            else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
+#ifdef NETDATA_INTERNAL_CHECKS
+            else error("unknown registry action '%s'", value);
+#endif /* NETDATA_INTERNAL_CHECKS */
+        }
+/*
+        else if(hash == hash_redirects && !strcmp(name, "redirects"))
+            redirects = atoi(value);
+*/
+        else if(hash == hash_machine && !strcmp(name, "machine"))
+            machine_guid = value;
+
+        else if(hash == hash_url && !strcmp(name, "url"))
+            machine_url = value;
+
+        else if(action == 'A') {
+            if(hash == hash_name && !strcmp(name, "name"))
+                url_name = value;
+        }
+        else if(action == 'D') {
+            if(hash == hash_delete_url && !strcmp(name, "delete_url"))
+                delete_url = value;
+        }
+        else if(action == 'S') {
+            if(hash == hash_for && !strcmp(name, "for"))
+                search_machine_guid = value;
+        }
+        else if(action == 'W') {
+            if(hash == hash_to && !strcmp(name, "to"))
+                to_person_guid = value;
+        }
+#ifdef NETDATA_INTERNAL_CHECKS
+        else error("unused registry URL parameter '%s' with value '%s'", name, value);
+#endif /* NETDATA_INTERNAL_CHECKS */
+    }
+
+    if(respect_web_browser_do_not_track_policy && w->donottrack) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
+        return 400;
+    }
+
+    if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
+        error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
+                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Invalid registry Access request.");
+        return 400;
+    }
+    else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
+        error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
+                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Invalid registry Delete request.");
+        return 400;
+    }
+    else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
+        error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
+                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Invalid registry Search request.");
+        return 400;
+    }
+    else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
+        error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
+                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Invalid registry Switch request.");
+        return 400;
+    }
+
+    switch(action) {
+        case 'A':
+            w->tracking_required = 1;
+            return registry_request_access_json(host, w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec());
+
+        case 'D':
+            w->tracking_required = 1;
+            return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec());
+
+        case 'S':
+            w->tracking_required = 1;
+            return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec());
+
+        case 'W':
+            w->tracking_required = 1;
+            return registry_request_switch_json(host, w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec());
+
+        case 'H':
+            return registry_request_hello_json(host, w);
+
+        default:
+            buffer_flush(w->response.data);
+            buffer_strcat(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
+            return 400;
+    }
+}
+
+inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) {
+    static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0, hash_alarm_variables = 0, hash_raw = 0;
+
+    if(unlikely(hash_data == 0)) {
+        hash_data = simple_hash("data");
+        hash_chart = simple_hash("chart");
+        hash_charts = simple_hash("charts");
+        hash_registry = simple_hash("registry");
+        hash_badge = simple_hash("badge.svg");
+        hash_alarms = simple_hash("alarms");
+        hash_alarm_log = simple_hash("alarm_log");
+        hash_alarm_variables = simple_hash("alarm_variables");
+        hash_raw = simple_hash("allmetrics");
+    }
+
+    // get the command
+    char *tok = mystrsep(&url, "/?&");
+    if(tok && *tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
+        uint32_t hash = simple_hash(tok);
+
+        if(hash == hash_data && !strcmp(tok, "data"))
+            return web_client_api_request_v1_data(host, w, url);
+
+        else if(hash == hash_chart && !strcmp(tok, "chart"))
+            return web_client_api_request_v1_chart(host, w, url);
+
+        else if(hash == hash_charts && !strcmp(tok, "charts"))
+            return web_client_api_request_v1_charts(host, w, url);
+
+        else if(hash == hash_registry && !strcmp(tok, "registry"))
+            return web_client_api_request_v1_registry(host, w, url);
+
+        else if(hash == hash_badge && !strcmp(tok, "badge.svg"))
+            return web_client_api_request_v1_badge(host, w, url);
+
+        else if(hash == hash_alarms && !strcmp(tok, "alarms"))
+            return web_client_api_request_v1_alarms(host, w, url);
+
+        else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log"))
+            return web_client_api_request_v1_alarm_log(host, w, url);
+
+        else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables"))
+            return web_client_api_request_v1_alarm_variables(host, w, url);
+
+        else if(hash == hash_raw && !strcmp(tok, "allmetrics"))
+            return web_client_api_request_v1_allmetrics(host, w, url);
+
+        else {
+            buffer_flush(w->response.data);
+            buffer_strcat(w->response.data, "Unsupported v1 API command: ");
+            buffer_strcat_htmlescape(w->response.data, tok);
+            return 404;
+        }
+    }
+    else {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Which API v1 command?");
+        return 400;
+    }
+}
diff --git a/src/web_api_v1.h b/src/web_api_v1.h
new file mode 100644 (file)
index 0000000..e980edb
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef NETDATA_WEB_API_V1_H
+#define NETDATA_WEB_API_V1_H
+
+extern int web_client_api_request_v1_data_group(char *name, int def);
+extern uint32_t web_client_api_request_v1_data_options(char *o);
+extern uint32_t web_client_api_request_v1_data_format(char *name);
+extern uint32_t web_client_api_request_v1_data_google_format(char *name);
+
+extern int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf));
+extern int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url);
+
+#endif //NETDATA_WEB_API_V1_H
index 49aef9fba3f2b596e39eb3b09bd0579c39d91ed0..feccde6b323fc4e66643e9cfe7cba0babdc05d73 100644 (file)
@@ -288,8 +288,7 @@ gid_t web_files_gid(void) {
     return(owner_gid);
 }
 
-int mysendfile(struct web_client *w, char *filename)
-{
+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);
 
     // skip leading slashes
@@ -531,98 +530,6 @@ void buffer_data_options2string(BUFFER *wb, uint32_t options) {
     }
 }
 
-uint32_t web_client_api_request_v1_data_options(char *o)
-{
-    uint32_t ret = 0x00000000;
-    char *tok;
-
-    while(o && *o && (tok = mystrsep(&o, ", |"))) {
-        if(!*tok) continue;
-
-        if(!strcmp(tok, "nonzero"))
-            ret |= RRDR_OPTION_NONZERO;
-        else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
-            ret |= RRDR_OPTION_REVERSED;
-        else if(!strcmp(tok, "jsonwrap"))
-            ret |= RRDR_OPTION_JSON_WRAP;
-        else if(!strcmp(tok, "min2max"))
-            ret |= RRDR_OPTION_MIN2MAX;
-        else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
-            ret |= RRDR_OPTION_MILLISECONDS;
-        else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
-            ret |= RRDR_OPTION_ABSOLUTE;
-        else if(!strcmp(tok, "seconds"))
-            ret |= RRDR_OPTION_SECONDS;
-        else if(!strcmp(tok, "null2zero"))
-            ret |= RRDR_OPTION_NULL2ZERO;
-        else if(!strcmp(tok, "objectrows"))
-            ret |= RRDR_OPTION_OBJECTSROWS;
-        else if(!strcmp(tok, "google_json"))
-            ret |= RRDR_OPTION_GOOGLE_JSON;
-        else if(!strcmp(tok, "percentage"))
-            ret |= RRDR_OPTION_PERCENTAGE;
-        else if(!strcmp(tok, "unaligned"))
-            ret |= RRDR_OPTION_NOT_ALIGNED;
-    }
-
-    return ret;
-}
-
-uint32_t web_client_api_request_v1_data_format(char *name)
-{
-    if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
-        return DATASOURCE_DATATABLE_JSON;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
-        return DATASOURCE_DATATABLE_JSONP;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
-        return DATASOURCE_JSON;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
-        return DATASOURCE_JSONP;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
-        return DATASOURCE_SSV;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
-        return DATASOURCE_CSV;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
-        return DATASOURCE_TSV;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
-        return DATASOURCE_HTML;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
-        return DATASOURCE_JS_ARRAY;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
-        return DATASOURCE_SSV_COMMA;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
-        return DATASOURCE_CSV_JSON_ARRAY;
-
-    return DATASOURCE_JSON;
-}
-
-uint32_t web_client_api_request_v1_data_google_format(char *name)
-{
-    if(!strcmp(name, "json"))
-        return DATASOURCE_DATATABLE_JSONP;
-
-    else if(!strcmp(name, "html"))
-        return DATASOURCE_HTML;
-
-    else if(!strcmp(name, "csv"))
-        return DATASOURCE_CSV;
-
-    else if(!strcmp(name, "tsv-excel"))
-        return DATASOURCE_TSV;
-
-    return DATASOURCE_JSON;
-}
-
 const char *group_method2string(int group) {
     switch(group) {
         case GROUP_UNDEFINED:
@@ -648,826 +555,14 @@ const char *group_method2string(int group) {
     }
 }
 
-int web_client_api_request_v1_data_group(char *name, int def)
-{
-    if(!strcmp(name, "average"))
-        return GROUP_AVERAGE;
-
-    else if(!strcmp(name, "min"))
-        return GROUP_MIN;
-
-    else if(!strcmp(name, "max"))
-        return GROUP_MAX;
-
-    else if(!strcmp(name, "sum"))
-        return GROUP_SUM;
-
-    else if(!strcmp(name, "incremental-sum"))
-        return GROUP_INCREMENTAL_SUM;
-
-    return def;
-}
-
-int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url)
-{
-    int all = 0;
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if (!value || !*value) continue;
-
-        if(!strcmp(value, "all")) all = 1;
-        else if(!strcmp(value, "active")) all = 0;
-    }
-
-    buffer_flush(w->response.data);
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    health_alarms2json(host, w->response.data, all);
-    return 200;
-}
-
-int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url)
-{
-    uint32_t after = 0;
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if (!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        if(!strcmp(name, "after")) after = (uint32_t)strtoul(value, NULL, 0);
-    }
-
-    buffer_flush(w->response.data);
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    health_alarm_log2json(host, w->response.data, after);
-    return 200;
-}
-
-int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf))
-{
-    int ret = 400;
-    char *chart = NULL;
-
-    buffer_flush(w->response.data);
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if(!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        // name and value are now the parameters
-        // they are not null and not empty
-
-        if(!strcmp(name, "chart")) chart = value;
-        //else {
-        /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
-        //  goto cleanup;
-        //}
-    }
-
-    if(!chart || !*chart) {
-        buffer_sprintf(w->response.data, "No chart id is given at the request.");
-        goto cleanup;
-    }
-
-    RRDSET *st = rrdset_find(host, chart);
-    if(!st) st = rrdset_find_byname(host, chart);
-    if(!st) {
-        buffer_strcat(w->response.data, "Chart is not found: ");
-        buffer_strcat_htmlescape(w->response.data, chart);
-        ret = 404;
-        goto cleanup;
-    }
-
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    callback(st, w->response.data);
-    return 200;
-
-    cleanup:
-    return ret;
-}
-
-int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url)
-{
-    return web_client_api_request_single_chart(host, w, url, health_api_v1_chart_variables2json);
-}
-
-int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url)
-{
-    (void)url;
-
-    buffer_flush(w->response.data);
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    rrd_stats_api_v1_charts(host, w->response.data);
-    return 200;
-}
-
-int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url)
-{
-    int format = ALLMETRICS_SHELL;
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if (!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        if(!strcmp(name, "format")) {
-            if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
-                format = ALLMETRICS_SHELL;
-            else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
-                format = ALLMETRICS_PROMETHEUS;
-            else
-                format = 0;
-        }
-    }
-
-    buffer_flush(w->response.data);
-    buffer_no_cacheable(w->response.data);
-
-    switch(format) {
-        case ALLMETRICS_SHELL:
-            w->response.data->contenttype = CT_TEXT_PLAIN;
-            rrd_stats_api_v1_charts_allmetrics_shell(host, w->response.data);
-            return 200;
-
-        case ALLMETRICS_PROMETHEUS:
-            w->response.data->contenttype = CT_PROMETHEUS;
-            rrd_stats_api_v1_charts_allmetrics_prometheus(host, w->response.data);
-            return 200;
-
-        default:
-            w->response.data->contenttype = CT_TEXT_PLAIN;
-            buffer_strcat(w->response.data, "Which format? Only '" ALLMETRICS_FORMAT_SHELL "' and '" ALLMETRICS_FORMAT_PROMETHEUS "' is currently supported.");
-            return 400;
-    }
-}
-
-int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url)
-{
-    return web_client_api_request_single_chart(host, w, url, rrd_stats_api_v1_chart);
-}
-
-int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
-    int ret = 400;
-    buffer_flush(w->response.data);
-
-    BUFFER *dimensions = NULL;
-    
-    const char *chart = NULL
-            , *before_str = NULL
-            , *after_str = NULL
-            , *points_str = NULL
-            , *multiply_str = NULL
-            , *divide_str = NULL
-            , *label = NULL
-            , *units = NULL
-            , *label_color = NULL
-            , *value_color = NULL
-            , *refresh_str = NULL
-            , *precision_str = NULL
-            , *alarm = NULL;
-
-    int group = GROUP_AVERAGE;
-    uint32_t options = 0x00000000;
-
-    while(url) {
-        char *value = mystrsep(&url, "/?&");
-        if(!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
-
-        // name and value are now the parameters
-        // they are not null and not empty
-
-        if(!strcmp(name, "chart")) chart = value;
-        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
-            if(!dimensions)
-                dimensions = buffer_create(100);
-
-            buffer_strcat(dimensions, "|");
-            buffer_strcat(dimensions, value);
-        }
-        else if(!strcmp(name, "after")) after_str = value;
-        else if(!strcmp(name, "before")) before_str = value;
-        else if(!strcmp(name, "points")) points_str = value;
-        else if(!strcmp(name, "group")) {
-            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
-        }
-        else if(!strcmp(name, "options")) {
-            options |= web_client_api_request_v1_data_options(value);
-        }
-        else if(!strcmp(name, "label")) label = value;
-        else if(!strcmp(name, "units")) units = value;
-        else if(!strcmp(name, "label_color")) label_color = value;
-        else if(!strcmp(name, "value_color")) value_color = value;
-        else if(!strcmp(name, "multiply")) multiply_str = value;
-        else if(!strcmp(name, "divide")) divide_str = value;
-        else if(!strcmp(name, "refresh")) refresh_str = value;
-        else if(!strcmp(name, "precision")) precision_str = value;
-        else if(!strcmp(name, "alarm")) alarm = value;
-    }
-
-    if(!chart || !*chart) {
-        buffer_no_cacheable(w->response.data);
-        buffer_sprintf(w->response.data, "No chart id is given at the request.");
-        goto cleanup;
-    }
-
-    RRDSET *st = rrdset_find(host, chart);
-    if(!st) st = rrdset_find_byname(host, chart);
-    if(!st) {
-        buffer_no_cacheable(w->response.data);
-        buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1);
-        ret = 200;
-        goto cleanup;
-    }
-
-    RRDCALC *rc = NULL;
-    if(alarm) {
-        rc = rrdcalc_find(st, alarm);
-        if (!rc) {
-            buffer_no_cacheable(w->response.data);
-            buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1);
-            ret = 200;
-            goto cleanup;
-        }
-    }
-
-    long long multiply  = (multiply_str  && *multiply_str )?str2l(multiply_str):1;
-    long long divide    = (divide_str    && *divide_str   )?str2l(divide_str):1;
-    long long before    = (before_str    && *before_str   )?str2l(before_str):0;
-    long long after     = (after_str     && *after_str    )?str2l(after_str):-st->update_every;
-    int       points    = (points_str    && *points_str   )?str2i(points_str):1;
-    int       precision = (precision_str && *precision_str)?str2i(precision_str):-1;
-
-    if(!multiply) multiply = 1;
-    if(!divide) divide = 1;
-
-    int refresh = 0;
-    if(refresh_str && *refresh_str) {
-        if(!strcmp(refresh_str, "auto")) {
-            if(rc) refresh = rc->update_every;
-            else if(options & RRDR_OPTION_NOT_ALIGNED)
-                refresh = st->update_every;
-            else {
-                refresh = (int)(before - after);
-                if(refresh < 0) refresh = -refresh;
-            }
-        }
-        else {
-            refresh = str2i(refresh_str);
-            if(refresh < 0) refresh = -refresh;
-        }
-    }
-
-    if(!label) {
-        if(alarm) {
-            char *s = (char *)alarm;
-            while(*s) {
-                if(*s == '_') *s = ' ';
-                s++;
-            }
-            label = alarm;
-        }
-        else if(dimensions) {
-            const char *dim = buffer_tostring(dimensions);
-            if(*dim == '|') dim++;
-            label = dim;
-        }
-        else
-            label = st->name;
-    }
-    if(!units) {
-        if(alarm) {
-            if(rc->units)
-                units = rc->units;
-            else
-                units = "";
-        }
-        else if(options & RRDR_OPTION_PERCENTAGE)
-            units = "%";
-        else
-            units = st->units;
-    }
-
-    debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
-            , w->id
-            , chart
-            , alarm?alarm:""
-            , (dimensions)?buffer_tostring(dimensions):""
-            , after
-            , before
-            , points
-            , group
-            , options
-            );
-
-    if(rc) {
-        if (refresh > 0) {
-            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
-            w->response.data->expires = now_realtime_sec() + refresh;
-        }
-        else buffer_no_cacheable(w->response.data);
-
-        if(!value_color) {
-            switch(rc->status) {
-                case RRDCALC_STATUS_CRITICAL:
-                    value_color = "red";
-                    break;
-
-                case RRDCALC_STATUS_WARNING:
-                    value_color = "orange";
-                    break;
-
-                case RRDCALC_STATUS_CLEAR:
-                    value_color = "brightgreen";
-                    break;
-
-                case RRDCALC_STATUS_UNDEFINED:
-                    value_color = "lightgrey";
-                    break;
-
-                case RRDCALC_STATUS_UNINITIALIZED:
-                    value_color = "#000";
-                    break;
-
-                default:
-                    value_color = "grey";
-                    break;
-            }
-        }
-
-        buffer_svg(w->response.data,
-                label,
-                (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
-                units,
-                label_color,
-                value_color,
-                precision);
-        ret = 200;
-    }
-    else {
-        time_t latest_timestamp = 0;
-        int value_is_null = 1;
-        calculated_number n = NAN;
-        ret = 500;
-
-        // if the collected value is too old, don't calculate its value
-        if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
-            ret = rrd2value(st,
-                            w->response.data,
-                            &n,
-                            (dimensions) ? buffer_tostring(dimensions) : NULL,
-                            points,
-                            after,
-                            before,
-                            group,
-                            options,
-                            NULL,
-                            &latest_timestamp,
-                            &value_is_null);
-
-        // if the value cannot be calculated, show empty badge
-        if (ret != 200) {
-            buffer_no_cacheable(w->response.data);
-            value_is_null = 1;
-            n = 0;
-            ret = 200;
-        }
-        else if (refresh > 0) {
-            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
-            w->response.data->expires = now_realtime_sec() + refresh;
-        }
-        else buffer_no_cacheable(w->response.data);
-
-        // render the badge
-        buffer_svg(w->response.data,
-                label,
-                (value_is_null)?NAN:(n * multiply / divide),
-                units,
-                label_color,
-                value_color,
-                precision);
-    }
-
-cleanup:
-    buffer_free(dimensions);
-    return ret;
-}
-
-// returns the HTTP code
-int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url)
-{
-    debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
-
-    int ret = 400;
-    BUFFER *dimensions = NULL;
-
-    buffer_flush(w->response.data);
-
-    char    *google_version = "0.6",
-            *google_reqId = "0",
-            *google_sig = "0",
-            *google_out = "json",
-            *responseHandler = NULL,
-            *outFileName = NULL;
-
-    time_t last_timestamp_in_data = 0, google_timestamp = 0;
-
-    char *chart = NULL
-            , *before_str = NULL
-            , *after_str = NULL
-            , *points_str = NULL;
-
-    int group = GROUP_AVERAGE;
-    uint32_t format = DATASOURCE_JSON;
-    uint32_t options = 0x00000000;
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if(!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
-
-        // name and value are now the parameters
-        // they are not null and not empty
-
-        if(!strcmp(name, "chart")) chart = value;
-        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
-            if(!dimensions) dimensions = buffer_create(100);
-            buffer_strcat(dimensions, "|");
-            buffer_strcat(dimensions, value);
-        }
-        else if(!strcmp(name, "after")) after_str = value;
-        else if(!strcmp(name, "before")) before_str = value;
-        else if(!strcmp(name, "points")) points_str = value;
-        else if(!strcmp(name, "group")) {
-            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
-        }
-        else if(!strcmp(name, "format")) {
-            format = web_client_api_request_v1_data_format(value);
-        }
-        else if(!strcmp(name, "options")) {
-            options |= web_client_api_request_v1_data_options(value);
-        }
-        else if(!strcmp(name, "callback")) {
-            responseHandler = value;
-        }
-        else if(!strcmp(name, "filename")) {
-            outFileName = value;
-        }
-        else if(!strcmp(name, "tqx")) {
-            // parse Google Visualization API options
-            // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
-            char *tqx_name, *tqx_value;
-
-            while(value) {
-                tqx_value = mystrsep(&value, ";");
-                if(!tqx_value || !*tqx_value) continue;
-
-                tqx_name = mystrsep(&tqx_value, ":");
-                if(!tqx_name || !*tqx_name) continue;
-                if(!tqx_value || !*tqx_value) continue;
-
-                if(!strcmp(tqx_name, "version"))
-                    google_version = tqx_value;
-                else if(!strcmp(tqx_name, "reqId"))
-                    google_reqId = tqx_value;
-                else if(!strcmp(tqx_name, "sig")) {
-                    google_sig = tqx_value;
-                    google_timestamp = strtoul(google_sig, NULL, 0);
-                }
-                else if(!strcmp(tqx_name, "out")) {
-                    google_out = tqx_value;
-                    format = web_client_api_request_v1_data_google_format(google_out);
-                }
-                else if(!strcmp(tqx_name, "responseHandler"))
-                    responseHandler = tqx_value;
-                else if(!strcmp(tqx_name, "outFileName"))
-                    outFileName = tqx_value;
-            }
-        }
-    }
-
-    if(!chart || !*chart) {
-        buffer_sprintf(w->response.data, "No chart id is given at the request.");
-        goto cleanup;
-    }
-
-    RRDSET *st = rrdset_find(host, chart);
-    if(!st) st = rrdset_find_byname(host, chart);
-    if(!st) {
-        buffer_strcat(w->response.data, "Chart is not found: ");
-        buffer_strcat_htmlescape(w->response.data, chart);
-        ret = 404;
-        goto cleanup;
-    }
-
-    long long before = (before_str && *before_str)?str2l(before_str):0;
-    long long after  = (after_str  && *after_str) ?str2l(after_str):0;
-    int       points = (points_str && *points_str)?str2i(points_str):0;
-
-    debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
-            , w->id
-            , chart
-            , (dimensions)?buffer_tostring(dimensions):""
-            , after
-            , before
-            , points
-            , group
-            , format
-            , options
-            );
-
-    if(outFileName && *outFileName) {
-        buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
-        debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
-    }
-
-    if(format == DATASOURCE_DATATABLE_JSONP) {
-        if(responseHandler == NULL)
-            responseHandler = "google.visualization.Query.setResponse";
-
-        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
-                w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
-            );
-
-        buffer_sprintf(w->response.data,
-            "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
-            responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
-    }
-    else if(format == DATASOURCE_JSONP) {
-        if(responseHandler == NULL)
-            responseHandler = "callback";
-
-        buffer_strcat(w->response.data, responseHandler);
-        buffer_strcat(w->response.data, "(");
-    }
-
-    ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
-
-    if(format == DATASOURCE_DATATABLE_JSONP) {
-        if(google_timestamp < last_timestamp_in_data)
-            buffer_strcat(w->response.data, "});");
-
-        else {
-            // the client already has the latest data
-            buffer_flush(w->response.data);
-            buffer_sprintf(w->response.data,
-                "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
-                responseHandler, google_version, google_reqId);
-        }
-    }
-    else if(format == DATASOURCE_JSONP)
-        buffer_strcat(w->response.data, ");");
-
-cleanup:
-    buffer_free(dimensions);
-    return ret;
-}
-
-
-int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url)
-{
-    static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
-            hash_switch = 0, hash_machine = 0, hash_url = 0, hash_name = 0, hash_delete_url = 0, hash_for = 0,
-            hash_to = 0 /*, hash_redirects = 0 */;
-
-    if(unlikely(!hash_action)) {
-        hash_action = simple_hash("action");
-        hash_access = simple_hash("access");
-        hash_hello = simple_hash("hello");
-        hash_delete = simple_hash("delete");
-        hash_search = simple_hash("search");
-        hash_switch = simple_hash("switch");
-        hash_machine = simple_hash("machine");
-        hash_url = simple_hash("url");
-        hash_name = simple_hash("name");
-        hash_delete_url = simple_hash("delete_url");
-        hash_for = simple_hash("for");
-        hash_to = simple_hash("to");
-/*
-        hash_redirects = simple_hash("redirects");
-*/
-    }
-
-    char person_guid[GUID_LEN + 1] = "";
-
-    debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
-
-    // FIXME
-    // The browser may send multiple cookies with our id
-    
-    char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
-    if(cookie)
-        strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
-
-    char action = '\0';
-    char *machine_guid = NULL,
-            *machine_url = NULL,
-            *url_name = NULL,
-            *search_machine_guid = NULL,
-            *delete_url = NULL,
-            *to_person_guid = NULL;
-/*
-    int redirects = 0;
-*/
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if (!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if (!name || !*name) continue;
-        if (!value || !*value) continue;
-
-        debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
-
-        uint32_t hash = simple_hash(name);
-
-        if(hash == hash_action && !strcmp(name, "action")) {
-            uint32_t vhash = simple_hash(value);
-
-            if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
-            else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
-            else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
-            else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
-            else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
-#ifdef NETDATA_INTERNAL_CHECKS
-            else error("unknown registry action '%s'", value);
-#endif /* NETDATA_INTERNAL_CHECKS */
-        }
-/*
-        else if(hash == hash_redirects && !strcmp(name, "redirects"))
-            redirects = atoi(value);
-*/
-        else if(hash == hash_machine && !strcmp(name, "machine"))
-            machine_guid = value;
-
-        else if(hash == hash_url && !strcmp(name, "url"))
-            machine_url = value;
-
-        else if(action == 'A') {
-            if(hash == hash_name && !strcmp(name, "name"))
-                url_name = value;
-        }
-        else if(action == 'D') {
-            if(hash == hash_delete_url && !strcmp(name, "delete_url"))
-                delete_url = value;
-        }
-        else if(action == 'S') {
-            if(hash == hash_for && !strcmp(name, "for"))
-                search_machine_guid = value;
-        }
-        else if(action == 'W') {
-            if(hash == hash_to && !strcmp(name, "to"))
-                to_person_guid = value;
-        }
-#ifdef NETDATA_INTERNAL_CHECKS
-        else error("unused registry URL parameter '%s' with value '%s'", name, value);
-#endif /* NETDATA_INTERNAL_CHECKS */
-    }
-
-    if(respect_web_browser_do_not_track_policy && w->donottrack) {
+static inline int check_host_and_call(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
+    if(unlikely(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) {
         buffer_flush(w->response.data);
-        buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
+        buffer_strcat(w->response.data, "This host does not maintain a database");
         return 400;
     }
 
-    if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
-        error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
-                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
-        buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "Invalid registry Access request.");
-        return 400;
-    }
-    else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
-        error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
-                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
-        buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "Invalid registry Delete request.");
-        return 400;
-    }
-    else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
-        error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
-                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
-        buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "Invalid registry Search request.");
-        return 400;
-    }
-    else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
-        error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
-                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
-        buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "Invalid registry Switch request.");
-        return 400;
-    }
-
-    switch(action) {
-        case 'A':
-            w->tracking_required = 1;
-            return registry_request_access_json(host, w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec());
-
-        case 'D':
-            w->tracking_required = 1;
-            return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec());
-
-        case 'S':
-            w->tracking_required = 1;
-            return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec());
-
-        case 'W':
-            w->tracking_required = 1;
-            return registry_request_switch_json(host, w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec());
-
-        case 'H':
-            return registry_request_hello_json(host, w);
-
-        default:
-            buffer_flush(w->response.data);
-            buffer_strcat(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
-            return 400;
-    }
-}
-
-int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) {
-    static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0, hash_alarm_variables = 0, hash_raw = 0;
-
-    if(unlikely(hash_data == 0)) {
-        hash_data = simple_hash("data");
-        hash_chart = simple_hash("chart");
-        hash_charts = simple_hash("charts");
-        hash_registry = simple_hash("registry");
-        hash_badge = simple_hash("badge.svg");
-        hash_alarms = simple_hash("alarms");
-        hash_alarm_log = simple_hash("alarm_log");
-        hash_alarm_variables = simple_hash("alarm_variables");
-        hash_raw = simple_hash("allmetrics");
-    }
-
-    // get the command
-    char *tok = mystrsep(&url, "/?&");
-    if(tok && *tok) {
-        debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
-        uint32_t hash = simple_hash(tok);
-
-        if(hash == hash_data && !strcmp(tok, "data"))
-            return web_client_api_request_v1_data(host, w, url);
-
-        else if(hash == hash_chart && !strcmp(tok, "chart"))
-            return web_client_api_request_v1_chart(host, w, url);
-
-        else if(hash == hash_charts && !strcmp(tok, "charts"))
-            return web_client_api_request_v1_charts(host, w, url);
-
-        else if(hash == hash_registry && !strcmp(tok, "registry"))
-            return web_client_api_request_v1_registry(host, w, url);
-
-        else if(hash == hash_badge && !strcmp(tok, "badge.svg"))
-            return web_client_api_request_v1_badge(host, w, url);
-
-        else if(hash == hash_alarms && !strcmp(tok, "alarms"))
-            return web_client_api_request_v1_alarms(host, w, url);
-
-        else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log"))
-            return web_client_api_request_v1_alarm_log(host, w, url);
-
-        else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables"))
-            return web_client_api_request_v1_alarm_variables(host, w, url);
-
-        else if(hash == hash_raw && !strcmp(tok, "allmetrics"))
-            return web_client_api_request_v1_allmetrics(host, w, url);
-
-        else {
-            buffer_flush(w->response.data);
-            buffer_strcat(w->response.data, "Unsupported v1 API command: ");
-            buffer_strcat_htmlescape(w->response.data, tok);
-            return 404;
-        }
-    }
-    else {
-        buffer_flush(w->response.data);
-        buffer_sprintf(w->response.data, "Which API v1 command?");
-        return 400;
-    }
+    return func(host, w, url);
 }
 
 int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
@@ -1492,179 +587,6 @@ int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
     }
 }
 
-int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type)
-{
-    if(!url || !*url) {
-        buffer_flush(w->response.data);
-        buffer_sprintf(w->response.data, "Incomplete request.");
-        return 400;
-    }
-
-    RRDSET *st = NULL;
-
-    char *args = strchr(url, '?');
-    if(args) {
-        *args='\0';
-        args = &args[1];
-    }
-
-    // get the name of the data to show
-    char *tok = mystrsep(&url, "/");
-    if(!tok) tok = "";
-
-    // do we have such a data set?
-    if(*tok) {
-        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-        st = rrdset_find_byname(host, tok);
-        if(!st) st = rrdset_find(host, tok);
-    }
-
-    if(!st) {
-        // we don't have it
-        // try to send a file with that name
-        buffer_flush(w->response.data);
-        return(mysendfile(w, tok));
-    }
-
-    // we have it
-    debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
-
-    // how many entries does the client want?
-    int lines = (int)st->entries;
-    int group_count = 1;
-    time_t after = 0, before = 0;
-    int group_method = GROUP_AVERAGE;
-    int nonzero = 0;
-
-    if(url) {
-        // parse the lines required
-        tok = mystrsep(&url, "/");
-        if(tok) lines = str2i(tok);
-        if(lines < 1) lines = 1;
-    }
-    if(url) {
-        // parse the group count required
-        tok = mystrsep(&url, "/");
-        if(tok && *tok) group_count = str2i(tok);
-        if(group_count < 1) group_count = 1;
-        //if(group_count > save_history / 20) group_count = save_history / 20;
-    }
-    if(url) {
-        // parse the grouping method required
-        tok = mystrsep(&url, "/");
-        if(tok && *tok) {
-            if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
-            else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
-            else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
-            else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
-        }
-    }
-    if(url) {
-        // parse after time
-        tok = mystrsep(&url, "/");
-        if(tok && *tok) after = str2ul(tok);
-        if(after < 0) after = 0;
-    }
-    if(url) {
-        // parse before time
-        tok = mystrsep(&url, "/");
-        if(tok && *tok) before = str2ul(tok);
-        if(before < 0) before = 0;
-    }
-    if(url) {
-        // parse nonzero
-        tok = mystrsep(&url, "/");
-        if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
-    }
-
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    buffer_flush(w->response.data);
-
-    char *google_version = "0.6";
-    char *google_reqId = "0";
-    char *google_sig = "0";
-    char *google_out = "json";
-    char *google_responseHandler = "google.visualization.Query.setResponse";
-    char *google_outFileName = NULL;
-    time_t last_timestamp_in_data = 0;
-    if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
-
-        w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-
-        while(args) {
-            tok = mystrsep(&args, "&");
-            if(tok && *tok) {
-                char *name = mystrsep(&tok, "=");
-                if(name && *name && strcmp(name, "tqx") == 0) {
-                    char *key = mystrsep(&tok, ":");
-                    char *value = mystrsep(&tok, ";");
-                    if(key && value && *key && *value) {
-                        if(strcmp(key, "version") == 0)
-                            google_version = value;
-
-                        else if(strcmp(key, "reqId") == 0)
-                            google_reqId = value;
-
-                        else if(strcmp(key, "sig") == 0)
-                            google_sig = value;
-
-                        else if(strcmp(key, "out") == 0)
-                            google_out = value;
-
-                        else if(strcmp(key, "responseHandler") == 0)
-                            google_responseHandler = value;
-
-                        else if(strcmp(key, "outFileName") == 0)
-                            google_outFileName = value;
-                    }
-                }
-            }
-        }
-
-        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
-            w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
-            );
-
-        if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-            last_timestamp_in_data = strtoul(google_sig, NULL, 0);
-
-            // check the client wants json
-            if(strcmp(google_out, "json") != 0) {
-                buffer_sprintf(w->response.data,
-                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
-                    google_responseHandler, google_version, google_reqId, google_out);
-                    return 200;
-            }
-        }
-    }
-
-    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-        buffer_sprintf(w->response.data,
-            "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
-            google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
-    }
-
-    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
-        w->id, st->name, st->id, lines, group_count, group_method, after, before);
-
-    time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
-
-    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-        if(timestamp_in_data > last_timestamp_in_data)
-            buffer_strcat(w->response.data, "});");
-
-        else {
-            // the client already has the latest data
-            buffer_flush(w->response.data);
-            buffer_sprintf(w->response.data,
-                "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
-                google_responseHandler, google_version, google_reqId);
-        }
-    }
-
-    return 200;
-}
-
 const char *web_content_type_to_string(uint8_t contenttype) {
     switch(contenttype) {
         case CT_TEXT_HTML:
@@ -2160,78 +1082,39 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
         uint32_t hash = simple_hash(tok);
         debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
 
-        if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) {
+        if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) {                           // current API
             debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
-            return web_client_api_request(host, w, url);
+            return check_host_and_call(host, w, url, web_client_api_request);
         }
-        else if(unlikely(hash == hash_host && strcmp(tok, "host") == 0)) {
+        else if(unlikely(hash == hash_host && strcmp(tok, "host") == 0)) {                    // host switching
             debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
             return web_client_switch_host(host, w, url);
         }
-        else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) {
-            debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
-            w->response.data->contenttype = CT_TEXT_PLAIN;
-            buffer_flush(w->response.data);
-            config_generate(w->response.data, 0);
-            return 200;
+        else if(unlikely(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0)) {             // old API "data"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API data request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_data_request_json);
         }
-        else if(unlikely(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0)) { // "data"
-            // the client is requesting rrd data -- OLD API
-            return web_client_api_old_data_request(host, w, url, DATASOURCE_JSON);
+        else if(unlikely(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0)) { // old API "datasource"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API datasource request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_data_request_jsonp);
         }
-        else if(unlikely(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0)) { // "datasource"
-            // the client is requesting google datasource -- OLD API
-            return web_client_api_old_data_request(host, w, url, DATASOURCE_DATATABLE_JSONP);
+        else if(unlikely(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0)) {           // old API "graph"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API graph request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_graph_request);
         }
-        else if(unlikely(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0)) { // "graph"
-            // the client is requesting an rrd graph -- OLD API
-
-            // get the name of the data to show
-            tok = mystrsep(&url, "/?&");
-            if(tok && *tok) {
-                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);
-                if(!st) st = rrdset_find(host, tok);
-                if(!st) {
-                    // we don't have it
-                    // try to send a file with that name
-                    buffer_flush(w->response.data);
-                    return mysendfile(w, tok);
-                }
-
-                debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
-                w->response.data->contenttype = CT_APPLICATION_JSON;
-                buffer_flush(w->response.data);
-                rrd_stats_graph_json(st, url, w->response.data);
-                return 200;
-            }
-
-            buffer_flush(w->response.data);
-            buffer_strcat(w->response.data, "Graph name?\r\n");
-            return 400;
+        else if(unlikely(hash == hash_list && strcmp(tok, "list") == 0)) {                    // old API "list"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API list request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_list_request);
         }
-        else if(unlikely(hash == hash_list && strcmp(tok, "list") == 0)) {
-            // OLD API
-            debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
-
-            buffer_flush(w->response.data);
-            RRDSET *st;
-
-            rrdhost_rdlock(host);
-            rrdset_foreach_read(st, host) buffer_sprintf(w->response.data, "%s\n", st->name);
-            rrdhost_unlock(host);
-
-            return 200;
+        else if(unlikely(hash == hash_all_json && strcmp(tok, "all.json") == 0)) {            // old API "all.json"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API all.json request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_all_json);
         }
-        else if(unlikely(hash == hash_all_json && strcmp(tok, "all.json") == 0)) {
-            // OLD API
-            debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
-
-            w->response.data->contenttype = CT_APPLICATION_JSON;
+        else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) {    // netdata.conf
+            debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
+            w->response.data->contenttype = CT_TEXT_PLAIN;
             buffer_flush(w->response.data);
-            rrd_stats_all_json(host, w->response.data);
+            config_generate(w->response.data, 0);
             return 200;
         }
 #ifdef NETDATA_INTERNAL_CHECKS
index 3ba2435a35f35c472f0bb99477f1d00951d2a14f..70c5b1ff05f66ec4262f1ead9a229e3ac4d93ea9 100644 (file)
@@ -115,4 +115,7 @@ extern int web_client_api_request_v1_data_group(char *name, int def);
 extern const char *group_method2string(int group);
 
 extern void buffer_data_options2string(BUFFER *wb, uint32_t options);
+
+extern int mysendfile(struct web_client *w, char *filename);
+
 #endif