@cryptotaxi247 / netdata-1 / commits / 58b7d95a7

New URL parser (#6070)

* URL_parser 3 * URL_parser rebase 2! * URL_parameter parsing 3 * URL_parameter parsing 4 * URL_parameter parsing 5 * URL_parser alarms * URL_parser finish the basic structure * URL_parser codacity fixes! * URL_parser scripts! * URL_parser codacy! * URL_parser rebase 3! * URL_parser host fixes! * URL_parser host fixes 2! * URL_parser fix spaces! * URL_parser error message! * URL_parser Christopher requests! * URL_parser alarms fixed! * URL_parser health fixed! * URL_parser rebase 4! * URL_parser C fix write format! * URL_parser fix bugs due cache!

thiagoftsm committed Jun 6, 2019 at 17:01 UTC 58b7d95a7ec9c576f8a06bbab07f755846b5349a
10 files changed +1056 -444
libnetdata/inlined.h
+28
@@ -31,6 +31,19 @@ static inline uint32_t simple_hash(const char *name) {
31 return hval;
32 }
33
34 +static inline uint32_t simple_nhash(const char *name,size_t len) {
35 + unsigned char *s = (unsigned char *) name;
36 + size_t i;
37 + uint32_t hval = 0x811c9dc5;
38 + i = 0;
39 + do {
40 + hval *= 16777619;
41 + hval ^= (uint32_t) *s++;
42 + } while (++i < len);
43 +
44 + return hval;
45 +}
46 +
47 static inline uint32_t simple_uhash(const char *name) {
48 unsigned char *s = (unsigned char *) name;
49 uint32_t hval = 0x811c9dc5, c;
@@ -42,6 +55,21 @@ static inline uint32_t simple_uhash(const char *name) {
55 return hval;
56 }
57
58 +static inline uint32_t simple_nuhash(const char *name,size_t len) {
59 + unsigned char *s = (unsigned char *) name;
60 + size_t i;
61 + uint32_t hval = 0x811c9dc5, c;
62 +
63 + i = 0;
64 + do {
65 + c = *s++;
66 + if (unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A';
67 + hval *= 16777619;
68 + hval ^= c;
69 + } while ( ++i < len);
70 + return hval;
71 +}
72 +
73 static inline int simple_hash_strcmp(const char *name, const char *b, uint32_t *hash) {
74 unsigned char *s = (unsigned char *) name;
75 uint32_t hval = 0x811c9dc5;
libnetdata/url/url.c
+85
@@ -79,3 +79,88 @@ char *url_decode_r(char *to, char *url, size_t size) {
79
80 return to;
81 }
82 +
83 +inline HTTP_VALIDATION url_is_request_complete(char *begin,char *end,size_t length) {
84 + if ( begin == end) {
85 + return HTTP_VALIDATION_INCOMPLETE;
86 + }
87 +
88 + if ( length > 3 ) {
89 + begin = end - 4;
90 + }
91 +
92 + uint32_t counter = 0;
93 + do {
94 + if (*begin == '\r') {
95 + begin++;
96 + if ( begin == end )
97 + {
98 + break;
99 + }
100 +
101 + if (*begin == '\n')
102 + {
103 + counter++;
104 + }
105 + } else if (*begin == '\n') {
106 + begin++;
107 + counter++;
108 + }
109 +
110 + if ( counter == 2) {
111 + break;
112 + }
113 + }
114 + while (begin != end);
115 +
116 + return (counter == 2)?HTTP_VALIDATION_OK:HTTP_VALIDATION_INCOMPLETE;
117 +}
118 +
119 +inline char *url_find_protocol(char *s) {
120 + while(*s) {
121 + // find the next space
122 + while (*s && *s != ' ') s++;
123 +
124 + // is it SPACE + "HTTP/" ?
125 + if(*s && !strncmp(s, " HTTP/", 6)) break;
126 + else s++;
127 + }
128 +
129 + return s;
130 +}
131 +
132 +int url_parse_query_string(struct web_fields *names,struct web_fields *values,char *moveme,char *divisor) {
133 + uint32_t i = 0;
134 + uint32_t max = WEB_FIELDS_MAX;
135 +
136 + do {
137 + if ( i == max) {
138 + error("We are exceeding the maximum number of elements possible(%u) in this query string(%s)",max,moveme);
139 + break;
140 + }
141 + if (divisor) {
142 + names[i].body = moveme;
143 + names[i].length = divisor - moveme;//= - begin
144 +
145 + moveme = ++divisor; //value
146 + values[i].body = moveme;
147 +
148 + (void)divisor;
149 + divisor = strchr(moveme,'&'); //end of value
150 + if (divisor) {
151 + values[i].length = (size_t )(divisor - moveme);
152 + } else{
153 + values[i].length = strlen(moveme);
154 + break;
155 + }
156 +
157 + moveme = divisor;
158 + divisor = strchr(++moveme,'='); //end of value
159 + i++;
160 + } else {
161 + break;
162 + }
163 + } while (moveme);
164 +
165 + return ++i;
166 +}
libnetdata/url/url.h
+22
@@ -25,4 +25,26 @@ extern char *url_decode(char *str);
25
26 extern char *url_decode_r(char *to, char *url, size_t size);
27
28 +#define WEB_FIELDS_MAX 200
29 +struct web_fields{
30 + char *body;
31 + size_t length;
32 +};
33 +// http_request_validate()
34 +// returns:
35 +// = 0 : all good, process the request
36 +// > 0 : request is not supported
37 +// < 0 : request is incomplete - wait for more data
38 +
39 +typedef enum {
40 + HTTP_VALIDATION_OK,
41 + HTTP_VALIDATION_NOT_SUPPORTED,
42 + HTTP_VALIDATION_INCOMPLETE,
43 + HTTP_VALIDATION_REDIRECT
44 +} HTTP_VALIDATION;
45 +
46 +extern HTTP_VALIDATION url_is_request_complete(char *begin,char *end,size_t length);
47 +extern char *url_find_protocol(char *s);
48 +extern int url_parse_query_string(struct web_fields *names,struct web_fields *values,char *moveme,char *divisor);
49 +
50 #endif /* NETDATA_URL_H */
tests/urls/requests.sh new
+207
@@ -0,0 +1,207 @@
1 +#!/bin/bash
2 +# SPDX-License-Identifier: GPL-3.0-or-later
3 +
4 +################################################################################################
5 +#### ####
6 +#### GLOBAL VARIABLES ####
7 +#### ####
8 +################################################################################################
9 +
10 +NETDATA_VARLIB_DIR="@varlibdir_POST@"
11 +
12 +#CT=`date +'%s%N' |cut -b1-13`
13 +# The current time
14 +CT=`date +'%s'`
15 +
16 +# The previous time
17 +PT=$(( $CT - 100))
18 +
19 +# The curl options used to do download
20 +CURLOPTS="-v --create-dirs -o"
21 +
22 +# The output directory where we will store the results and error
23 +OUTDIR="tests"
24 +
25 +################################################################################################
26 +#### ####
27 +#### FUNCTIONS ####
28 +#### ####
29 +################################################################################################
30 +
31 +# Print error message and close script
32 +netdata_print_error(){
33 + echo "Closing due error \"$1\" code \"$2\""
34 + exit 1
35 +}
36 +
37 +# Print the header message of the function
38 +netdata_print_header() {
39 + echo "$1"
40 +}
41 +
42 +# Create the main directory where the results will be stored
43 +netdata_create_directory() {
44 + netdata_print_header "Creating directory $1"
45 + if [ ! -d $1 ]; then
46 + mkdir $1
47 + if [ $? -ne 0 ]; then
48 + netdata_print_error "Cannot create directory" $?
49 + fi
50 + else
51 + echo "Working with directory $OUTDIR"
52 + fi
53 +}
54 +
55 +netdata_test_download(){
56 + grep "HTTP/1.1 200 OK" $1 2>/dev/null 1>/dev/null
57 + if [ $? -ne 0 ]; then
58 + netdata_print_error "Cannot do download of the page $2" $?
59 + fi
60 +}
61 +
62 +# Download information from Netdata
63 +netdata_download_various() {
64 + netdata_print_header "Getting $2"
65 + curl $CURLOPTS $OUTDIR/$3.out "$1/$2" 2> $OUTDIR/$3.err
66 + netdata_test_download $OUTDIR/$3.err "$1/$2"
67 +}
68 +
69 +# Download charts from Netdata
70 +netdata_download_charts() {
71 + curl $CURLOPTS $OUTDIR/charts.out "$1/$2/charts" 2> $OUTDIR/charts.err
72 + netdata_test_download $OUTDIR/charts.err "$1/$2/charts"
73 +
74 + #Rewrite the next
75 + cat tests/charts.out | grep -w "id"| cut -d: -f2 | grep "\"," | sed s/,//g | sort
76 +}
77 +
78 +#Test options for a specific chart
79 +netdata_download_chart() {
80 + NAME=`echo $3| sed s/\"//g`
81 + netdata_print_header "Getting data for $NAME using $4"
82 +
83 + LDIR=$OUTDIR"/"$4
84 +
85 + LURL=$1/$2=$NAME
86 +
87 + NAME=$NAME"_$4"
88 +
89 + curl $CURLOPTS $LDIR/$NAME.out "$LURL" 2> $LDIR/$NAME.err
90 + netdata_test_download $LDIR/$NAME.err $LURL
91 +
92 + UFILES=( "points" "before" "after" )
93 + COUNTER=0
94 + for OPT in "&points=100" "&before=$PT" "&after=$CT" ;
95 + do
96 + LURL="$LURL$OPT"
97 + LFILE=$NAME"_${UFILES[$COUNTER]}";
98 +
99 + curl $CURLOPTS "$LDIR/$LFILE.out" "$LURL" 2> "$LDIR/$LFILE.err"
100 + netdata_test_download $LDIR/$LFILE.err $LURL
101 +
102 + COUNTER=$(($COUNTER + 1))
103 + done
104 +
105 + LURL="$LURL&group="
106 + for OPT in "min" "max" "sum" "median" "stddev" "cv" "ses" "des" "incremental_sum" "average";
107 + do
108 + TURL=$LURL$OPT
109 + TFILE=$NAME"_$OPT";
110 + curl $CURLOPTS "$LDIR/$TFILE.out" "$TURL" 2> "$LDIR/$TFILE.err"
111 + netdata_test_download $LDIR/$TFILE.err $TURL
112 + for MORE in "jsonp" "json" "ssv" "csv" "datatable" "datasource" "tsv" "ssvcomma" "html" "array";
113 + do
114 + TURL=$TURL"&format="$MORE
115 + TFILE=$NAME"_$OPT""_$MORE";
116 + curl $CURLOPTS "$LDIR/$TFILE.out" "$TURL" 2> "$LDIR/$TFILE.err"
117 + netdata_test_download $LDIR/$TFILE.err $TURL
118 + done
119 +
120 + done
121 +
122 + LURL="$LURL$OPT&gtime=60"
123 + NFILE=$NAME"_gtime"
124 + curl $CURLOPTS "$LDIR/$NFILE.out" "$TURL" 2> "$LDIR/$NFILE.err"
125 + netdata_test_download $LDIR/$NFILE.err $LURL
126 +
127 + LURL="$LURL$OPT&options=percentage"
128 + NFILE=$NAME"_percentage"
129 + curl $CURLOPTS "$LDIR/$NFILE.out" "$TURL" 2> "$LDIR/$NFILE.err"
130 + netdata_test_download $LDIR/$NFILE.err $LURL
131 +
132 + LURL="$LURL$OPT&options=percentage"
133 + NFILE=$NAME"_percentage"
134 + curl $CURLOPTS "$LDIR/$NFILE.out" "$TURL" 2> "$LDIR/$NFILE.err"
135 + netdata_test_download $LDIR/$NFILE.err $LURL
136 +
137 + LURL="$LURL$OPT&dimensions=system%7Cnice"
138 + NFILE=$NAME"_dimension"
139 + curl $CURLOPTS "$LDIR/$NFILE.out" "$TURL" 2> "$LDIR/$NFILE.err"
140 + netdata_test_download $LDIR/$NFILE.err $LURL
141 +
142 +}
143 +
144 +# Download information from Netdata
145 +netdata_download_allmetrics() {
146 + netdata_print_header "Getting All metrics"
147 + curl $CURLOPTS $OUTDIR/allmetrics.out "$1/$2" 2> $OUTDIR/allmetrics.err
148 + netdata_test_download $OUTDIR/allmetrics.err "$1/$2"
149 +}
150 +
151 +# Download charts from Netdata
152 +
153 +################################################################################################
154 +#### ####
155 +#### MAIN ROUTINE ####
156 +#### ####
157 +################################################################################################
158 +MURL="http://127.0.0.1:19999"
159 +
160 +wget --execute="robots = off" --mirror --convert-links --no-parent http://127.0.0.1:19999
161 +
162 +netdata_create_directory $OUTDIR
163 +
164 +netdata_download_various $MURL "netdata.conf" "netdata.conf"
165 +
166 +netdata_download_various $MURL "api/v1/info" "info"
167 +
168 +netdata_download_various $MURL "api/v1/registry?action=hello" "action"
169 +
170 +netdata_print_header "Getting all the netdata charts"
171 +CHARTS=$( netdata_download_charts "http://127.0.0.1:19999" "api/v1" )
172 +
173 +netdata_download_various $MURL "api/v1/allmetrics?format=json" "allmetrics"
174 +
175 +netdata_download_various $MURL "api/v1/alarms?all" "alarms_all"
176 +
177 +netdata_download_various $MURL "api/v1/alarms?active" "alarms_active"
178 +
179 +netdata_download_various $MURL "api/v1/alarm_log?after&_=$PT" "alarm_log"
180 +
181 +for I in $CHARTS ; do
182 + NAME=`echo $I| sed s/\"//g`
183 + netdata_download_various $MURL "api/v1/alarm_variables?chart=$NAME" "alarm_variables_$NAME"
184 +done
185 +
186 +netdata_create_directory "$OUTDIR/data"
187 +for I in $CHARTS ; do
188 + netdata_download_chart $MURL "api/v1/data?chart" $I "data"
189 +done
190 +
191 +netdata_create_directory "$OUTDIR/badge.svg"
192 +for I in $CHARTS ; do
193 + netdata_download_chart $MURL "api/v1/badge.svg?chart" $I "badge.svg"
194 +done
195 +
196 +if [ -f "${NETDATA_VARLIB_DIR}/netdata.api.key" ] ;then
197 + read -r CORRECT_TOKEN < "${NETDATA_VARLIB_DIR}/netdata.api.key"
198 +else
199 + echo "${NETDATA_VARLIB_DIR}/netdata.api.key not found"
200 + echo "Token not found."
201 + exit 2
202 +fi
203 +curl -H "X-Auth-Token: $TOKEN" "http://127.0.0.1:19999/api/v1/manage/health?cmd=RESET"
204 +
205 +echo "ALL the URLS got 200 as answer!"
206 +
207 +exit 0
web/api/badges/web_buffer_svg.c
+57 -40
@@ -889,6 +889,7 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
889 }
890
891 int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
892 + (void)url;
893 int ret = 400;
894 buffer_flush(w->response.data);
895
@@ -912,46 +913,54 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
913 int group = RRDR_GROUPING_AVERAGE;
914 uint32_t options = 0x00000000;
915
915 - while(url) {
916 - char *value = mystrsep(&url, "&");
917 - if(!value || !*value) continue;
918 -
919 - char *name = mystrsep(&value, "=");
920 - if(!name || !*name) continue;
921 - if(!value || !*value) continue;
922 -
923 - debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
924 -
925 - // name and value are now the parameters
926 - // they are not null and not empty
927 -
928 - if(!strcmp(name, "chart")) chart = value;
929 - else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
930 - if(!dimensions)
931 - dimensions = buffer_create(100);
932 -
933 - buffer_strcat(dimensions, "|");
934 - buffer_strcat(dimensions, value);
935 - }
936 - else if(!strcmp(name, "after")) after_str = value;
937 - else if(!strcmp(name, "before")) before_str = value;
938 - else if(!strcmp(name, "points")) points_str = value;
939 - else if(!strcmp(name, "group")) {
940 - group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
941 - }
942 - else if(!strcmp(name, "options")) {
943 - options |= web_client_api_request_v1_data_options(value);
944 - }
945 - else if(!strcmp(name, "label")) label = value;
946 - else if(!strcmp(name, "units")) units = value;
947 - else if(!strcmp(name, "label_color")) label_color = value;
948 - else if(!strcmp(name, "value_color")) value_color = value;
949 - else if(!strcmp(name, "multiply")) multiply_str = value;
950 - else if(!strcmp(name, "divide")) divide_str = value;
951 - else if(!strcmp(name, "refresh")) refresh_str = value;
952 - else if(!strcmp(name, "precision")) precision_str = value;
953 - else if(!strcmp(name, "scale")) scale_str = value;
954 - else if(!strcmp(name, "alarm")) alarm = value;
916 + uint32_t i = 0;
917 + uint32_t end = w->total_params;
918 + char save[WEB_FIELDS_MAX];
919 + char *value;
920 + size_t lvalue;
921 + if(end) {
922 + do {
923 + value = w->param_values[i].body;
924 + lvalue = w->param_values[i].length;
925 + save[i] = value[lvalue];
926 + value[lvalue] = 0x00;
927 +
928 + char *name = w->param_name[i].body;
929 + size_t lname = w->param_name[i].length;
930 +
931 + debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
932 +
933 + // name and value are now the parameters
934 + // they are not null and not empty
935 + if(!strncmp(name, "chart",lname)) chart = value;
936 + else if(!strncmp(name, "dimension",lname) || !strncmp(name, "dim",lname) || !strncmp(name, "dimensions",lname) || !strncmp(name, "dims",lname)) {
937 + if(!dimensions)
938 + dimensions = buffer_create(100);
939 +
940 + buffer_strcat(dimensions, "|");
941 + buffer_strcat(dimensions, value);
942 + }
943 + else if(!strncmp(name, "after",lname)) after_str = value;
944 + else if(!strncmp(name, "before",lname)) before_str = value;
945 + else if(!strncmp(name, "points",lname)) points_str = value;
946 + else if(!strncmp(name, "group",lname)) {
947 + group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
948 + }
949 + else if(!strncmp(name, "options",lname)) {
950 + options |= web_client_api_request_v1_data_options(value);
951 + }
952 + else if(!strncmp(name, "label",lname)) label = value;
953 + else if(!strncmp(name, "units",lname)) units = value;
954 + else if(!strncmp(name, "label_color",lname)) label_color = value;
955 + else if(!strncmp(name, "value_color",lname)) value_color = value;
956 + else if(!strncmp(name, "multiply",lname)) multiply_str = value;
957 + else if(!strncmp(name, "divide",lname)) divide_str = value;
958 + else if(!strncmp(name, "refresh",lname)) refresh_str = value;
959 + else if(!strncmp(name, "precision",lname)) precision_str = value;
960 + else if(!strncmp(name, "scale",lname)) scale_str = value;
961 + else if(!strncmp(name, "alarm",lname)) alarm = value;
962 +
963 + } while (++i < end );
964 }
965
966 if(!chart || !*chart) {
@@ -1137,6 +1146,14 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
1146 }
1147
1148 cleanup:
1149 + if(end) {
1150 + i = 0;
1151 + do {
1152 + value = w->param_values[i].body;
1153 + lvalue = w->param_values[i].length;
1154 + value[lvalue] = save[i];
1155 + } while(++i < end);
1156 + }
1157 buffer_free(dimensions);
1158 return ret;
1159 }
web/api/exporters/allmetrics.c
+41 -38
@@ -18,54 +18,57 @@ struct prometheus_output_options {
18 };
19
20 inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) {
21 + (void)url;
22 int format = ALLMETRICS_SHELL;
23 const char *prometheus_server = w->client_ip;
24 uint32_t prometheus_backend_options = global_backend_options;
25 PROMETHEUS_OUTPUT_OPTIONS prometheus_output_options = PROMETHEUS_OUTPUT_TIMESTAMPS | ((global_backend_options & BACKEND_OPTION_SEND_NAMES)?PROMETHEUS_OUTPUT_NAMES:0);
26 const char *prometheus_prefix = global_backend_prefix;
27
27 - while(url) {
28 - char *value = mystrsep(&url, "&");
29 - if (!value || !*value) continue;
28 + uint32_t end = w->total_params;
29 + if (end) {
30 + uint32_t i = 0;
31 + do {
32 + char *name = w->param_name[i].body;
33 + size_t lname = w->param_name[i].length;
34 + char *value = w->param_values[i].body;
35 + size_t lvalue = w->param_values[i].length;
36
31 - char *name = mystrsep(&value, "=");
32 - if(!name || !*name) continue;
33 - if(!value || !*value) continue;
34 -
35 - if(!strcmp(name, "format")) {
36 - if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
37 - format = ALLMETRICS_SHELL;
38 - else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
39 - format = ALLMETRICS_PROMETHEUS;
40 - else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS))
41 - format = ALLMETRICS_PROMETHEUS_ALL_HOSTS;
42 - else if(!strcmp(value, ALLMETRICS_FORMAT_JSON))
43 - format = ALLMETRICS_JSON;
44 - else
45 - format = 0;
46 - }
47 - else if(!strcmp(name, "server")) {
48 - prometheus_server = value;
49 - }
50 - else if(!strcmp(name, "prefix")) {
51 - prometheus_prefix = value;
52 - }
53 - else if(!strcmp(name, "data") || !strcmp(name, "source") || !strcmp(name, "data source") || !strcmp(name, "data-source") || !strcmp(name, "data_source") || !strcmp(name, "datasource")) {
54 - prometheus_backend_options = backend_parse_data_source(value, prometheus_backend_options);
55 - }
56 - else {
57 - int i;
58 - for(i = 0; prometheus_output_flags_root[i].name ; i++) {
59 - if(!strcmp(name, prometheus_output_flags_root[i].name)) {
60 - if(!strcmp(value, "yes") || !strcmp(value, "1") || !strcmp(value, "true"))
61 - prometheus_output_options |= prometheus_output_flags_root[i].flag;
62 - else
63 - prometheus_output_options &= ~prometheus_output_flags_root[i].flag;
37 + if(!strncmp(name, "format",lname)) {
38 + if(!strncmp(value, ALLMETRICS_FORMAT_SHELL,lvalue))
39 + format = ALLMETRICS_SHELL;
40 + else if(!strncmp(value, ALLMETRICS_FORMAT_PROMETHEUS,lvalue))
41 + format = ALLMETRICS_PROMETHEUS;
42 + else if(!strncmp(value, ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS,lvalue))
43 + format = ALLMETRICS_PROMETHEUS_ALL_HOSTS;
44 + else if(!strncmp(value, ALLMETRICS_FORMAT_JSON,lvalue))
45 + format = ALLMETRICS_JSON;
46 + else
47 + format = 0;
48 + }
49 + else if(!strncmp(name, "server",lname)) {
50 + prometheus_server = value;
51 + }
52 + else if(!strncmp(name, "prefix",lname)) {
53 + prometheus_prefix = value;
54 + }
55 + 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)) {
56 + prometheus_backend_options = backend_parse_data_source(value, prometheus_backend_options);
57 + }
58 + else {
59 + int i;
60 + for(i = 0; prometheus_output_flags_root[i].name ; i++) {
61 + if(!strncmp(name, prometheus_output_flags_root[i].name,lname)) {
62 + if(!strncmp(value, "yes",lvalue) || !strncmp(value, "1",lvalue) || !strncmp(value, "true",lvalue))
63 + prometheus_output_options |= prometheus_output_flags_root[i].flag;
64 + else
65 + prometheus_output_options &= ~prometheus_output_flags_root[i].flag;
66
65 - break;
67 + break;
68 + }
69 }
70 }
68 - }
71 + } while( ++i < end);
72 }
73
74 buffer_flush(w->response.data);
web/api/health/health_cmdapi.c
+77 -68
@@ -31,13 +31,10 @@ void free_silencers(SILENCER *t) {
31 return;
32 }
33
34 -
35 -
34 int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url) {
35 int ret = 400;
36 (void) host;
39 -
40 -
37 + (void)url;
38
39 BUFFER *wb = w->response.data;
40 buffer_flush(wb);
@@ -73,75 +70,86 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
70 buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
71 ret = 403;
72 } else {
76 - while (url) {
77 - char *value = mystrsep(&url, "&");
78 - if (!value || !*value) continue;
79 -
80 - char *key = mystrsep(&value, "=");
81 - if (!key || !*key) continue;
82 - if (!value || !*value) continue;
83 -
84 - debug(D_WEB_CLIENT, "%llu: API v1 health query param '%s' with value '%s'", w->id, key, value);
85 -
86 - // name and value are now the parameters
87 - if (!strcmp(key, "cmd")) {
88 - if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCEALL)) {
89 - silencers->all_alarms = 1;
90 - silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
91 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCEALL);
92 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLEALL)) {
93 - silencers->all_alarms = 1;
94 - silencers->stype = STYPE_DISABLE_ALARMS;
95 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLEALL);
96 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCE)) {
97 - silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
98 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCE);
99 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLE)) {
100 - silencers->stype = STYPE_DISABLE_ALARMS;
101 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLE);
102 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_RESET)) {
103 - silencers->all_alarms = 0;
104 - silencers->stype = STYPE_NONE;
105 - free_silencers(silencers->silencers);
106 - silencers->silencers = NULL;
107 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_RESET);
108 - }
109 - } else {
110 - uint32_t hash = simple_uhash(key);
111 - if (unlikely(silencer == NULL)) {
112 - if (
113 - (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
114 - (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
115 - (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
116 - (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
117 - (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) ||
118 - (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY))
119 - ) {
120 - silencer = create_silencer();
73 + uint32_t end = w->total_params;
74 + if (end) {
75 + uint32_t i = 0;
76 + do {
77 + char *key = w->param_name[i].body;
78 + size_t lkey = w->param_name[i].length;
79 + char ksave = key[lkey];
80 + key[lkey] = 0x00;
81 +
82 + char *value = w->param_values[i].body;
83 + size_t lvalue = w->param_values[i].length;
84 + char vsave = value[lvalue];
85 + value[lvalue] = 0x00;
86 +
87 + debug(D_WEB_CLIENT, "%llu: API v1 health query param '%s' with value '%s'", w->id, key, value);
88 +
89 + // name and value are now the parameters
90 + if (!strncmp(key, "cmd",lkey)) {
91 + if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCEALL)) {
92 + silencers->all_alarms = 1;
93 + silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
94 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCEALL);
95 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLEALL)) {
96 + silencers->all_alarms = 1;
97 + silencers->stype = STYPE_DISABLE_ALARMS;
98 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLEALL);
99 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCE)) {
100 + silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
101 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCE);
102 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLE)) {
103 + silencers->stype = STYPE_DISABLE_ALARMS;
104 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLE);
105 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_RESET)) {
106 + silencers->all_alarms = 0;
107 + silencers->stype = STYPE_NONE;
108 + free_silencers(silencers->silencers);
109 + silencers->silencers = NULL;
110 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_RESET);
111 }
122 - }
123 -
124 - if (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
125 - silencer->alarms = strdupz(value);
126 - silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT);
127 - } else if (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) {
128 - silencer->charts = strdupz(value);
129 - silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT);
130 - } else if (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) {
131 - silencer->contexts = strdupz(value);
132 - silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT);
133 - } else if (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
134 - silencer->hosts = strdupz(value);
135 - silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT);
136 - } else if (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
137 - silencer->families = strdupz(value);
138 - silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT);
112 } else {
140 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_INVALID_KEY);
113 + uint32_t hash = simple_uhash(key);
114 + if (unlikely(silencer == NULL)) {
115 + if (
116 + (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
117 + (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
118 + (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
119 + (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
120 + (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) ||
121 + (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY))
122 + ) {
123 + silencer = create_silencer();
124 + }
125 + }
126 +
127 + if (hash == hash_alarm && !strncasecmp(key, HEALTH_ALARM_KEY,lkey)) {
128 + silencer->alarms = strdupz(value);
129 + silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT);
130 + } else if (hash == hash_chart && !strncasecmp(key, HEALTH_CHART_KEY,lkey)) {
131 + silencer->charts = strdupz(value);
132 + silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT);
133 + } else if (hash == hash_context && !strncasecmp(key, HEALTH_CONTEXT_KEY,lkey)) {
134 + silencer->contexts = strdupz(value);
135 + silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT);
136 + } else if (hash == hash_host && !strncasecmp(key, HEALTH_HOST_KEY,lkey)) {
137 + silencer->hosts = strdupz(value);
138 + silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT);
139 + } else if (hash == hash_families && !strncasecmp(key, HEALTH_FAMILIES_KEY,lkey)) {
140 + silencer->families = strdupz(value);
141 + silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT);
142 + } else {
143 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_INVALID_KEY);
144 + }
145 }
142 - }
146 + key[lkey] = ksave ;
147 + value[lvalue] = vsave ;
148 +
149 + } while( ++i < end );
150
151 }
152 +
153 if (likely(silencer)) {
154 // Add the created instance to the linked list in silencers
155 silencer->next = silencers->silencers;
@@ -160,6 +168,7 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
168 ret = 200;
169 }
170 }
171 +
172 w->response.data = wb;
173 buffer_no_cacheable(w->response.data);
174 return ret;
web/api/web_api_v1.c
+174 -138
@@ -195,14 +195,18 @@ inline uint32_t web_client_api_request_v1_data_google_format(char *name) {
195
196
197 inline int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url) {
198 + (void)url;
199 int all = 0;
200
200 - while(url) {
201 - char *value = mystrsep(&url, "&");
202 - if (!value || !*value) continue;
201 + uint32_t end = w->total_params;
202 + if(end) {
203 + uint32_t i = 0;
204 + do {
205 + char *value = w->param_values[i].body;
206
204 - if(!strcmp(value, "all")) all = 1;
205 - else if(!strcmp(value, "active")) all = 0;
207 + if(!strncmp(value, "all",3)) all = 1;
208 + else if(!strncmp(value, "active",6)) all = 0;
209 + } while(++i < end);
210 }
211
212 buffer_flush(w->response.data);
@@ -213,17 +217,24 @@ inline int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w,
217 }
218
219 inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url) {
220 + (void)url;
221 uint32_t after = 0;
222
218 - while(url) {
219 - char *value = mystrsep(&url, "&");
220 - if (!value || !*value) continue;
221 -
222 - char *name = mystrsep(&value, "=");
223 - if(!name || !*name) continue;
224 - if(!value || !*value) continue;
225 -
226 - if(!strcmp(name, "after")) after = (uint32_t)strtoul(value, NULL, 0);
223 + uint32_t end = w->total_params;
224 + if(end) {
225 + uint32_t i = 0;
226 + do {
227 + char *value = w->param_values[i].body;
228 + size_t lvalue = w->param_values[i].length;
229 + char save = value[lvalue];
230 + value[lvalue] = 0x00;
231 +
232 + char *name = w->param_name[i].body;
233 + size_t lname = w->param_name[i].length;
234 +
235 + if(!strncmp(name, "after",lname)) after = (uint32_t)strtoul(value, NULL, 0);
236 + value[lvalue] = save;
237 + } while (++i < end);
238 }
239
240 buffer_flush(w->response.data);
@@ -233,27 +244,29 @@ inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client
244 }
245
246 inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) {
247 + (void)url;
248 int ret = 400;
249 char *chart = NULL;
250
251 buffer_flush(w->response.data);
252
241 - while(url) {
242 - char *value = mystrsep(&url, "&");
243 - if(!value || !*value) continue;
244 -
245 - char *name = mystrsep(&value, "=");
246 - if(!name || !*name) continue;
247 - if(!value || !*value) continue;
248 -
249 - // name and value are now the parameters
250 - // they are not null and not empty
251 -
252 - if(!strcmp(name, "chart")) chart = value;
253 - //else {
254 - /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
255 - // goto cleanup;
256 - //}
253 + uint32_t i = 0;
254 + uint32_t end = w->total_params;
255 + if(end) {
256 + do {
257 + char *name = w->param_name[i].body;
258 + size_t nlength = w->param_name[i].length;
259 + char *value = w->param_values[i].body;
260 +
261 + // name and value are now the parameters
262 + // they are not null and not empty
263 +
264 + if(!strncmp(name, "chart",nlength)) chart = value;
265 + //else {
266 + /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
267 + // goto cleanup;
268 + //}
269 + } while (++i < end);
270 }
271
272 if(!chart || !*chart) {
@@ -307,6 +320,7 @@ void fix_google_param(char *s) {
320
321 // returns the HTTP code
322 inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) {
323 + (void)url;
324 debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
325
326 int ret = 400;
@@ -333,75 +347,82 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
347 uint32_t format = DATASOURCE_JSON;
348 uint32_t options = 0x00000000;
349
336 - while(url) {
337 - char *value = mystrsep(&url, "&");
338 - if(!value || !*value) continue;
339 -
340 - char *name = mystrsep(&value, "=");
341 - if(!name || !*name) continue;
342 - if(!value || !*value) continue;
343 -
344 - debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
345 -
346 - // name and value are now the parameters
347 - // they are not null and not empty
348 -
349 - if(!strcmp(name, "chart")) chart = value;
350 - else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
351 - if(!dimensions) dimensions = buffer_create(100);
352 - buffer_strcat(dimensions, "|");
353 - buffer_strcat(dimensions, value);
354 - }
355 - else if(!strcmp(name, "after")) after_str = value;
356 - else if(!strcmp(name, "before")) before_str = value;
357 - else if(!strcmp(name, "points")) points_str = value;
358 - else if(!strcmp(name, "gtime")) group_time_str = value;
359 - else if(!strcmp(name, "group")) {
360 - group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
361 - }
362 - else if(!strcmp(name, "format")) {
363 - format = web_client_api_request_v1_data_format(value);
364 - }
365 - else if(!strcmp(name, "options")) {
366 - options |= web_client_api_request_v1_data_options(value);
367 - }
368 - else if(!strcmp(name, "callback")) {
369 - responseHandler = value;
370 - }
371 - else if(!strcmp(name, "filename")) {
372 - outFileName = value;
373 - }
374 - else if(!strcmp(name, "tqx")) {
375 - // parse Google Visualization API options
376 - // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
377 - char *tqx_name, *tqx_value;
378 -
379 - while(value) {
380 - tqx_value = mystrsep(&value, ";");
381 - if(!tqx_value || !*tqx_value) continue;
382 -
383 - tqx_name = mystrsep(&tqx_value, ":");
384 - if(!tqx_name || !*tqx_name) continue;
385 - if(!tqx_value || !*tqx_value) continue;
386 -
387 - if(!strcmp(tqx_name, "version"))
388 - google_version = tqx_value;
389 - else if(!strcmp(tqx_name, "reqId"))
390 - google_reqId = tqx_value;
391 - else if(!strcmp(tqx_name, "sig")) {
392 - google_sig = tqx_value;
393 - google_timestamp = strtoul(google_sig, NULL, 0);
394 - }
395 - else if(!strcmp(tqx_name, "out")) {
396 - google_out = tqx_value;
397 - format = web_client_api_request_v1_data_google_format(google_out);
350 + uint32_t end = w->total_params;
351 + char save[WEB_FIELDS_MAX];
352 + char *value ;
353 + size_t lvalue;
354 + if(end) {
355 + uint32_t i = 0;
356 + do {
357 + char *name = w->param_name[i].body;
358 + size_t lname = w->param_name[i].length;
359 + value = w->param_values[i].body;
360 + lvalue = w->param_values[i].length;
361 + save[i] = value[lvalue];
362 + value[lvalue] = 0x00;
363 +
364 + debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
365 +
366 + // name and value are now the parameters
367 + // they are not null and not empty
368 +
369 + if(!strncmp(name, "chart",lname)) chart = value;
370 + else if(!strncmp(name, "dimension",lname) || !strncmp(name, "dim",lname) || !strncmp(name, "dimensions",lname) || !strncmp(name, "dims",lname)) {
371 + if(!dimensions) dimensions = buffer_create(100);
372 + buffer_strcat(dimensions, "|");
373 + buffer_strcat(dimensions, value);
374 + }
375 + else if(!strncmp(name, "after",lname)) after_str = value;
376 + else if(!strncmp(name, "before",lname)) before_str = value;
377 + else if(!strncmp(name, "points",lname)) points_str = value;
378 + else if(!strncmp(name, "gtime",lname)) group_time_str = value;
379 + else if(!strncmp(name, "group",lname)) {
380 + group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
381 + }
382 + else if(!strncmp(name, "format",lname)) {
383 + format = web_client_api_request_v1_data_format(value);
384 + }
385 + else if(!strncmp(name, "options",lname)) {
386 + options |= web_client_api_request_v1_data_options(value);
387 + }
388 + else if(!strncmp(name, "callback",lname)) {
389 + responseHandler = value;
390 + }
391 + else if(!strncmp(name, "filename",lname)) {
392 + outFileName = value;
393 + }
394 + else if(!strncmp(name, "tqx",lname)) {
395 + // parse Google Visualization API options
396 + // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
397 + char *tqx_name, *tqx_value;
398 +
399 + while(value) {
400 + tqx_value = mystrsep(&value, ";");
401 + if(!tqx_value || !*tqx_value) continue;
402 +
403 + tqx_name = mystrsep(&tqx_value, ":");
404 + if(!tqx_name || !*tqx_name) continue;
405 + if(!tqx_value || !*tqx_value) continue;
406 +
407 + if(!strcmp(tqx_name, "version"))
408 + google_version = tqx_value;
409 + else if(!strcmp(tqx_name, "reqId"))
410 + google_reqId = tqx_value;
411 + else if(!strcmp(tqx_name, "sig")) {
412 + google_sig = tqx_value;
413 + google_timestamp = strtoul(google_sig, NULL, 0);
414 + }
415 + else if(!strcmp(tqx_name, "out")) {
416 + google_out = tqx_value;
417 + format = web_client_api_request_v1_data_google_format(google_out);
418 + }
419 + else if(!strcmp(tqx_name, "responseHandler"))
420 + responseHandler = tqx_value;
421 + else if(!strcmp(tqx_name, "outFileName"))
422 + outFileName = tqx_value;
423 }
399 - else if(!strcmp(tqx_name, "responseHandler"))
400 - responseHandler = tqx_value;
401 - else if(!strcmp(tqx_name, "outFileName"))
402 - outFileName = tqx_value;
424 }
404 - }
425 + } while (++i < end);
426 }
427
428 // validate the google parameters given
@@ -488,6 +509,14 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
509 buffer_strcat(w->response.data, ");");
510
511 cleanup:
512 + if(end) {
513 + uint32_t i = 0;
514 + do {
515 + value = w->param_values[i].body;
516 + lvalue = w->param_values[i].length;
517 + value[lvalue] = save[i];
518 + } while ( ++i < end );
519 + }
520 buffer_free(dimensions);
521 return ret;
522 }
@@ -551,26 +580,33 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
580 int redirects = 0;
581 */
582
554 - while(url) {
555 - char *value = mystrsep(&url, "&");
556 - if (!value || !*value) continue;
583 + uint32_t i = 0;
584 + uint32_t end = w->total_params;
585 + if (!end) {
586 + goto nothing;
587 + }
588
558 - char *name = mystrsep(&value, "=");
559 - if (!name || !*name) continue;
560 - if (!value || !*value) continue;
589 + do {
590 + char *name = w->param_name[i].body;
591 + size_t nlength = w->param_name[i].length;
592 + char *value = w->param_values[i].body;
593 + size_t vlength = w->param_values[i].length;
594
595 debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
596
564 - uint32_t hash = simple_hash(name);
597 + //uint32_t hash = simple_hash(name);
598 + uint32_t hash = simple_nhash(name,nlength);
599
566 - if(hash == hash_action && !strcmp(name, "action")) {
567 - uint32_t vhash = simple_hash(value);
600 + //if(hash == hash_action && !strcmp(name, "action")) {
601 + if(hash == hash_action && !strncmp(name, "action",nlength)) {
602 + //uint32_t vhash = simple_hash(value);
603 + uint32_t vhash = simple_nhash(value,vlength);
604
569 - if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
570 - else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
571 - else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
572 - else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
573 - else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
605 + if(vhash == hash_access && !strncmp(value, "access",vlength)) action = 'A';
606 + else if(vhash == hash_hello && !strncmp(value, "hello",vlength)) action = 'H';
607 + else if(vhash == hash_delete && !strncmp(value, "delete",vlength)) action = 'D';
608 + else if(vhash == hash_search && !strncmp(value, "search",vlength)) action = 'S';
609 + else if(vhash == hash_switch && !strncmp(value, "switch",vlength)) action = 'W';
610 #ifdef NETDATA_INTERNAL_CHECKS
611 else error("unknown registry action '%s'", value);
612 #endif /* NETDATA_INTERNAL_CHECKS */
@@ -579,33 +615,34 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
615 else if(hash == hash_redirects && !strcmp(name, "redirects"))
616 redirects = atoi(value);
617 */
582 - else if(hash == hash_machine && !strcmp(name, "machine"))
618 + else if(hash == hash_machine && !strncmp(name, "machine",nlength))
619 machine_guid = value;
620
585 - else if(hash == hash_url && !strcmp(name, "url"))
621 + else if(hash == hash_url && !strncmp(name, "url",nlength))
622 machine_url = value;
623
624 else if(action == 'A') {
589 - if(hash == hash_name && !strcmp(name, "name"))
625 + if(hash == hash_name && !strncmp(name, "name",nlength))
626 url_name = value;
627 }
628 else if(action == 'D') {
593 - if(hash == hash_delete_url && !strcmp(name, "delete_url"))
629 + if(hash == hash_delete_url && !strncmp(name, "delete_url",nlength))
630 delete_url = value;
631 }
632 else if(action == 'S') {
597 - if(hash == hash_for && !strcmp(name, "for"))
633 + if(hash == hash_for && !strncmp(name, "for",nlength))
634 search_machine_guid = value;
635 }
636 else if(action == 'W') {
601 - if(hash == hash_to && !strcmp(name, "to"))
637 + if(hash == hash_to && !strncmp(name, "to",nlength))
638 to_person_guid = value;
639 }
640 #ifdef NETDATA_INTERNAL_CHECKS
641 else error("unused registry URL parameter '%s' with value '%s'", name, value);
642 #endif /* NETDATA_INTERNAL_CHECKS */
607 - }
643 + } while (++i < end );
644
645 +nothing:
646 if(unlikely(respect_web_browser_do_not_track_policy && web_client_has_donottrack(w))) {
647 buffer_flush(w->response.data);
648 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.");
@@ -797,28 +834,27 @@ inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *
834 }
835
836 // get the command
800 - char *tok = mystrsep(&url, "?");
801 - if(tok && *tok) {
802 - debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
803 - uint32_t hash = simple_hash(tok);
837
805 - for(i = 0; api_commands[i].command ;i++) {
806 - if(unlikely(hash == api_commands[i].hash && !strcmp(tok, api_commands[i].command))) {
807 - if(unlikely(api_commands[i].acl != WEB_CLIENT_ACL_NOCHECK) && !(w->acl & api_commands[i].acl))
808 - return web_client_permission_denied(w);
838 + char *cmd = w->command.body;
839 + size_t length = w->command.length;
840 + uint32_t hash = simple_nhash(cmd,length);
841
810 - return api_commands[i].callback(host, w, url);
811 - }
812 - }
842 + for(i = 0; api_commands[i].command ;i++) {
843 + if(unlikely(hash == api_commands[i].hash && !strncmp(cmd, api_commands[i].command,length))) {
844 + if(unlikely(api_commands[i].acl != WEB_CLIENT_ACL_NOCHECK) && !(w->acl & api_commands[i].acl))
845 + return web_client_permission_denied(w);
846
814 - buffer_flush(w->response.data);
815 - buffer_strcat(w->response.data, "Unsupported v1 API command: ");
816 - buffer_strcat_htmlescape(w->response.data, tok);
817 - return 404;
818 - }
819 - else {
820 - buffer_flush(w->response.data);
821 - buffer_sprintf(w->response.data, "Which API v1 command?");
822 - return 400;
847 + return api_commands[i].callback(host, w, url);
848 + }
849 }
850 +
851 + char copyme[256];
852 + length = w->path.length;
853 + memcpy(copyme,w->path.body,length);
854 + copyme[length] = 0x00;
855 +
856 + buffer_flush(w->response.data);
857 + buffer_strcat(w->response.data, "Unsupported v1 API command: ");
858 + buffer_strcat_htmlescape(w->response.data, copyme);
859 + return 404;
860 }
web/server/web_client.c
+353 -160
@@ -143,7 +143,7 @@ void web_client_request_done(struct web_client *w) {
143 debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
144
145 if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
146 - if (w->ifd != -1){
146 + if (w->ifd != -1) {
147 close(w->ifd);
148 }
149 }
@@ -352,7 +352,7 @@ int mysendfile(struct web_client *w, char *filename) {
352 // if the filename contains "strange" characters, refuse to serve it
353 char *s;
354 for(s = filename; *s ;s++) {
355 - if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
355 + if(!isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
356 debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
357 w->response.data->contenttype = CT_TEXT_HTML;
358 buffer_sprintf(w->response.data, "Filename contains invalid characters: ");
@@ -594,15 +594,26 @@ int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
594 {
595 // get the api version
596 char *tok = mystrsep(&url, "/");
597 - if(tok && *tok) {
598 - debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
599 - if(strcmp(tok, "v1") == 0)
597 + (void)tok;
598 + char *body = w->version.body;
599 + size_t length = w->version.length;
600 + if(body) {
601 + //if(tok && *tok) {
602 + //debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
603 + debug(D_WEB_CLIENT, "%llu: Searching for API version'.", w->id);
604 + //if(strcmp(tok, "v1") == 0)
605 + if(strncmp(body, "v1",length) == 0) {
606 return web_client_api_request_v1(host, w, url);
601 - else {
607 + } else {
608 + char response[NETDATA_WEB_REQUEST_URL_SIZE];
609 + strncpy(response,w->version.body,length);
610 + response[length] = 0x00;
611 buffer_flush(w->response.data);
612 +
613 w->response.data->contenttype = CT_TEXT_HTML;
614 buffer_strcat(w->response.data, "Unsupported API version: ");
605 - buffer_strcat_htmlescape(w->response.data, tok);
615 + buffer_strcat_htmlescape(w->response.data, response);
616 + //buffer_strcat_htmlescape(w->response.data, tok);
617 return 404;
618 }
619 }
@@ -791,7 +802,7 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
802 } else if(hash == hash_authorization&& !strcasecmp(s, "X-Auth-Token")) {
803 w->auth_bearer_token = strdupz(v);
804 }
794 - else if(hash == hash_host && !strcasecmp(s, "Host")){
805 + else if(hash == hash_host && !strcasecmp(s, "Host")) {
806 strncpyz(w->host, v, (ve - v));
807 }
808 #ifdef NETDATA_WITH_ZLIB
@@ -812,76 +823,67 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
823 return ve;
824 }
825
815 -// http_request_validate()
816 -// returns:
817 -// = 0 : all good, process the request
818 -// > 0 : request is not supported
819 -// < 0 : request is incomplete - wait for more data
820 -
821 -typedef enum {
822 - HTTP_VALIDATION_OK,
823 - HTTP_VALIDATION_NOT_SUPPORTED,
824 -#ifdef ENABLE_HTTPS
825 - HTTP_VALIDATION_INCOMPLETE,
826 - HTTP_VALIDATION_REDIRECT
827 -#else
828 - HTTP_VALIDATION_INCOMPLETE
829 -#endif
830 -} HTTP_VALIDATION;
831 -
832 -static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
833 - char *s = (char *)buffer_tostring(w->response.data), *encoded_url = NULL;
826 +static inline HTTP_VALIDATION web_client_is_complete(char *begin,char *end,size_t length){
827 + if ( begin == end){
828 + return HTTP_VALIDATION_INCOMPLETE;
829 + }
830
835 - size_t last_pos = w->header_parse_last_size;
836 - if(last_pos > 4) last_pos -= 4; // allow searching for \r\n\r\n
837 - else last_pos = 0;
831 + if ( length > 3 ){
832 + begin = end - 4;
833 + }
834
839 - w->header_parse_tries++;
840 - w->header_parse_last_size = buffer_strlen(w->response.data);
835 + uint32_t counter = 0;
836 + do{
837 + if (*begin == '\r'){
838 + begin++;
839 + if ( begin == end )
840 + {
841 + break;
842 + }
843
842 - if(w->header_parse_tries > 1) {
843 - if(w->header_parse_last_size < last_pos)
844 - last_pos = 0;
845 -
846 - if(strstr(&s[last_pos], "\r\n\r\n") == NULL) {
847 - if(w->header_parse_tries > 10) {
848 - info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
849 - w->header_parse_tries = 0;
850 - w->header_parse_last_size = 0;
851 - web_client_disable_wait_receive(w);
852 - return HTTP_VALIDATION_NOT_SUPPORTED;
844 + if (*begin == '\n')
845 + {
846 + counter++;
847 }
848 + } else if (*begin == '\n') {
849 + begin++;
850 + counter++;
851 + }
852
855 - return HTTP_VALIDATION_INCOMPLETE;
853 + if ( counter == 2){
854 + break;
855 }
856 }
857 + while(begin != end);
858
859 - // is is a valid request?
859 + return (counter == 2)?HTTP_VALIDATION_OK:HTTP_VALIDATION_INCOMPLETE;
860 +}
861 +
862 +static inline char *web_client_parse_method(struct web_client *w,char *s) {
863 if(!strncmp(s, "GET ", 4)) {
861 - encoded_url = s = &s[4];
864 + s = &s[4];
865 w->mode = WEB_CLIENT_MODE_NORMAL;
866 }
867 else if(!strncmp(s, "OPTIONS ", 8)) {
865 - encoded_url = s = &s[8];
868 + s = &s[8];
869 w->mode = WEB_CLIENT_MODE_OPTIONS;
870 }
871 else if(!strncmp(s, "STREAM ", 7)) {
872 #ifdef ENABLE_HTTPS
870 - if ( (w->ssl.flags) && (netdata_use_ssl_on_stream & NETDATA_SSL_FORCE)){
873 + if ((w->ssl.flags) && (netdata_use_ssl_on_stream & NETDATA_SSL_FORCE)) {
874 w->header_parse_tries = 0;
875 w->header_parse_last_size = 0;
876 web_client_disable_wait_receive(w);
877 char hostname[256];
878 char *copyme = strstr(s,"hostname=");
876 - if ( copyme ){
879 + if (copyme) {
880 copyme += 9;
881 char *end = strchr(copyme,'&');
879 - if(end){
882 + if(end) {
883 size_t length = end - copyme;
884 memcpy(hostname,copyme,length);
885 hostname[length] = 0X00;
883 - }
884 - else{
886 + } else {
887 memcpy(hostname,"not available",13);
888 hostname[13] = 0x00;
889 }
@@ -891,30 +893,205 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
893 hostname[13] = 0x00;
894 }
895 error("The server is configured to always use encrypt connection, please enable the SSL on slave with hostname '%s'.",hostname);
894 - return HTTP_VALIDATION_NOT_SUPPORTED;
896 + return NULL;
897 }
898 #endif
897 -
898 - encoded_url = s = &s[7];
899 + s = &s[7];
900 w->mode = WEB_CLIENT_MODE_STREAM;
901 }
902 else {
903 + s = NULL;
904 + }
905 +
906 + return s;
907 +}
908 +
909 +static inline char *web_client_find_protocol(struct web_client *w,char *s) {
910 + s = url_find_protocol(s);
911 +
912 + w->protocol.body = s+1;
913 + char *end = strchr(s+6,'\n');
914 + if (end) {
915 + w->protocol.length = end - w->protocol.body ;
916 + }
917 +
918 + return s;
919 +}
920 +
921 +static inline void web_client_parse_headers(struct web_client *w,char *s) {
922 + while(*s) {
923 + // find a line feed
924 + while(*s && *s++ != '\r');
925 +
926 + // did we reach the end?
927 + if(unlikely(!*s)) break;
928 +
929 + if (*s == '\n') {
930 + s++;
931 + }
932 +
933 + s = http_header_parse(w, s,
934 + (w->mode == WEB_CLIENT_MODE_STREAM) // parse user agent
935 + );
936 +
937 + }
938 +}
939 +
940 +int web_client_parse_request(struct web_client *w,char *divisor) {
941 + if(!divisor) {
942 + w->total_params = 0;
943 + return 0;
944 + }
945 +
946 + uint32_t i = url_parse_query_string(w->param_name,w->param_values,w->query_string.body+1,divisor);
947 + w->total_params = i;
948 +
949 + return i;
950 +}
951 +
952 +static inline void web_client_set_directory(struct web_client *w,char *begin,char *enddir,char *endcmd) {
953 + if (enddir) {
954 + w->directory.body = begin;
955 + w->directory.length = enddir - begin;
956 +
957 + if (!strncmp(w->directory.body,"api",w->directory.length)) {
958 + begin = enddir + 1;
959 + enddir = strchr(begin,'/');
960 + if(enddir) {
961 + w->version.body = begin;
962 + w->version.length = enddir - begin;
963 +
964 + enddir++;
965 + w->command.body = enddir;
966 + w->command.length = (size_t) (endcmd - enddir);
967 + }
968 + }
969 + }
970 + else{
971 + w->directory.body = begin;
972 + w->directory.length = w->path.length - 1;
973 + w->version.body = NULL;
974 + w->version.length = 0;
975 + w->command.body = NULL;
976 + w->command.length = 0;
977 + }
978 +}
979 +
980 +static inline void web_client_set_without_query_string(struct web_client *w) {
981 + w->query_string.body = NULL;
982 + w->query_string.length = 0;
983 +
984 + char *test = w->path.body+1;
985 + if (!strncmp(test,"api/v1/",7) ) {
986 + test += 7;
987 + if (!strncmp(test,"info",4)) {
988 + w->command.length = 4;
989 + }
990 + else if (!strncmp(test,"charts",6)) {
991 + w->command.length = 6;
992 + }
993 + else {
994 + test = NULL;
995 + w->command.length = 0;
996 + }
997 + }else{
998 + w->command.length = w->path.length;
999 + }
1000 + w->command.body = test;
1001 + w->total_params = 0;
1002 +}
1003 +
1004 +static inline void web_client_split_path_query(struct web_client *w) {
1005 + w->path.body = w->decoded_url;
1006 + w->decoded_length = strlen(w->decoded_url);
1007 + char *moveme = strchr(w->path.body,'?');
1008 + char *enddir;
1009 + if (moveme) {
1010 + w->path.length = moveme - w->path.body;
1011 + w->query_string.body = moveme;
1012 + w->query_string.length = w->decoded_length - w->path.length;
1013 +
1014 + enddir = strchr(w->path.body+1,'/');
1015 + char *begin = w->path.body+1;
1016 + web_client_set_directory(w,begin,enddir,moveme);
1017 + if (w->query_string.body) {
1018 + enddir = strchr(moveme,'=');
1019 + if (!web_client_parse_request(w,enddir) ) {
1020 + moveme++;
1021 + size_t length = strlen(moveme);
1022 + w->param_name[0].body = moveme;
1023 + w->param_name[0].length = length;
1024 + w->param_values[0].body = moveme;
1025 + w->param_values[0].length = length;
1026 +
1027 + w->total_params = 1;
1028 + }
1029 + }
1030 + } else {
1031 + w->path.length = w->decoded_length;
1032 +
1033 + enddir = strchr(w->path.body+1,'/');
1034 + w->directory.body = w->path.body + 1;
1035 + if(enddir) {
1036 + w->directory.length = (size_t)(enddir - w->directory.body);
1037 + enddir++;
1038 +
1039 + w->version.body = enddir;
1040 + enddir = strchr(++enddir,'/');
1041 + if(enddir) {
1042 + w->version.length = (size_t)(enddir - w->version.body);
1043 +
1044 + enddir++;
1045 + w->command.body = enddir;
1046 + w->command.length = (size_t)(moveme - enddir);
1047 + } else{
1048 + w->version.length = strlen(w->version.body);
1049 + }
1050 +
1051 + }else {
1052 + w->directory.length = w->decoded_length - 1;
1053 + }
1054 + web_client_set_without_query_string(w);
1055 + }
1056 +}
1057 +
1058 +static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
1059 + char *s = (char *)buffer_tostring(w->response.data), *encoded_url = NULL;
1060 + size_t status;
1061 +
1062 + w->header_parse_tries++;
1063 + w->header_parse_last_size = buffer_strlen(w->response.data);
1064 + status = w->header_parse_last_size;
1065 +
1066 + // make sure we have complete request
1067 + // complete requests contain: \r\n\r\n
1068 + status = url_is_request_complete(s,&s[status],status);
1069 + if (w->header_parse_tries > 10) {
1070 + if (status == HTTP_VALIDATION_INCOMPLETE) {
1071 + info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
1072 + w->header_parse_tries = 0;
1073 + w->header_parse_last_size = 0;
1074 + web_client_disable_wait_receive(w);
1075 + return HTTP_VALIDATION_NOT_SUPPORTED;
1076 + }
1077 + } else{
1078 + if (status == HTTP_VALIDATION_INCOMPLETE) {
1079 + web_client_enable_wait_receive(w);
1080 + return HTTP_VALIDATION_INCOMPLETE;
1081 + }
1082 + }
1083 + //Parse the method used to communicate
1084 + s = web_client_parse_method(w,s);
1085 + if (!s) {
1086 w->header_parse_tries = 0;
1087 w->header_parse_last_size = 0;
1088 web_client_disable_wait_receive(w);
1089 return HTTP_VALIDATION_NOT_SUPPORTED;
1090 }
1091
908 - // find the SPACE + "HTTP/"
909 - while(*s) {
910 - // find the next space
911 - while (*s && *s != ' ') s++;
912 -
913 - // is it SPACE + "HTTP/" ?
914 - if(*s && !strncmp(s, " HTTP/", 6)) break;
915 - else s++;
916 - }
1092 + encoded_url = s;
1093
1094 + s = web_client_find_protocol(w,s);
1095 // incomplete requests
1096 if(unlikely(!*s)) {
1097 web_client_enable_wait_receive(w);
@@ -924,64 +1101,40 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
1101 // we have the end of encoded_url - remember it
1102 char *ue = s;
1103
927 - // make sure we have complete request
928 - // complete requests contain: \r\n\r\n
929 - while(*s) {
930 - // find a line feed
931 - while(*s && *s++ != '\r');
932 -
933 - // did we reach the end?
934 - if(unlikely(!*s)) break;
1104 + *ue = '\0';
1105 + url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1);
1106
936 - // is it \r\n ?
937 - if(likely(*s++ == '\n')) {
1107 + web_client_split_path_query(w);
1108 + *ue = ' ';
1109 + web_client_parse_headers(w,s);
1110
939 - // is it again \r\n ? (header end)
940 - if(unlikely(*s == '\r' && s[1] == '\n')) {
941 - // a valid complete HTTP request found
1111 + // copy the URL - we are going to overwrite parts of it
1112 + // TODO -- ideally we we should avoid copying buffers around
1113 + strncpyz(w->last_url, w->decoded_url, NETDATA_WEB_REQUEST_URL_SIZE);
1114
943 - *ue = '\0';
944 - url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1);
945 - *ue = ' ';
946 -
947 - // copy the URL - we are going to overwrite parts of it
948 - // TODO -- ideally we we should avoid copying buffers around
949 - strncpyz(w->last_url, w->decoded_url, NETDATA_WEB_REQUEST_URL_SIZE);
1115 #ifdef ENABLE_HTTPS
951 - if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
952 - 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)) ) {
953 - w->header_parse_tries = 0;
954 - w->header_parse_last_size = 0;
955 - web_client_disable_wait_receive(w);
956 - return HTTP_VALIDATION_REDIRECT;
957 - }
958 - }
959 -#endif
960 -
961 - w->header_parse_tries = 0;
962 - w->header_parse_last_size = 0;
963 - web_client_disable_wait_receive(w);
964 - return HTTP_VALIDATION_OK;
965 - }
966 -
967 - // another header line
968 - s = http_header_parse(w, s,
969 - (w->mode == WEB_CLIENT_MODE_STREAM) // parse user agent
970 - );
1116 + if ((!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
1117 + 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)) ) {
1118 + w->header_parse_tries = 0;
1119 + w->header_parse_last_size = 0;
1120 + web_client_disable_wait_receive(w);
1121 + return HTTP_VALIDATION_REDIRECT;
1122 }
1123 }
1124 +#endif
1125
974 - // incomplete request
975 - web_client_enable_wait_receive(w);
976 - return HTTP_VALIDATION_INCOMPLETE;
1126 + w->header_parse_tries = 0;
1127 + w->header_parse_last_size = 0;
1128 + web_client_disable_wait_receive(w);
1129 + return HTTP_VALIDATION_OK;
1130 }
1131
1132 static inline ssize_t web_client_send_data(struct web_client *w,const void *buf,size_t len, int flags)
1133 {
1134 ssize_t bytes;
1135 #ifdef ENABLE_HTTPS
983 - if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
984 - if ( ( w->ssl.conn ) && ( !w->ssl.flags ) ){
1136 + if ((!web_client_check_unix(w)) && (netdata_srv_ctx)) {
1137 + if ((w->ssl.conn) && (!w->ssl.flags)) {
1138 bytes = SSL_write(w->ssl.conn,buf, len) ;
1139 } else {
1140 bytes = send(w->ofd,buf, len , flags);
@@ -1102,7 +1255,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
1255 buffer_sprintf(w->response.header_output,
1256 "Cache-Control: %s\r\n"
1257 "Expires: %s\r\n",
1105 - (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache, no-store, must-revalidate\r\nPragma: no-cache":"public",
1258 + (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
1259 edate);
1260 }
1261
@@ -1143,8 +1296,8 @@ static inline void web_client_send_http_header(struct web_client *w) {
1296 size_t count = 0;
1297 ssize_t bytes;
1298 #ifdef ENABLE_HTTPS
1146 - if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
1147 - if ( ( w->ssl.conn ) && ( !w->ssl.flags ) ){
1299 + if ((!web_client_check_unix(w)) && (netdata_srv_ctx)) {
1300 + if ((w->ssl.conn) && (!w->ssl.flags)) {
1301 while((bytes = SSL_write(w->ssl.conn, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output))) < 0) {
1302 count++;
1303 if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
@@ -1213,21 +1366,52 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
1366 return 400;
1367 }
1368
1216 - char *tok = mystrsep(&url, "/");
1217 - if(tok && *tok) {
1218 - debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
1369 + char *tok = strchr(url,'/');
1370 + if (tok) {
1371 + w->switch_host = 1;
1372 + debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, url);
1373
1374 // copy the URL, we need it to serve files
1375 w->last_url[0] = '/';
1222 - if(url && *url) strncpyz(&w->last_url[1], url, NETDATA_WEB_REQUEST_URL_SIZE - 1);
1223 - else w->last_url[1] = '\0';
1224 -
1225 - uint32_t hash = simple_hash(tok);
1376 + if(*(tok+1) != ' ') {
1377 + strncpyz(&w->last_url[1], tok+1, NETDATA_WEB_REQUEST_URL_SIZE - 1);
1378 + char *enddir;
1379 + if (w->total_params) {
1380 + enddir = strchr(tok+1,'/');
1381 + if (enddir) {
1382 + char *moveme = strchr(enddir,'?');
1383 + if (moveme) {
1384 + web_client_set_directory(w,tok+1,enddir,moveme);
1385 + }
1386 + }
1387 + else {
1388 + w->directory.body = tok;
1389 + w->directory.length = strlen(tok);
1390 + }
1391 + } else {
1392 + enddir = strchr(tok+1,'/');
1393 + if(enddir) {
1394 + w->directory.body = tok + 1;
1395 + w->directory.length = enddir - tok - 1;
1396 + } else {
1397 + if (!strlen(tok + 1)) {
1398 + w->directory.body = tok;
1399 + w->directory.length = 1;
1400 + }
1401 + }
1402 + }
1403 + }
1404 + else {
1405 + w->last_url[1] = '\0';
1406 + }
1407 + *tok = 0x00;
1408 + uint32_t hash = simple_hash(url);
1409
1227 - host = rrdhost_find_by_hostname(tok, hash);
1228 - if(!host) host = rrdhost_find_by_guid(tok, hash);
1410 + host = rrdhost_find_by_hostname(url, hash);
1411 + if(!host) host = rrdhost_find_by_guid(url, hash);
1412 + *tok = '/';
1413
1230 - if(host) return web_client_process_url(host, w, url);
1414 + if(host) return web_client_process_url(host, w, tok);
1415 }
1416
1417 buffer_flush(w->response.data);
@@ -1258,20 +1442,21 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1442 #endif
1443 }
1444
1261 - char *tok = mystrsep(&url, "/?");
1262 - if(likely(tok && *tok)) {
1263 - uint32_t hash = simple_hash(tok);
1264 - debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
1445 + if (w->path.length > 1) {
1446 + char *cmp = w->directory.body;
1447 + size_t len = w->directory.length;
1448 + uint32_t hash = simple_nhash(cmp,len);
1449 + debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, w->command.body);
1450
1266 - if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) { // current API
1451 + if(unlikely(hash == hash_api && strncmp(cmp, "api",len) == 0)) { // current API
1452 debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
1453 return check_host_and_call(host, w, url, web_client_api_request);
1454 }
1270 - else if(unlikely(hash == hash_host && strcmp(tok, "host") == 0)) { // host switching
1455 + else if(unlikely(hash == hash_host && strncmp(cmp, "host",len) == 0)) { // host switching
1456 debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
1272 - return web_client_switch_host(host, w, url);
1457 + return web_client_switch_host(host, w, cmp+5);
1458 }
1274 - else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) { // netdata.conf
1459 + else if(unlikely(hash == hash_netdata_conf && strncmp(cmp, "netdata.conf",len) == 0)) { // current API
1460 if(unlikely(!web_client_can_access_netdataconf(w)))
1461 return web_client_permission_denied(w);
1462
@@ -1282,7 +1467,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1467 return 200;
1468 }
1469 #ifdef NETDATA_INTERNAL_CHECKS
1285 - else if(unlikely(hash == hash_exit && strcmp(tok, "exit") == 0)) {
1470 + else if(unlikely(hash == hash_exit && strncmp(cmp, "exit",len) == 0)) {
1471 if(unlikely(!web_client_can_access_netdataconf(w)))
1472 return web_client_permission_denied(w);
1473
@@ -1298,47 +1483,50 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1483 netdata_cleanup_and_exit(0);
1484 return 200;
1485 }
1301 - else if(unlikely(hash == hash_debug && strcmp(tok, "debug") == 0)) {
1486 + else if(unlikely(hash == hash_debug && strncmp(cmp, "debug",len) == 0)) {
1487 if(unlikely(!web_client_can_access_netdataconf(w)))
1488 return web_client_permission_denied(w);
1489
1490 buffer_flush(w->response.data);
1491
1492 // get the name of the data to show
1308 - tok = mystrsep(&url, "&");
1309 - if(tok && *tok) {
1310 - debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
1311 -
1312 - // do we have such a data set?
1313 - RRDSET *st = rrdset_find_byname(host, tok);
1314 - if(!st) st = rrdset_find(host, tok);
1315 - if(!st) {
1316 - w->response.data->contenttype = CT_TEXT_HTML;
1317 - buffer_strcat(w->response.data, "Chart is not found: ");
1318 - buffer_strcat_htmlescape(w->response.data, tok);
1319 - debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
1320 - return 404;
1321 - }
1493 + char *tok = mystrsep(&url, "/?");
1494 + if(likely(tok && *tok)) {
1495 + tok = mystrsep(&url, "&");
1496 + if(tok && *tok) {
1497 + debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
1498 +
1499 + // do we have such a data set?
1500 + RRDSET *st = rrdset_find_byname(host, tok);
1501 + if(!st) st = rrdset_find(host, tok);
1502 + if(!st) {
1503 + w->response.data->contenttype = CT_TEXT_HTML;
1504 + buffer_strcat(w->response.data, "Chart is not found: ");
1505 + buffer_strcat_htmlescape(w->response.data, tok);
1506 + debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
1507 + return 404;
1508 + }
1509
1323 - debug_flags |= D_RRD_STATS;
1510 + debug_flags |= D_RRD_STATS;
1511
1325 - if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))
1326 - rrdset_flag_clear(st, RRDSET_FLAG_DEBUG);
1327 - else
1328 - rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
1512 + if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))
1513 + rrdset_flag_clear(st, RRDSET_FLAG_DEBUG);
1514 + else
1515 + rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
1516
1330 - w->response.data->contenttype = CT_TEXT_HTML;
1331 - buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
1332 - buffer_strcat_htmlescape(w->response.data, tok);
1333 - debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
1334 - return 200;
1517 + w->response.data->contenttype = CT_TEXT_HTML;
1518 + buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
1519 + buffer_strcat_htmlescape(w->response.data, tok);
1520 + debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
1521 + return 200;
1522 + }
1523 }
1524
1525 buffer_flush(w->response.data);
1526 buffer_strcat(w->response.data, "debug which chart?\r\n");
1527 return 400;
1528 }
1341 - else if(unlikely(hash == hash_mirror && strcmp(tok, "mirror") == 0)) {
1529 + else if(unlikely(hash == hash_mirror && strncmp(cmp, "mirror",len) == 0)) {
1530 if(unlikely(!web_client_can_access_netdataconf(w)))
1531 return web_client_permission_denied(w);
1532
@@ -1355,12 +1543,17 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1543 #endif /* NETDATA_INTERNAL_CHECKS */
1544 }
1545
1546 + w->switch_host = 0;
1547 + char *tok = mystrsep(&url, "/?");
1548 + (void)tok;
1549 +
1550 char filename[FILENAME_MAX+1];
1551 url = filename;
1552 strncpyz(filename, w->last_url, FILENAME_MAX);
1553 tok = mystrsep(&url, "?");
1554 buffer_flush(w->response.data);
1363 - return mysendfile(w, (tok && *tok)?tok:"/");
1555 +
1556 + return mysendfile(w, (w->path.length > 1)?tok:"/");
1557 }
1558
1559 void web_client_process_request(struct web_client *w) {
@@ -1812,8 +2005,8 @@ ssize_t web_client_receive(struct web_client *w)
2005 buffer_need_bytes(w->response.data, NETDATA_WEB_REQUEST_RECEIVE_SIZE);
2006
2007 #ifdef ENABLE_HTTPS
1815 - if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
1816 - if ( ( w->ssl.conn ) && (!w->ssl.flags)) {
2008 + if ((!web_client_check_unix(w)) && (netdata_srv_ctx)) {
2009 + if ((w->ssl.conn) && (!w->ssl.flags)) {
2010 bytes = SSL_read(w->ssl.conn, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
2011 }else {
2012 bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
web/server/web_client.h
+12
@@ -128,6 +128,18 @@ struct web_client {
128 char client_port[NI_MAXSERV+1];
129
130 char decoded_url[NETDATA_WEB_REQUEST_URL_SIZE + 1]; // we decode the URL in this buffer
131 + size_t decoded_length;
132 + struct web_fields path;
133 + struct web_fields directory;
134 + struct web_fields query_string;
135 + struct web_fields version;
136 + struct web_fields command;
137 + struct web_fields protocol;
138 + struct web_fields param_name[WEB_FIELDS_MAX];
139 + struct web_fields param_values[WEB_FIELDS_MAX];
140 + uint32_t total_params;
141 + int switch_host;
142 +
143 char last_url[NETDATA_WEB_REQUEST_URL_SIZE+1]; // we keep a copy of the decoded URL here
144 char host[256];
145