Browse Source

Revert "New URL parser (#6070)"

This reverts commit 58b7d95a7ec9c576f8a06bbab07f755846b5349a.
---
As agreed with @thiago and @cakrit we revert URL parser changes,
to buy the time on a more detailed investigation
---
Pavlos Emm. Katsoulakis 5 years ago
parent
commit
0fca36fd59

+ 0 - 28
libnetdata/inlined.h

@@ -31,19 +31,6 @@ static inline uint32_t simple_hash(const char *name) {
     return hval;
 }
 
-static inline uint32_t simple_nhash(const char *name,size_t len) {
-    unsigned char *s = (unsigned char *) name;
-    size_t i;
-    uint32_t hval = 0x811c9dc5;
-    i = 0;
-    do {
-        hval *= 16777619;
-        hval ^= (uint32_t) *s++;
-    } while (++i < len);
-
-    return hval;
-}
-
 static inline uint32_t simple_uhash(const char *name) {
     unsigned char *s = (unsigned char *) name;
     uint32_t hval = 0x811c9dc5, c;
@@ -55,21 +42,6 @@ static inline uint32_t simple_uhash(const char *name) {
     return hval;
 }
 
-static inline uint32_t simple_nuhash(const char *name,size_t len) {
-    unsigned char *s = (unsigned char *) name;
-    size_t i;
-    uint32_t hval = 0x811c9dc5, c;
-
-    i = 0;
-    do {
-        c = *s++;
-        if (unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A';
-        hval *= 16777619;
-        hval ^= c;
-    } while ( ++i < len);
-    return hval;
-}
-
 static inline int simple_hash_strcmp(const char *name, const char *b, uint32_t *hash) {
     unsigned char *s = (unsigned char *) name;
     uint32_t hval = 0x811c9dc5;

+ 0 - 85
libnetdata/url/url.c

@@ -79,88 +79,3 @@ char *url_decode_r(char *to, char *url, size_t size) {
 
     return to;
 }
-
-inline HTTP_VALIDATION url_is_request_complete(char *begin,char *end,size_t length) {
-    if ( begin == end) {
-        return HTTP_VALIDATION_INCOMPLETE;
-    }
-
-    if ( length > 3  ) {
-        begin = end - 4;
-    }
-
-    uint32_t counter = 0;
-    do {
-        if (*begin == '\r') {
-            begin++;
-            if ( begin == end )
-            {
-                break;
-            }
-
-            if (*begin == '\n')
-            {
-                counter++;
-            }
-        } else if (*begin == '\n') {
-            begin++;
-            counter++;
-        }
-
-        if ( counter == 2) {
-            break;
-        }
-    }
-    while (begin != end);
-
-    return (counter == 2)?HTTP_VALIDATION_OK:HTTP_VALIDATION_INCOMPLETE;
-}
-
-inline char *url_find_protocol(char *s) {
-    while(*s) {
-        // find the next space
-        while (*s && *s != ' ') s++;
-
-        // is it SPACE + "HTTP/" ?
-        if(*s && !strncmp(s, " HTTP/", 6)) break;
-        else s++;
-    }
-
-    return s;
-}
-
-int url_parse_query_string(struct web_fields *names,struct web_fields *values,char *moveme,char *divisor) {
-    uint32_t i = 0;
-    uint32_t max = WEB_FIELDS_MAX;
-
-    do {
-        if ( i == max) {
-            error("We are exceeding the maximum number of elements possible(%u) in this query string(%s)",max,moveme);
-            break;
-        }
-        if (divisor) {
-            names[i].body = moveme;
-            names[i].length = divisor - moveme;//= - begin
-
-            moveme = ++divisor; //value
-            values[i].body = moveme;
-
-            (void)divisor;
-            divisor = strchr(moveme,'&'); //end of value
-            if (divisor) {
-                values[i].length = (size_t )(divisor - moveme);
-            } else{
-                values[i].length = strlen(moveme);
-                break;
-            }
-
-            moveme = divisor;
-            divisor = strchr(++moveme,'='); //end of value
-            i++;
-        } else {
-            break;
-        }
-    } while (moveme);
-
-    return ++i;
-}

+ 0 - 22
libnetdata/url/url.h

@@ -25,26 +25,4 @@ extern char *url_decode(char *str);
 
 extern char *url_decode_r(char *to, char *url, size_t size);
 
-#define WEB_FIELDS_MAX 200
-struct web_fields{
-    char *body;
-    size_t length;
-};
-// http_request_validate()
-// returns:
-// = 0 : all good, process the request
-// > 0 : request is not supported
-// < 0 : request is incomplete - wait for more data
-
-typedef enum {
-    HTTP_VALIDATION_OK,
-    HTTP_VALIDATION_NOT_SUPPORTED,
-    HTTP_VALIDATION_INCOMPLETE,
-    HTTP_VALIDATION_REDIRECT
-} HTTP_VALIDATION;
-
-extern HTTP_VALIDATION url_is_request_complete(char *begin,char *end,size_t length);
-extern char *url_find_protocol(char *s);
-extern int url_parse_query_string(struct web_fields *names,struct web_fields *values,char *moveme,char *divisor);
-
 #endif /* NETDATA_URL_H */

+ 0 - 207
tests/urls/requests.sh

@@ -1,207 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-################################################################################################
-####																						####
-####									GLOBAL VARIABLES									####
-####																						####
-################################################################################################
-
-NETDATA_VARLIB_DIR="@varlibdir_POST@"
-
-#CT=`date +'%s%N' |cut -b1-13`
-# The current time
-CT=`date +'%s'`
-
-# The previous time
-PT=$(( $CT - 100))
-
-# The curl options used to do download
-CURLOPTS="-v --create-dirs -o"
-
-# The output directory where we will store the results and error
-OUTDIR="tests"
-
-################################################################################################
-####																						####
-####										FUNCTIONS										####
-####																						####
-################################################################################################
-
-# Print error message and close script
-netdata_print_error(){
-	echo "Closing due error \"$1\" code \"$2\""
-	exit 1
-}
-
-# Print the header message of the function
-netdata_print_header() {
-	echo "$1"
-}
-
-# Create the main directory where the results will be stored
-netdata_create_directory() {
-	netdata_print_header "Creating directory $1"
-	if [ ! -d $1 ]; then
-		mkdir $1
-		if [ $? -ne  0 ]; then
-			netdata_print_error "Cannot create directory" $?
-		fi
-	else
-		echo "Working with directory $OUTDIR"
-	fi
-}
-
-netdata_test_download(){
-	grep "HTTP/1.1 200 OK" $1 2>/dev/null 1>/dev/null
-	if [ $? -ne 0 ]; then
-		netdata_print_error "Cannot do download of the page $2" $?
-	fi
-}
-
-# Download information from Netdata 
-netdata_download_various() {
-	netdata_print_header "Getting $2"
-	curl $CURLOPTS $OUTDIR/$3.out "$1/$2" 2> $OUTDIR/$3.err
-	netdata_test_download $OUTDIR/$3.err "$1/$2"
-}
-
-# Download charts from Netdata 
-netdata_download_charts() {
-	curl $CURLOPTS $OUTDIR/charts.out "$1/$2/charts" 2> $OUTDIR/charts.err
-	netdata_test_download $OUTDIR/charts.err "$1/$2/charts"
-
-	#Rewrite the next
-	cat tests/charts.out | grep -w "id"| cut -d: -f2 | grep "\"," | sed s/,//g | sort 
-}
-
-#Test options for a specific chart
-netdata_download_chart() {
-	NAME=`echo $3| sed s/\"//g`
-	netdata_print_header "Getting data for $NAME using $4"
-
-	LDIR=$OUTDIR"/"$4
-
-	LURL=$1/$2=$NAME
-
-	NAME=$NAME"_$4"
-
-	curl $CURLOPTS $LDIR/$NAME.out "$LURL" 2> $LDIR/$NAME.err
-	netdata_test_download $LDIR/$NAME.err $LURL
-
-	UFILES=( "points" "before" "after" )
-	COUNTER=0
-	for OPT in "&points=100" "&before=$PT" "&after=$CT" ;
-	do
-		LURL="$LURL$OPT"
-		LFILE=$NAME"_${UFILES[$COUNTER]}";
-
-		curl $CURLOPTS "$LDIR/$LFILE.out" "$LURL" 2> "$LDIR/$LFILE.err"
-		netdata_test_download $LDIR/$LFILE.err $LURL
-
-		COUNTER=$(($COUNTER + 1))
-	done
-
-	LURL="$LURL&group="
-	for OPT in "min" "max" "sum" "median" "stddev" "cv" "ses" "des" "incremental_sum" "average";
-	do
-		TURL=$LURL$OPT
-		TFILE=$NAME"_$OPT";
-		curl $CURLOPTS "$LDIR/$TFILE.out" "$TURL" 2> "$LDIR/$TFILE.err"
-		netdata_test_download $LDIR/$TFILE.err $TURL
-		for MORE in "jsonp" "json" "ssv" "csv" "datatable" "datasource" "tsv" "ssvcomma" "html" "array";
-		do
-			TURL=$TURL"&format="$MORE
-			TFILE=$NAME"_$OPT""_$MORE";
-			curl $CURLOPTS "$LDIR/$TFILE.out" "$TURL" 2> "$LDIR/$TFILE.err"
-			netdata_test_download $LDIR/$TFILE.err $TURL
-		done
-
-	done
-
-	LURL="$LURL$OPT&gtime=60"
-	NFILE=$NAME"_gtime"
-	curl $CURLOPTS "$LDIR/$NFILE.out" "$TURL" 2> "$LDIR/$NFILE.err"
-	netdata_test_download $LDIR/$NFILE.err $LURL
-
-	LURL="$LURL$OPT&options=percentage"
-	NFILE=$NAME"_percentage"
-	curl $CURLOPTS "$LDIR/$NFILE.out" "$TURL" 2> "$LDIR/$NFILE.err"
-	netdata_test_download $LDIR/$NFILE.err $LURL
-
-	LURL="$LURL$OPT&options=percentage"
-	NFILE=$NAME"_percentage"
-	curl $CURLOPTS "$LDIR/$NFILE.out" "$TURL" 2> "$LDIR/$NFILE.err"
-	netdata_test_download $LDIR/$NFILE.err $LURL
-
-	LURL="$LURL$OPT&dimensions=system%7Cnice"
-	NFILE=$NAME"_dimension"
-	curl $CURLOPTS "$LDIR/$NFILE.out" "$TURL" 2> "$LDIR/$NFILE.err"
-	netdata_test_download $LDIR/$NFILE.err $LURL
-
-}
-
-# Download information from Netdata 
-netdata_download_allmetrics() {
-	netdata_print_header "Getting All metrics"
-	curl $CURLOPTS $OUTDIR/allmetrics.out "$1/$2" 2> $OUTDIR/allmetrics.err
-	netdata_test_download $OUTDIR/allmetrics.err "$1/$2"
-}
-
-# Download charts from Netdata 
-
-################################################################################################
-####																						####
-####									MAIN ROUTINE										####
-####																						####
-################################################################################################
-MURL="http://127.0.0.1:19999"
-
-wget --execute="robots = off" --mirror --convert-links --no-parent http://127.0.0.1:19999
-
-netdata_create_directory $OUTDIR
-
-netdata_download_various $MURL "netdata.conf" "netdata.conf"
-
-netdata_download_various $MURL "api/v1/info" "info"
-
-netdata_download_various $MURL "api/v1/registry?action=hello"  "action"
-
-netdata_print_header "Getting all the netdata charts"
-CHARTS=$( netdata_download_charts "http://127.0.0.1:19999" "api/v1" )
-
-netdata_download_various $MURL "api/v1/allmetrics?format=json"  "allmetrics"
-
-netdata_download_various $MURL "api/v1/alarms?all"  "alarms_all"
-
-netdata_download_various $MURL "api/v1/alarms?active"  "alarms_active"
-
-netdata_download_various $MURL "api/v1/alarm_log?after&_=$PT"  "alarm_log"
-
-for I in $CHARTS ; do
-	NAME=`echo $I| sed s/\"//g`
-	netdata_download_various $MURL "api/v1/alarm_variables?chart=$NAME"  "alarm_variables_$NAME"
-done
-
-netdata_create_directory "$OUTDIR/data"
-for I in $CHARTS ; do
-	netdata_download_chart $MURL "api/v1/data?chart" $I "data"
-done
-
-netdata_create_directory "$OUTDIR/badge.svg"
-for I in $CHARTS ; do
-	netdata_download_chart $MURL "api/v1/badge.svg?chart" $I "badge.svg"
-done
-
-if [ -f "${NETDATA_VARLIB_DIR}/netdata.api.key" ] ;then
-	read -r CORRECT_TOKEN < "${NETDATA_VARLIB_DIR}/netdata.api.key"
-else
-	echo "${NETDATA_VARLIB_DIR}/netdata.api.key not found"
-	echo "Token not found."
-	exit 2
-fi
-curl -H "X-Auth-Token: $TOKEN" "http://127.0.0.1:19999/api/v1/manage/health?cmd=RESET"
-
-echo "ALL the URLS got 200 as answer!"
-
-exit 0

+ 40 - 57
web/api/badges/web_buffer_svg.c

@@ -889,7 +889,6 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
 }
 
 int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
-    (void)url;
     int ret = 400;
     buffer_flush(w->response.data);
 
@@ -913,54 +912,46 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
     int group = RRDR_GROUPING_AVERAGE;
     uint32_t options = 0x00000000;
 
-    uint32_t i = 0;
-    uint32_t end = w->total_params;
-    char save[WEB_FIELDS_MAX];
-    char *value;
-    size_t lvalue;
-    if(end) {
-        do {
-            value = w->param_values[i].body;
-            lvalue = w->param_values[i].length;
-            save[i] = value[lvalue];
-            value[lvalue] = 0x00;
-
-            char *name = w->param_name[i].body;
-            size_t lname = w->param_name[i].length;
-
-            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(!strncmp(name, "chart",lname)) chart = value;
-            else if(!strncmp(name, "dimension",lname) || !strncmp(name, "dim",lname) || !strncmp(name, "dimensions",lname) || !strncmp(name, "dims",lname)) {
-                if(!dimensions)
-                    dimensions = buffer_create(100);
-
-                buffer_strcat(dimensions, "|");
-                buffer_strcat(dimensions, value);
-            }
-            else if(!strncmp(name, "after",lname)) after_str = value;
-            else if(!strncmp(name, "before",lname)) before_str = value;
-            else if(!strncmp(name, "points",lname)) points_str = value;
-            else if(!strncmp(name, "group",lname)) {
-                group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
-            }
-            else if(!strncmp(name, "options",lname)) {
-                options |= web_client_api_request_v1_data_options(value);
-            }
-            else if(!strncmp(name, "label",lname)) label = value;
-            else if(!strncmp(name, "units",lname)) units = value;
-            else if(!strncmp(name, "label_color",lname)) label_color = value;
-            else if(!strncmp(name, "value_color",lname)) value_color = value;
-            else if(!strncmp(name, "multiply",lname)) multiply_str = value;
-            else if(!strncmp(name, "divide",lname)) divide_str = value;
-            else if(!strncmp(name, "refresh",lname)) refresh_str = value;
-            else if(!strncmp(name, "precision",lname)) precision_str = value;
-            else if(!strncmp(name, "scale",lname)) scale_str = value;
-            else if(!strncmp(name, "alarm",lname)) alarm = value;
-
-        } while (++i < end );
+    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, RRDR_GROUPING_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, "scale")) scale_str = value;
+        else if(!strcmp(name, "alarm")) alarm = value;
     }
 
     if(!chart || !*chart) {
@@ -1146,14 +1137,6 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
     }
 
     cleanup:
-    if(end) {
-        i = 0;
-        do {
-            value = w->param_values[i].body;
-            lvalue = w->param_values[i].length;
-            value[lvalue] = save[i];
-        } while(++i < end);
-    }
     buffer_free(dimensions);
     return ret;
 }

+ 38 - 41
web/api/exporters/allmetrics.c

@@ -18,57 +18,54 @@ struct prometheus_output_options {
 };
 
 inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) {
-    (void)url;
     int format = ALLMETRICS_SHELL;
     const char *prometheus_server = w->client_ip;
     uint32_t prometheus_backend_options = global_backend_options;
     PROMETHEUS_OUTPUT_OPTIONS prometheus_output_options = PROMETHEUS_OUTPUT_TIMESTAMPS | ((global_backend_options & BACKEND_OPTION_SEND_NAMES)?PROMETHEUS_OUTPUT_NAMES:0);
     const char *prometheus_prefix = global_backend_prefix;
 
-    uint32_t end = w->total_params;
-    if (end) {
-        uint32_t i = 0;
-        do {
-            char *name = w->param_name[i].body;
-            size_t lname = w->param_name[i].length;
-            char *value = w->param_values[i].body;
-            size_t lvalue = w->param_values[i].length;
+    while(url) {
+        char *value = mystrsep(&url, "&");
+        if (!value || !*value) continue;
 
-            if(!strncmp(name, "format",lname)) {
-                if(!strncmp(value, ALLMETRICS_FORMAT_SHELL,lvalue))
-                    format = ALLMETRICS_SHELL;
-                else if(!strncmp(value, ALLMETRICS_FORMAT_PROMETHEUS,lvalue))
-                    format = ALLMETRICS_PROMETHEUS;
-                else if(!strncmp(value, ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS,lvalue))
-                    format = ALLMETRICS_PROMETHEUS_ALL_HOSTS;
-                else if(!strncmp(value, ALLMETRICS_FORMAT_JSON,lvalue))
-                    format = ALLMETRICS_JSON;
-                else
-                    format = 0;
-            }
-            else if(!strncmp(name, "server",lname)) {
-                prometheus_server = value;
-            }
-            else if(!strncmp(name, "prefix",lname)) {
-                prometheus_prefix = value;
-            }
-            else if(!strncmp(name, "data",lname) || !strncmp(name, "source",lname) || !strncmp(name, "data source",lname) || !strncmp(name, "data-source",lname) || !strncmp(name, "data_source",lname) || !strncmp(name, "datasource",lname)) {
-                prometheus_backend_options = backend_parse_data_source(value, prometheus_backend_options);
-            }
-            else {
-                int i;
-                for(i = 0; prometheus_output_flags_root[i].name ; i++) {
-                    if(!strncmp(name, prometheus_output_flags_root[i].name,lname)) {
-                        if(!strncmp(value, "yes",lvalue) || !strncmp(value, "1",lvalue) || !strncmp(value, "true",lvalue))
-                            prometheus_output_options |= prometheus_output_flags_root[i].flag;
-                        else
-                            prometheus_output_options &= ~prometheus_output_flags_root[i].flag;
+        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 if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS))
+                format = ALLMETRICS_PROMETHEUS_ALL_HOSTS;
+            else if(!strcmp(value, ALLMETRICS_FORMAT_JSON))
+                format = ALLMETRICS_JSON;
+            else
+                format = 0;
+        }
+        else if(!strcmp(name, "server")) {
+            prometheus_server = value;
+        }
+        else if(!strcmp(name, "prefix")) {
+            prometheus_prefix = value;
+        }
+        else if(!strcmp(name, "data") || !strcmp(name, "source") || !strcmp(name, "data source") || !strcmp(name, "data-source") || !strcmp(name, "data_source") || !strcmp(name, "datasource")) {
+            prometheus_backend_options = backend_parse_data_source(value, prometheus_backend_options);
+        }
+        else {
+            int i;
+            for(i = 0; prometheus_output_flags_root[i].name ; i++) {
+                if(!strcmp(name, prometheus_output_flags_root[i].name)) {
+                    if(!strcmp(value, "yes") || !strcmp(value, "1") || !strcmp(value, "true"))
+                        prometheus_output_options |= prometheus_output_flags_root[i].flag;
+                    else
+                        prometheus_output_options &= ~prometheus_output_flags_root[i].flag;
 
-                        break;
-                    }
+                    break;
                 }
             }
-        } while( ++i < end);
+        }
     }
 
     buffer_flush(w->response.data);

+ 68 - 77
web/api/health/health_cmdapi.c

@@ -31,10 +31,13 @@ void free_silencers(SILENCER *t) {
     return;
 }
 
+
+
 int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url) {
     int ret = 400;
     (void) host;
-    (void)url;
+
+
 
     BUFFER *wb = w->response.data;
     buffer_flush(wb);
@@ -70,86 +73,75 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
             buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
             ret = 403;
         } else {
-            uint32_t end = w->total_params;
-            if (end) {
-                uint32_t i = 0;
-                do {
-                    char *key = w->param_name[i].body;
-                    size_t lkey = w->param_name[i].length;
-                    char ksave = key[lkey];
-                    key[lkey] = 0x00;
-
-                    char *value = w->param_values[i].body;
-                    size_t lvalue = w->param_values[i].length;
-                    char vsave = value[lvalue];
-                    value[lvalue] = 0x00;
-
-                    debug(D_WEB_CLIENT, "%llu: API v1 health query param '%s' with value '%s'", w->id, key, value);
-
-                    // name and value are now the parameters
-                    if (!strncmp(key, "cmd",lkey)) {
-                        if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCEALL)) {
-                            silencers->all_alarms = 1;
-                            silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
-                            buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCEALL);
-                        } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLEALL)) {
-                            silencers->all_alarms = 1;
-                            silencers->stype = STYPE_DISABLE_ALARMS;
-                            buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLEALL);
-                        } else if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCE)) {
-                            silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
-                            buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCE);
-                        } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLE)) {
-                            silencers->stype = STYPE_DISABLE_ALARMS;
-                            buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLE);
-                        } else if (!strcmp(value, HEALTH_CMDAPI_CMD_RESET)) {
-                            silencers->all_alarms = 0;
-                            silencers->stype = STYPE_NONE;
-                            free_silencers(silencers->silencers);
-                            silencers->silencers = NULL;
-                            buffer_strcat(wb, HEALTH_CMDAPI_MSG_RESET);
-                        }
-                    } else {
-                        uint32_t hash = simple_uhash(key);
-                        if (unlikely(silencer == NULL)) {
-                            if (
-                                    (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
-                                    (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
-                                    (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
-                                    (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
-                                    (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) ||
-                                    (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY))
-                                    ) {
-                                silencer = create_silencer();
-                            }
-                        }
-
-                        if (hash == hash_alarm && !strncasecmp(key, HEALTH_ALARM_KEY,lkey)) {
-                            silencer->alarms = strdupz(value);
-                            silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT);
-                        } else if (hash == hash_chart && !strncasecmp(key, HEALTH_CHART_KEY,lkey)) {
-                            silencer->charts = strdupz(value);
-                            silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT);
-                        } else if (hash == hash_context && !strncasecmp(key, HEALTH_CONTEXT_KEY,lkey)) {
-                            silencer->contexts = strdupz(value);
-                            silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT);
-                        } else if (hash == hash_host && !strncasecmp(key, HEALTH_HOST_KEY,lkey)) {
-                            silencer->hosts = strdupz(value);
-                            silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT);
-                        } else if (hash == hash_families && !strncasecmp(key, HEALTH_FAMILIES_KEY,lkey)) {
-                            silencer->families = strdupz(value);
-                            silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT);
-                        } else {
-                            buffer_strcat(wb, HEALTH_CMDAPI_MSG_INVALID_KEY);
+            while (url) {
+                char *value = mystrsep(&url, "&");
+                if (!value || !*value) continue;
+
+                char *key = mystrsep(&value, "=");
+                if (!key || !*key) continue;
+                if (!value || !*value) continue;
+
+                debug(D_WEB_CLIENT, "%llu: API v1 health query param '%s' with value '%s'", w->id, key, value);
+
+                // name and value are now the parameters
+                if (!strcmp(key, "cmd")) {
+                    if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCEALL)) {
+                        silencers->all_alarms = 1;
+                        silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
+                        buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCEALL);
+                    } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLEALL)) {
+                        silencers->all_alarms = 1;
+                        silencers->stype = STYPE_DISABLE_ALARMS;
+                        buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLEALL);
+                    } else if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCE)) {
+                        silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
+                        buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCE);
+                    } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLE)) {
+                        silencers->stype = STYPE_DISABLE_ALARMS;
+                        buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLE);
+                    } else if (!strcmp(value, HEALTH_CMDAPI_CMD_RESET)) {
+                        silencers->all_alarms = 0;
+                        silencers->stype = STYPE_NONE;
+                        free_silencers(silencers->silencers);
+                        silencers->silencers = NULL;
+                        buffer_strcat(wb, HEALTH_CMDAPI_MSG_RESET);
+                    }
+                } else {
+                    uint32_t hash = simple_uhash(key);
+                    if (unlikely(silencer == NULL)) {
+                        if (
+                                (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
+                                (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
+                                (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
+                                (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
+                                (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) ||
+                                (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY))
+                                ) {
+                            silencer = create_silencer();
                         }
                     }
-                    key[lkey] = ksave ;
-                    value[lvalue] = vsave ;
 
-                } while( ++i < end );
+                    if (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
+                        silencer->alarms = strdupz(value);
+                        silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT);
+                    } else if (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) {
+                        silencer->charts = strdupz(value);
+                        silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT);
+                    } else if (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) {
+                        silencer->contexts = strdupz(value);
+                        silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT);
+                    } else if (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
+                        silencer->hosts = strdupz(value);
+                        silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT);
+                    } else if (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
+                        silencer->families = strdupz(value);
+                        silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT);
+                    } else {
+                        buffer_strcat(wb, HEALTH_CMDAPI_MSG_INVALID_KEY);
+                    }
+                }
 
             }
-
             if (likely(silencer)) {
                 // Add the created instance to the linked list in silencers
                 silencer->next = silencers->silencers;
@@ -168,7 +160,6 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
             ret = 200;
         }
     }
-
     w->response.data = wb;
     buffer_no_cacheable(w->response.data);
     return ret;

+ 138 - 174
web/api/web_api_v1.c

@@ -195,18 +195,14 @@ inline uint32_t web_client_api_request_v1_data_google_format(char *name) {
 
 
 inline int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url) {
-    (void)url;
     int all = 0;
 
-    uint32_t end = w->total_params;
-    if(end) {
-        uint32_t  i = 0;
-        do {
-            char *value = w->param_values[i].body;
+    while(url) {
+        char *value = mystrsep(&url, "&");
+        if (!value || !*value) continue;
 
-            if(!strncmp(value, "all",3)) all = 1;
-            else if(!strncmp(value, "active",6)) all = 0;
-        } while(++i < end);
+        if(!strcmp(value, "all")) all = 1;
+        else if(!strcmp(value, "active")) all = 0;
     }
 
     buffer_flush(w->response.data);
@@ -217,24 +213,17 @@ inline int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w,
 }
 
 inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url) {
-    (void)url;
     uint32_t after = 0;
 
-    uint32_t end = w->total_params;
-    if(end) {
-        uint32_t  i = 0;
-        do {
-            char *value = w->param_values[i].body;
-            size_t lvalue = w->param_values[i].length;
-            char save = value[lvalue];
-            value[lvalue] = 0x00;
-
-            char *name = w->param_name[i].body;
-            size_t lname = w->param_name[i].length;
-
-            if(!strncmp(name, "after",lname)) after = (uint32_t)strtoul(value, NULL, 0);
-            value[lvalue] = save;
-        } while (++i < end);
+    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);
@@ -244,29 +233,27 @@ inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client
 }
 
 inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) {
-    (void)url;
     int ret = 400;
     char *chart = NULL;
 
     buffer_flush(w->response.data);
 
-    uint32_t  i = 0;
-    uint32_t end = w->total_params;
-    if(end) {
-        do {
-            char *name = w->param_name[i].body;
-            size_t nlength  = w->param_name[i].length;
-            char *value = w->param_values[i].body;
-
-            // name and value are now the parameters
-            // they are not null and not empty
-
-            if(!strncmp(name, "chart",nlength)) chart = value;
-            //else {
-            /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
-            //  goto cleanup;
-            //}
-        } while (++i < end);
+    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) {
@@ -320,7 +307,6 @@ void fix_google_param(char *s) {
 
 // returns the HTTP code
 inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) {
-    (void)url;
     debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
 
     int ret = 400;
@@ -347,82 +333,75 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
     uint32_t format = DATASOURCE_JSON;
     uint32_t options = 0x00000000;
 
-    uint32_t end = w->total_params;
-    char save[WEB_FIELDS_MAX];
-    char *value ;
-    size_t lvalue;
-    if(end) {
-        uint32_t i = 0;
-        do {
-            char *name = w->param_name[i].body;
-            size_t lname = w->param_name[i].length;
-            value = w->param_values[i].body;
-            lvalue = w->param_values[i].length;
-            save[i] = value[lvalue];
-            value[lvalue] = 0x00;
-
-            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(!strncmp(name, "chart",lname)) chart = value;
-            else if(!strncmp(name, "dimension",lname) || !strncmp(name, "dim",lname) || !strncmp(name, "dimensions",lname) || !strncmp(name, "dims",lname)) {
-                if(!dimensions) dimensions = buffer_create(100);
-                buffer_strcat(dimensions, "|");
-                buffer_strcat(dimensions, value);
-            }
-            else if(!strncmp(name, "after",lname)) after_str = value;
-            else if(!strncmp(name, "before",lname)) before_str = value;
-            else if(!strncmp(name, "points",lname)) points_str = value;
-            else if(!strncmp(name, "gtime",lname)) group_time_str = value;
-            else if(!strncmp(name, "group",lname)) {
-                group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
-            }
-            else if(!strncmp(name, "format",lname)) {
-                format = web_client_api_request_v1_data_format(value);
-            }
-            else if(!strncmp(name, "options",lname)) {
-                options |= web_client_api_request_v1_data_options(value);
-            }
-            else if(!strncmp(name, "callback",lname)) {
-                responseHandler = value;
-            }
-            else if(!strncmp(name, "filename",lname)) {
-                outFileName = value;
-            }
-            else if(!strncmp(name, "tqx",lname)) {
-                // 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;
+    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, "gtime")) group_time_str = value;
+        else if(!strcmp(name, "group")) {
+            group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_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;
             }
-        } while (++i < end);
+        }
     }
 
     // validate the google parameters given
@@ -509,14 +488,6 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
         buffer_strcat(w->response.data, ");");
 
     cleanup:
-    if(end) {
-        uint32_t i = 0;
-        do {
-            value = w->param_values[i].body;
-            lvalue = w->param_values[i].length;
-            value[lvalue] = save[i];
-        } while ( ++i < end );
-    }
     buffer_free(dimensions);
     return ret;
 }
@@ -580,33 +551,26 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
     int redirects = 0;
 */
 
-    uint32_t i = 0;
-    uint32_t end = w->total_params;
-    if (!end) {
-        goto nothing;
-    }
+    while(url) {
+        char *value = mystrsep(&url, "&");
+        if (!value || !*value) continue;
 
-    do {
-        char *name = w->param_name[i].body;
-        size_t nlength = w->param_name[i].length;
-        char *value = w->param_values[i].body;
-        size_t vlength = w->param_values[i].length;
+        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);
-        uint32_t hash = simple_nhash(name,nlength);
+        uint32_t hash = simple_hash(name);
 
-        //if(hash == hash_action && !strcmp(name, "action")) {
-        if(hash == hash_action && !strncmp(name, "action",nlength)) {
-            //uint32_t vhash = simple_hash(value);
-            uint32_t vhash = simple_nhash(value,vlength);
+        if(hash == hash_action && !strcmp(name, "action")) {
+            uint32_t vhash = simple_hash(value);
 
-            if(vhash == hash_access && !strncmp(value, "access",vlength)) action = 'A';
-            else if(vhash == hash_hello && !strncmp(value, "hello",vlength)) action = 'H';
-            else if(vhash == hash_delete && !strncmp(value, "delete",vlength)) action = 'D';
-            else if(vhash == hash_search && !strncmp(value, "search",vlength)) action = 'S';
-            else if(vhash == hash_switch && !strncmp(value, "switch",vlength)) action = 'W';
+            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 */
@@ -615,34 +579,33 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
         else if(hash == hash_redirects && !strcmp(name, "redirects"))
             redirects = atoi(value);
 */
-        else if(hash == hash_machine && !strncmp(name, "machine",nlength))
+        else if(hash == hash_machine && !strcmp(name, "machine"))
             machine_guid = value;
 
-        else if(hash == hash_url && !strncmp(name, "url",nlength))
+        else if(hash == hash_url && !strcmp(name, "url"))
             machine_url = value;
 
         else if(action == 'A') {
-            if(hash == hash_name && !strncmp(name, "name",nlength))
+            if(hash == hash_name && !strcmp(name, "name"))
                 url_name = value;
         }
         else if(action == 'D') {
-            if(hash == hash_delete_url && !strncmp(name, "delete_url",nlength))
+            if(hash == hash_delete_url && !strcmp(name, "delete_url"))
                 delete_url = value;
         }
         else if(action == 'S') {
-            if(hash == hash_for && !strncmp(name, "for",nlength))
+            if(hash == hash_for && !strcmp(name, "for"))
                 search_machine_guid = value;
         }
         else if(action == 'W') {
-            if(hash == hash_to && !strncmp(name, "to",nlength))
+            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 */
-    } while (++i < end );
+    }
 
-nothing:
     if(unlikely(respect_web_browser_do_not_track_policy && web_client_has_donottrack(w))) {
         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.");
@@ -834,27 +797,28 @@ inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *
     }
 
     // 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);
 
-    char *cmd = w->command.body;
-    size_t length = w->command.length;
-    uint32_t hash = simple_nhash(cmd,length);
-
-    for(i = 0; api_commands[i].command ;i++) {
-        if(unlikely(hash == api_commands[i].hash && !strncmp(cmd, api_commands[i].command,length))) {
-            if(unlikely(api_commands[i].acl != WEB_CLIENT_ACL_NOCHECK) &&  !(w->acl & api_commands[i].acl))
-                return web_client_permission_denied(w);
+        for(i = 0; api_commands[i].command ;i++) {
+            if(unlikely(hash == api_commands[i].hash && !strcmp(tok, api_commands[i].command))) {
+                if(unlikely(api_commands[i].acl != WEB_CLIENT_ACL_NOCHECK) &&  !(w->acl & api_commands[i].acl))
+                    return web_client_permission_denied(w);
 
-            return api_commands[i].callback(host, w, url);
+                return api_commands[i].callback(host, w, url);
+            }
         }
-    }
-
-    char copyme[256];
-    length = w->path.length;
-    memcpy(copyme,w->path.body,length);
-    copyme[length] = 0x00;
 
-    buffer_flush(w->response.data);
-    buffer_strcat(w->response.data, "Unsupported v1 API command: ");
-    buffer_strcat_htmlescape(w->response.data, copyme);
-    return 404;
+        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;
+    }
 }

+ 160 - 353
web/server/web_client.c

@@ -143,7 +143,7 @@ void web_client_request_done(struct web_client *w) {
             debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
 
             if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
-                if (w->ifd != -1) {
+                if (w->ifd != -1){
                     close(w->ifd);
                 }
             }
@@ -352,7 +352,7 @@ int mysendfile(struct web_client *w, char *filename) {
     // if the filename contains "strange" characters, refuse to serve it
     char *s;
     for(s = filename; *s ;s++) {
-        if(!isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
+        if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
             debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
             w->response.data->contenttype = CT_TEXT_HTML;
             buffer_sprintf(w->response.data, "Filename contains invalid characters: ");
@@ -594,26 +594,15 @@ int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
 {
     // get the api version
     char *tok = mystrsep(&url, "/");
-    (void)tok;
-    char *body = w->version.body;
-    size_t length = w->version.length;
-    if(body) {
-    //if(tok && *tok) {
-        //debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
-        debug(D_WEB_CLIENT, "%llu: Searching for API version'.", w->id);
-        //if(strcmp(tok, "v1") == 0)
-        if(strncmp(body, "v1",length) == 0) {
+    if(tok && *tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
+        if(strcmp(tok, "v1") == 0)
             return web_client_api_request_v1(host, w, url);
-        } else {
-            char response[NETDATA_WEB_REQUEST_URL_SIZE];
-            strncpy(response,w->version.body,length);
-            response[length] = 0x00;
+        else {
             buffer_flush(w->response.data);
-
             w->response.data->contenttype = CT_TEXT_HTML;
             buffer_strcat(w->response.data, "Unsupported API version: ");
-            buffer_strcat_htmlescape(w->response.data, response);
-            //buffer_strcat_htmlescape(w->response.data, tok);
+            buffer_strcat_htmlescape(w->response.data, tok);
             return 404;
         }
     }
@@ -802,7 +791,7 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
     } else if(hash == hash_authorization&& !strcasecmp(s, "X-Auth-Token")) {
         w->auth_bearer_token = strdupz(v);
     }
-    else if(hash == hash_host && !strcasecmp(s, "Host")) {
+    else if(hash == hash_host && !strcasecmp(s, "Host")){
         strncpyz(w->host, v, (ve - v));
     }
 #ifdef NETDATA_WITH_ZLIB
@@ -823,67 +812,76 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
     return ve;
 }
 
-static inline HTTP_VALIDATION web_client_is_complete(char *begin,char *end,size_t length){
-    if ( begin == end){
-        return HTTP_VALIDATION_INCOMPLETE;
-    }
+// http_request_validate()
+// returns:
+// = 0 : all good, process the request
+// > 0 : request is not supported
+// < 0 : request is incomplete - wait for more data
 
-    if ( length > 3  ){
-        begin = end - 4;
-    }
+typedef enum {
+    HTTP_VALIDATION_OK,
+    HTTP_VALIDATION_NOT_SUPPORTED,
+#ifdef ENABLE_HTTPS
+    HTTP_VALIDATION_INCOMPLETE,
+    HTTP_VALIDATION_REDIRECT
+#else
+    HTTP_VALIDATION_INCOMPLETE
+#endif
+} HTTP_VALIDATION;
 
-    uint32_t counter = 0;
-    do{
-        if (*begin == '\r'){
-            begin++;
-            if ( begin == end )
-            {
-                break;
-            }
+static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
+    char *s = (char *)buffer_tostring(w->response.data), *encoded_url = NULL;
+
+    size_t last_pos = w->header_parse_last_size;
+    if(last_pos > 4) last_pos -= 4; // allow searching for \r\n\r\n
+    else last_pos = 0;
 
-            if (*begin == '\n')
-            {
-                counter++;
+    w->header_parse_tries++;
+    w->header_parse_last_size = buffer_strlen(w->response.data);
+
+    if(w->header_parse_tries > 1) {
+        if(w->header_parse_last_size < last_pos)
+            last_pos = 0;
+
+        if(strstr(&s[last_pos], "\r\n\r\n") == NULL) {
+            if(w->header_parse_tries > 10) {
+                info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
+                w->header_parse_tries = 0;
+                w->header_parse_last_size = 0;
+                web_client_disable_wait_receive(w);
+                return HTTP_VALIDATION_NOT_SUPPORTED;
             }
-        } else if (*begin == '\n') {
-            begin++;
-            counter++;
-        }
 
-        if ( counter == 2){
-            break;
+            return HTTP_VALIDATION_INCOMPLETE;
         }
     }
-    while(begin != end);
 
-    return (counter == 2)?HTTP_VALIDATION_OK:HTTP_VALIDATION_INCOMPLETE;
-}
-
-static inline char *web_client_parse_method(struct web_client *w,char *s) {
+    // is is a valid request?
     if(!strncmp(s, "GET ", 4)) {
-        s = &s[4];
+        encoded_url = s = &s[4];
         w->mode = WEB_CLIENT_MODE_NORMAL;
     }
     else if(!strncmp(s, "OPTIONS ", 8)) {
-        s = &s[8];
+        encoded_url = s = &s[8];
         w->mode = WEB_CLIENT_MODE_OPTIONS;
     }
     else if(!strncmp(s, "STREAM ", 7)) {
 #ifdef ENABLE_HTTPS
-        if ((w->ssl.flags) && (netdata_use_ssl_on_stream & NETDATA_SSL_FORCE)) {
+        if ( (w->ssl.flags) && (netdata_use_ssl_on_stream & NETDATA_SSL_FORCE)){
             w->header_parse_tries = 0;
             w->header_parse_last_size = 0;
             web_client_disable_wait_receive(w);
             char hostname[256];
             char *copyme = strstr(s,"hostname=");
-            if (copyme) {
+            if ( copyme ){
                 copyme += 9;
                 char *end = strchr(copyme,'&');
-                if(end) {
+                if(end){
                     size_t length = end - copyme;
                     memcpy(hostname,copyme,length);
                     hostname[length] = 0X00;
-                } else {
+                }
+                else{
                     memcpy(hostname,"not available",13);
                     hostname[13] = 0x00;
                 }
@@ -893,205 +891,30 @@ static inline char *web_client_parse_method(struct web_client *w,char *s) {
                 hostname[13] = 0x00;
             }
             error("The server is configured to always use encrypt connection, please enable the SSL on slave with hostname '%s'.",hostname);
-            return NULL;
+            return HTTP_VALIDATION_NOT_SUPPORTED;
         }
 #endif
-        s = &s[7];
+
+        encoded_url = s = &s[7];
         w->mode = WEB_CLIENT_MODE_STREAM;
     }
     else {
-        s = NULL;
-    }
-
-    return s;
-}
-
-static inline char *web_client_find_protocol(struct web_client *w,char *s) {
-    s = url_find_protocol(s);
-
-    w->protocol.body = s+1;
-    char *end = strchr(s+6,'\n');
-    if (end) {
-        w->protocol.length = end - w->protocol.body ;
-    }
-
-    return s;
-}
-
-static inline void web_client_parse_headers(struct web_client *w,char *s) {
-    while(*s) {
-        // find a line feed
-        while(*s && *s++ != '\r');
-
-        // did we reach the end?
-        if(unlikely(!*s)) break;
-
-        if (*s == '\n') {
-            s++;
-        }
-
-        s = http_header_parse(w, s,
-                              (w->mode == WEB_CLIENT_MODE_STREAM) // parse user agent
-        );
-
-    }
-}
-
-int web_client_parse_request(struct web_client *w,char *divisor) {
-    if(!divisor) {
-        w->total_params = 0;
-        return 0;
-    }
-
-    uint32_t i = url_parse_query_string(w->param_name,w->param_values,w->query_string.body+1,divisor);
-    w->total_params = i;
-
-    return i;
-}
-
-static inline void web_client_set_directory(struct web_client *w,char *begin,char *enddir,char *endcmd) {
-    if (enddir) {
-        w->directory.body = begin;
-        w->directory.length = enddir - begin;
-
-        if (!strncmp(w->directory.body,"api",w->directory.length)) {
-            begin = enddir + 1;
-            enddir = strchr(begin,'/');
-            if(enddir) {
-                w->version.body = begin;
-                w->version.length = enddir - begin;
-
-                enddir++;
-                w->command.body = enddir;
-                w->command.length = (size_t) (endcmd - enddir);
-            }
-        }
-    }
-    else{
-        w->directory.body = begin;
-        w->directory.length = w->path.length - 1;
-        w->version.body = NULL;
-        w->version.length = 0;
-        w->command.body = NULL;
-        w->command.length = 0;
-    }
-}
-
-static inline void web_client_set_without_query_string(struct web_client *w) {
-    w->query_string.body = NULL;
-    w->query_string.length = 0;
-
-    char *test = w->path.body+1;
-    if (!strncmp(test,"api/v1/",7) ) {
-        test += 7;
-        if (!strncmp(test,"info",4)) {
-            w->command.length = 4;
-        }
-        else if (!strncmp(test,"charts",6)) {
-            w->command.length = 6;
-        }
-        else {
-            test = NULL;
-            w->command.length = 0;
-        }
-    }else{
-        w->command.length = w->path.length;
-    }
-    w->command.body = test;
-    w->total_params = 0;
-}
-
-static inline void web_client_split_path_query(struct web_client *w) {
-    w->path.body = w->decoded_url;
-    w->decoded_length = strlen(w->decoded_url);
-    char *moveme = strchr(w->path.body,'?');
-    char *enddir;
-    if (moveme) {
-        w->path.length = moveme - w->path.body;
-        w->query_string.body = moveme;
-        w->query_string.length = w->decoded_length - w->path.length;
-
-        enddir = strchr(w->path.body+1,'/');
-        char *begin = w->path.body+1;
-        web_client_set_directory(w,begin,enddir,moveme);
-        if (w->query_string.body) {
-            enddir = strchr(moveme,'=');
-            if (!web_client_parse_request(w,enddir) ) {
-                moveme++;
-                size_t length = strlen(moveme);
-                w->param_name[0].body = moveme;
-                w->param_name[0].length = length;
-                w->param_values[0].body = moveme;
-                w->param_values[0].length = length;
-
-                w->total_params = 1;
-            }
-        }
-    } else {
-        w->path.length = w->decoded_length;
-
-        enddir = strchr(w->path.body+1,'/');
-        w->directory.body = w->path.body + 1;
-        if(enddir) {
-            w->directory.length = (size_t)(enddir - w->directory.body);
-            enddir++;
-
-            w->version.body = enddir;
-            enddir = strchr(++enddir,'/');
-            if(enddir) {
-                w->version.length = (size_t)(enddir - w->version.body);
-
-                enddir++;
-                w->command.body = enddir;
-                w->command.length = (size_t)(moveme - enddir);
-            } else{
-                w->version.length = strlen(w->version.body);
-            }
-
-        }else {
-            w->directory.length = w->decoded_length - 1;
-        }
-        web_client_set_without_query_string(w);
-    }
-}
-
-static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
-    char *s = (char *)buffer_tostring(w->response.data), *encoded_url = NULL;
-    size_t status;
-
-    w->header_parse_tries++;
-    w->header_parse_last_size = buffer_strlen(w->response.data);
-    status = w->header_parse_last_size;
-
-    // make sure we have complete request
-    // complete requests contain: \r\n\r\n
-    status = url_is_request_complete(s,&s[status],status);
-    if (w->header_parse_tries > 10) {
-        if (status == HTTP_VALIDATION_INCOMPLETE) {
-            info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
-            w->header_parse_tries = 0;
-            w->header_parse_last_size = 0;
-            web_client_disable_wait_receive(w);
-            return HTTP_VALIDATION_NOT_SUPPORTED;
-        }
-    } else{
-        if (status == HTTP_VALIDATION_INCOMPLETE) {
-            web_client_enable_wait_receive(w);
-            return HTTP_VALIDATION_INCOMPLETE;
-        }
-    }
-    //Parse the method used to communicate
-    s = web_client_parse_method(w,s);
-    if (!s) {
         w->header_parse_tries = 0;
         w->header_parse_last_size = 0;
         web_client_disable_wait_receive(w);
         return HTTP_VALIDATION_NOT_SUPPORTED;
     }
 
-    encoded_url = s;
+    // find the SPACE + "HTTP/"
+    while(*s) {
+        // find the next space
+        while (*s && *s != ' ') s++;
+
+        // is it SPACE + "HTTP/" ?
+        if(*s && !strncmp(s, " HTTP/", 6)) break;
+        else s++;
+    }
 
-    s = web_client_find_protocol(w,s);
     // incomplete requests
     if(unlikely(!*s)) {
         web_client_enable_wait_receive(w);
@@ -1101,40 +924,64 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
     // we have the end of encoded_url - remember it
     char *ue = s;
 
-    *ue = '\0';
-    url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1);
+    // make sure we have complete request
+    // complete requests contain: \r\n\r\n
+    while(*s) {
+        // find a line feed
+        while(*s && *s++ != '\r');
+
+        // did we reach the end?
+        if(unlikely(!*s)) break;
 
-    web_client_split_path_query(w);
-    *ue = ' ';
-    web_client_parse_headers(w,s);
+        // is it \r\n ?
+        if(likely(*s++ == '\n')) {
 
-    // copy the URL - we are going to overwrite parts of it
-    // TODO -- ideally we we should avoid copying buffers around
-    strncpyz(w->last_url, w->decoded_url, NETDATA_WEB_REQUEST_URL_SIZE);
+            // is it again \r\n ? (header end)
+            if(unlikely(*s == '\r' && s[1] == '\n')) {
+                // a valid complete HTTP request found
 
+                *ue = '\0';
+                url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1);
+                *ue = ' ';
+                
+                // copy the URL - we are going to overwrite parts of it
+                // TODO -- ideally we we should avoid copying buffers around
+                strncpyz(w->last_url, w->decoded_url, NETDATA_WEB_REQUEST_URL_SIZE);
 #ifdef ENABLE_HTTPS
-    if ((!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
-        if ((w->ssl.conn) && ((w->ssl.flags & NETDATA_SSL_NO_HANDSHAKE) && (netdata_use_ssl_on_http & NETDATA_SSL_FORCE) && (w->mode != WEB_CLIENT_MODE_STREAM)) ) {
-            w->header_parse_tries = 0;
-            w->header_parse_last_size = 0;
-            web_client_disable_wait_receive(w);
-            return HTTP_VALIDATION_REDIRECT;
+                if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
+                    if ((w->ssl.conn) && ((w->ssl.flags & NETDATA_SSL_NO_HANDSHAKE) && (netdata_use_ssl_on_http & NETDATA_SSL_FORCE) && (w->mode != WEB_CLIENT_MODE_STREAM))  ) {
+                        w->header_parse_tries = 0;
+                        w->header_parse_last_size = 0;
+                        web_client_disable_wait_receive(w);
+                        return HTTP_VALIDATION_REDIRECT;
+                    }
+                }
+#endif
+
+                w->header_parse_tries = 0;
+                w->header_parse_last_size = 0;
+                web_client_disable_wait_receive(w);
+                return HTTP_VALIDATION_OK;
+            }
+
+            // another header line
+            s = http_header_parse(w, s,
+                    (w->mode == WEB_CLIENT_MODE_STREAM) // parse user agent
+            );
         }
     }
-#endif
 
-    w->header_parse_tries = 0;
-    w->header_parse_last_size = 0;
-    web_client_disable_wait_receive(w);
-    return HTTP_VALIDATION_OK;
+    // incomplete request
+    web_client_enable_wait_receive(w);
+    return HTTP_VALIDATION_INCOMPLETE;
 }
 
 static inline ssize_t web_client_send_data(struct web_client *w,const void *buf,size_t len, int flags)
 {
     ssize_t bytes;
 #ifdef ENABLE_HTTPS
-    if ((!web_client_check_unix(w)) && (netdata_srv_ctx)) {
-        if ((w->ssl.conn) && (!w->ssl.flags)) {
+    if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
+        if ( ( w->ssl.conn ) && ( !w->ssl.flags ) ){
             bytes = SSL_write(w->ssl.conn,buf, len) ;
         } else {
             bytes = send(w->ofd,buf, len , flags);
@@ -1255,7 +1102,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
         buffer_sprintf(w->response.header_output,
                 "Cache-Control: %s\r\n"
                         "Expires: %s\r\n",
-                (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
+                (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache, no-store, must-revalidate\r\nPragma: no-cache":"public",
                 edate);
     }
 
@@ -1296,8 +1143,8 @@ static inline void web_client_send_http_header(struct web_client *w) {
     size_t count = 0;
     ssize_t bytes;
 #ifdef ENABLE_HTTPS
-    if ((!web_client_check_unix(w)) && (netdata_srv_ctx)) {
-           if ((w->ssl.conn) && (!w->ssl.flags)) {
+    if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
+           if ( ( w->ssl.conn ) && ( !w->ssl.flags ) ){
                 while((bytes = SSL_write(w->ssl.conn, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output))) < 0) {
                     count++;
                     if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
@@ -1366,52 +1213,21 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
         return 400;
     }
 
-    char *tok = strchr(url,'/');
-    if (tok) {
-        w->switch_host = 1;
-        debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, url);
+    char *tok = mystrsep(&url, "/");
+    if(tok && *tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
 
         // copy the URL, we need it to serve files
         w->last_url[0] = '/';
-        if(*(tok+1) != ' ') {
-            strncpyz(&w->last_url[1], tok+1, NETDATA_WEB_REQUEST_URL_SIZE - 1);
-            char *enddir;
-            if (w->total_params) {
-                enddir = strchr(tok+1,'/');
-                if (enddir) {
-                    char *moveme = strchr(enddir,'?');
-                    if (moveme) {
-                        web_client_set_directory(w,tok+1,enddir,moveme);
-                    }
-                }
-                else {
-                    w->directory.body = tok;
-                    w->directory.length = strlen(tok);
-                }
-            } else {
-                enddir = strchr(tok+1,'/');
-                if(enddir) {
-                    w->directory.body = tok + 1;
-                    w->directory.length = enddir - tok - 1;
-                } else {
-                    if (!strlen(tok + 1)) {
-                        w->directory.body = tok;
-                        w->directory.length = 1;
-                    }
-                }
-            }
-        }
-        else {
-            w->last_url[1] = '\0';
-        }
-        *tok = 0x00;
-        uint32_t hash = simple_hash(url);
+        if(url && *url) strncpyz(&w->last_url[1], url, NETDATA_WEB_REQUEST_URL_SIZE - 1);
+        else w->last_url[1] = '\0';
+
+        uint32_t hash = simple_hash(tok);
 
-        host = rrdhost_find_by_hostname(url, hash);
-        if(!host) host = rrdhost_find_by_guid(url, hash);
-        *tok = '/';
+        host = rrdhost_find_by_hostname(tok, hash);
+        if(!host) host = rrdhost_find_by_guid(tok, hash);
 
-        if(host) return web_client_process_url(host, w, tok);
+        if(host) return web_client_process_url(host, w, url);
     }
 
     buffer_flush(w->response.data);
@@ -1442,21 +1258,20 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
 #endif
     }
 
-    if (w->path.length > 1) {
-        char *cmp = w->directory.body;
-        size_t len = w->directory.length;
-        uint32_t hash = simple_nhash(cmp,len);
-        debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, w->command.body);
+    char *tok = mystrsep(&url, "/?");
+    if(likely(tok && *tok)) {
+        uint32_t hash = simple_hash(tok);
+        debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
 
-        if(unlikely(hash == hash_api && strncmp(cmp, "api",len) == 0)) {                           // current API
+        if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) {                           // current API
             debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
             return check_host_and_call(host, w, url, web_client_api_request);
         }
-        else if(unlikely(hash == hash_host && strncmp(cmp, "host",len) == 0)) {                    // host switching
+        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, cmp+5);
+            return web_client_switch_host(host, w, url);
         }
-        else if(unlikely(hash == hash_netdata_conf && strncmp(cmp, "netdata.conf",len) == 0)) {                           // current API
+        else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) {    // netdata.conf
             if(unlikely(!web_client_can_access_netdataconf(w)))
                 return web_client_permission_denied(w);
 
@@ -1467,7 +1282,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
             return 200;
         }
 #ifdef NETDATA_INTERNAL_CHECKS
-        else if(unlikely(hash == hash_exit && strncmp(cmp, "exit",len) == 0)) {
+        else if(unlikely(hash == hash_exit && strcmp(tok, "exit") == 0)) {
             if(unlikely(!web_client_can_access_netdataconf(w)))
                 return web_client_permission_denied(w);
 
@@ -1483,50 +1298,47 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
             netdata_cleanup_and_exit(0);
             return 200;
         }
-        else if(unlikely(hash == hash_debug && strncmp(cmp, "debug",len) == 0)) {
+        else if(unlikely(hash == hash_debug && strcmp(tok, "debug") == 0)) {
             if(unlikely(!web_client_can_access_netdataconf(w)))
                 return web_client_permission_denied(w);
 
             buffer_flush(w->response.data);
 
             // get the name of the data to show
-            char *tok = mystrsep(&url, "/?");
-            if(likely(tok && *tok)) {
-                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) {
-                        w->response.data->contenttype = CT_TEXT_HTML;
-                        buffer_strcat(w->response.data, "Chart is not found: ");
-                        buffer_strcat_htmlescape(w->response.data, tok);
-                        debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
-                        return 404;
-                    }
-
-                    debug_flags |= D_RRD_STATS;
-
-                    if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))
-                        rrdset_flag_clear(st, RRDSET_FLAG_DEBUG);
-                    else
-                        rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
-
+            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) {
                     w->response.data->contenttype = CT_TEXT_HTML;
-                    buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
+                    buffer_strcat(w->response.data, "Chart is not found: ");
                     buffer_strcat_htmlescape(w->response.data, tok);
-                    debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
-                    return 200;
+                    debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
+                    return 404;
                 }
+
+                debug_flags |= D_RRD_STATS;
+
+                if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))
+                    rrdset_flag_clear(st, RRDSET_FLAG_DEBUG);
+                else
+                    rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
+
+                w->response.data->contenttype = CT_TEXT_HTML;
+                buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
+                buffer_strcat_htmlescape(w->response.data, tok);
+                debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
+                return 200;
             }
 
             buffer_flush(w->response.data);
             buffer_strcat(w->response.data, "debug which chart?\r\n");
             return 400;
         }
-        else if(unlikely(hash == hash_mirror && strncmp(cmp, "mirror",len) == 0)) {
+        else if(unlikely(hash == hash_mirror && strcmp(tok, "mirror") == 0)) {
             if(unlikely(!web_client_can_access_netdataconf(w)))
                 return web_client_permission_denied(w);
 
@@ -1543,17 +1355,12 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
 #endif  /* NETDATA_INTERNAL_CHECKS */
     }
 
-    w->switch_host = 0;
-    char *tok = mystrsep(&url, "/?");
-    (void)tok;
-
     char filename[FILENAME_MAX+1];
     url = filename;
     strncpyz(filename, w->last_url, FILENAME_MAX);
     tok = mystrsep(&url, "?");
     buffer_flush(w->response.data);
-
-    return mysendfile(w, (w->path.length > 1)?tok:"/");
+    return mysendfile(w, (tok && *tok)?tok:"/");
 }
 
 void web_client_process_request(struct web_client *w) {
@@ -2005,8 +1812,8 @@ ssize_t web_client_receive(struct web_client *w)
     buffer_need_bytes(w->response.data, NETDATA_WEB_REQUEST_RECEIVE_SIZE);
 
 #ifdef ENABLE_HTTPS
-    if ((!web_client_check_unix(w)) && (netdata_srv_ctx)) {
-        if ((w->ssl.conn) && (!w->ssl.flags)) {
+    if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
+        if ( ( w->ssl.conn ) && (!w->ssl.flags)) {
             bytes = SSL_read(w->ssl.conn, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
         }else {
             bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);

+ 0 - 12
web/server/web_client.h

@@ -128,18 +128,6 @@ struct web_client {
     char client_port[NI_MAXSERV+1];
 
     char decoded_url[NETDATA_WEB_REQUEST_URL_SIZE + 1];  // we decode the URL in this buffer
-    size_t decoded_length;
-    struct web_fields path;
-    struct web_fields directory;
-    struct web_fields query_string;
-    struct web_fields version;
-    struct web_fields command;
-    struct web_fields protocol;
-    struct web_fields param_name[WEB_FIELDS_MAX];
-    struct web_fields param_values[WEB_FIELDS_MAX];
-    uint32_t total_params;
-    int switch_host;
-
     char last_url[NETDATA_WEB_REQUEST_URL_SIZE+1];       // we keep a copy of the decoded URL here
     char host[256];